mirror of
https://github.com/go-gitea/gitea
synced 2024-11-17 23:54:25 +00:00
235 lines
5.9 KiB
Go
235 lines
5.9 KiB
Go
|
package analysis
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"github.com/go-openapi/spec"
|
||
|
"github.com/go-openapi/strfmt"
|
||
|
)
|
||
|
|
||
|
// SchemaOpts configures the schema analyzer
|
||
|
type SchemaOpts struct {
|
||
|
Schema *spec.Schema
|
||
|
Root interface{}
|
||
|
BasePath string
|
||
|
_ struct{}
|
||
|
}
|
||
|
|
||
|
// Schema analysis, will classify the schema according to known
|
||
|
// patterns.
|
||
|
func Schema(opts SchemaOpts) (*AnalyzedSchema, error) {
|
||
|
if opts.Schema == nil {
|
||
|
return nil, fmt.Errorf("no schema to analyze")
|
||
|
}
|
||
|
|
||
|
a := &AnalyzedSchema{
|
||
|
schema: opts.Schema,
|
||
|
root: opts.Root,
|
||
|
basePath: opts.BasePath,
|
||
|
}
|
||
|
|
||
|
a.initializeFlags()
|
||
|
a.inferKnownType()
|
||
|
a.inferEnum()
|
||
|
a.inferBaseType()
|
||
|
|
||
|
if err := a.inferMap(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if err := a.inferArray(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
a.inferTuple()
|
||
|
|
||
|
if err := a.inferFromRef(); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
a.inferSimpleSchema()
|
||
|
return a, nil
|
||
|
}
|
||
|
|
||
|
// AnalyzedSchema indicates what the schema represents
|
||
|
type AnalyzedSchema struct {
|
||
|
schema *spec.Schema
|
||
|
root interface{}
|
||
|
basePath string
|
||
|
|
||
|
hasProps bool
|
||
|
hasAllOf bool
|
||
|
hasItems bool
|
||
|
hasAdditionalProps bool
|
||
|
hasAdditionalItems bool
|
||
|
hasRef bool
|
||
|
|
||
|
IsKnownType bool
|
||
|
IsSimpleSchema bool
|
||
|
IsArray bool
|
||
|
IsSimpleArray bool
|
||
|
IsMap bool
|
||
|
IsSimpleMap bool
|
||
|
IsExtendedObject bool
|
||
|
IsTuple bool
|
||
|
IsTupleWithExtra bool
|
||
|
IsBaseType bool
|
||
|
IsEnum bool
|
||
|
}
|
||
|
|
||
|
// Inherits copies value fields from other onto this schema
|
||
|
func (a *AnalyzedSchema) inherits(other *AnalyzedSchema) {
|
||
|
if other == nil {
|
||
|
return
|
||
|
}
|
||
|
a.hasProps = other.hasProps
|
||
|
a.hasAllOf = other.hasAllOf
|
||
|
a.hasItems = other.hasItems
|
||
|
a.hasAdditionalItems = other.hasAdditionalItems
|
||
|
a.hasAdditionalProps = other.hasAdditionalProps
|
||
|
a.hasRef = other.hasRef
|
||
|
|
||
|
a.IsKnownType = other.IsKnownType
|
||
|
a.IsSimpleSchema = other.IsSimpleSchema
|
||
|
a.IsArray = other.IsArray
|
||
|
a.IsSimpleArray = other.IsSimpleArray
|
||
|
a.IsMap = other.IsMap
|
||
|
a.IsSimpleMap = other.IsSimpleMap
|
||
|
a.IsExtendedObject = other.IsExtendedObject
|
||
|
a.IsTuple = other.IsTuple
|
||
|
a.IsTupleWithExtra = other.IsTupleWithExtra
|
||
|
a.IsBaseType = other.IsBaseType
|
||
|
a.IsEnum = other.IsEnum
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferFromRef() error {
|
||
|
if a.hasRef {
|
||
|
sch := new(spec.Schema)
|
||
|
sch.Ref = a.schema.Ref
|
||
|
err := spec.ExpandSchema(sch, a.root, nil)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
rsch, err := Schema(SchemaOpts{
|
||
|
Schema: sch,
|
||
|
Root: a.root,
|
||
|
BasePath: a.basePath,
|
||
|
})
|
||
|
if err != nil {
|
||
|
// NOTE(fredbi): currently the only cause for errors is
|
||
|
// unresolved ref. Since spec.ExpandSchema() expands the
|
||
|
// schema recursively, there is no chance to get there,
|
||
|
// until we add more causes for error in this schema analysis.
|
||
|
return err
|
||
|
}
|
||
|
a.inherits(rsch)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferSimpleSchema() {
|
||
|
a.IsSimpleSchema = a.IsKnownType || a.IsSimpleArray || a.IsSimpleMap
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferKnownType() {
|
||
|
tpe := a.schema.Type
|
||
|
format := a.schema.Format
|
||
|
a.IsKnownType = tpe.Contains("boolean") ||
|
||
|
tpe.Contains("integer") ||
|
||
|
tpe.Contains("number") ||
|
||
|
tpe.Contains("string") ||
|
||
|
(format != "" && strfmt.Default.ContainsName(format)) ||
|
||
|
(a.isObjectType() && !a.hasProps && !a.hasAllOf && !a.hasAdditionalProps && !a.hasAdditionalItems)
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferMap() error {
|
||
|
if a.isObjectType() {
|
||
|
hasExtra := a.hasProps || a.hasAllOf
|
||
|
a.IsMap = a.hasAdditionalProps && !hasExtra
|
||
|
a.IsExtendedObject = a.hasAdditionalProps && hasExtra
|
||
|
if a.IsMap {
|
||
|
if a.schema.AdditionalProperties.Schema != nil {
|
||
|
msch, err := Schema(SchemaOpts{
|
||
|
Schema: a.schema.AdditionalProperties.Schema,
|
||
|
Root: a.root,
|
||
|
BasePath: a.basePath,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
a.IsSimpleMap = msch.IsSimpleSchema
|
||
|
} else if a.schema.AdditionalProperties.Allows {
|
||
|
a.IsSimpleMap = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferArray() error {
|
||
|
// an array has Items defined as an object schema, otherwise we qualify this JSON array as a tuple
|
||
|
// (yes, even if the Items array contains only one element).
|
||
|
// arrays in JSON schema may be unrestricted (i.e no Items specified).
|
||
|
// Note that arrays in Swagger MUST have Items. Nonetheless, we analyze unrestricted arrays.
|
||
|
//
|
||
|
// NOTE: the spec package misses the distinction between:
|
||
|
// items: [] and items: {}, so we consider both arrays here.
|
||
|
a.IsArray = a.isArrayType() && (a.schema.Items == nil || a.schema.Items.Schemas == nil)
|
||
|
if a.IsArray && a.hasItems {
|
||
|
if a.schema.Items.Schema != nil {
|
||
|
itsch, err := Schema(SchemaOpts{
|
||
|
Schema: a.schema.Items.Schema,
|
||
|
Root: a.root,
|
||
|
BasePath: a.basePath,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
a.IsSimpleArray = itsch.IsSimpleSchema
|
||
|
}
|
||
|
}
|
||
|
if a.IsArray && !a.hasItems {
|
||
|
a.IsSimpleArray = true
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferTuple() {
|
||
|
tuple := a.hasItems && a.schema.Items.Schemas != nil
|
||
|
a.IsTuple = tuple && !a.hasAdditionalItems
|
||
|
a.IsTupleWithExtra = tuple && a.hasAdditionalItems
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferBaseType() {
|
||
|
if a.isObjectType() {
|
||
|
a.IsBaseType = a.schema.Discriminator != ""
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) inferEnum() {
|
||
|
a.IsEnum = len(a.schema.Enum) > 0
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) initializeFlags() {
|
||
|
a.hasProps = len(a.schema.Properties) > 0
|
||
|
a.hasAllOf = len(a.schema.AllOf) > 0
|
||
|
a.hasRef = a.schema.Ref.String() != ""
|
||
|
|
||
|
a.hasItems = a.schema.Items != nil &&
|
||
|
(a.schema.Items.Schema != nil || len(a.schema.Items.Schemas) > 0)
|
||
|
|
||
|
a.hasAdditionalProps = a.schema.AdditionalProperties != nil &&
|
||
|
(a.schema.AdditionalProperties != nil || a.schema.AdditionalProperties.Allows)
|
||
|
|
||
|
a.hasAdditionalItems = a.schema.AdditionalItems != nil &&
|
||
|
(a.schema.AdditionalItems.Schema != nil || a.schema.AdditionalItems.Allows)
|
||
|
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) isObjectType() bool {
|
||
|
return !a.hasRef && (a.schema.Type == nil || a.schema.Type.Contains("") || a.schema.Type.Contains("object"))
|
||
|
}
|
||
|
|
||
|
func (a *AnalyzedSchema) isArrayType() bool {
|
||
|
return !a.hasRef && (a.schema.Type != nil && a.schema.Type.Contains("array"))
|
||
|
}
|