func bodyTypable(in string, schema *spec.Schema) (swaggerTypable, *spec.Schema) { if in == "body" { // get the schema for items on the schema property if schema == nil { schema = new(spec.Schema) } if schema.Items == nil { schema.Items = new(spec.SchemaOrArray) } if schema.Items.Schema == nil { schema.Items.Schema = new(spec.Schema) } schema.Typed("array", "") return schemaTypable{schema.Items.Schema, 0}, schema } return nil, nil }
func TestTypeResolver_AdditionalItems(t *testing.T) { _, resolver, err := basicTaskListResolver(t) tpe := spec.StringProperty() if assert.NoError(t, err) { // arrays of primitives and string formats with additional formats for _, val := range schTypeVals { var sch spec.Schema sch.Typed(val.Type, val.Format) var coll spec.Schema coll.Type = []string{"array"} coll.Items = new(spec.SchemaOrArray) coll.Items.Schema = tpe coll.AdditionalItems = new(spec.SchemaOrBool) coll.AdditionalItems.Schema = &sch rt, err := resolver.ResolveSchema(&coll, true, true) if assert.NoError(t, err) && assert.True(t, rt.IsArray) { assert.True(t, rt.HasAdditionalItems) assert.False(t, rt.IsNullable) //if assert.NotNil(t, rt.ElementType) { //assertPrimitiveResolve(t, "string", "", "string", *rt.ElementType) //} } } } }
func (scp *schemaParser) parseAllOfMember(gofile *ast.File, schema *spec.Schema, expr ast.Expr, seenPreviously map[string]struct{}) error { // TODO: check if struct is annotated with swagger:model or known in the definitions otherwise var pkg *loader.PackageInfo var file *ast.File var gd *ast.GenDecl var ts *ast.TypeSpec var err error switch tpe := expr.(type) { case *ast.Ident: // do lookup of type // take primitives into account, they should result in an error for swagger pkg, err = scp.packageForFile(gofile) if err != nil { return err } file, gd, ts, err = findSourceFile(pkg, tpe.Name) if err != nil { return err } case *ast.SelectorExpr: // look up package, file and then type pkg, err = scp.packageForSelector(gofile, tpe.X) if err != nil { return fmt.Errorf("embedded struct: %v", err) } file, gd, ts, err = findSourceFile(pkg, tpe.Sel.Name) if err != nil { return fmt.Errorf("embedded struct: %v", err) } default: return fmt.Errorf("unable to resolve allOf member for: %v\n", expr) } sd := newSchemaDecl(file, gd, ts) if sd.hasAnnotation() { ref, err := spec.NewRef("#/definitions/" + sd.Name) if err != nil { return err } schema.Ref = ref scp.postDecls = append(scp.postDecls, *sd) } else { switch st := ts.Type.(type) { case *ast.StructType: return scp.parseStructType(file, schema, st, seenPreviously) case *ast.InterfaceType: return scp.parseInterfaceType(file, schema, st, seenPreviously) } } return nil }
func TestTypeResolver_AnonymousStructs(t *testing.T) { _, resolver, err := basicTaskListResolver(t) if assert.NoError(t, err) { // anonymous structs should be accounted for parent := new(spec.Schema) parent.Typed("object", "") parent.Properties = make(map[string]spec.Schema) parent.Properties["name"] = *spec.StringProperty() parent.Properties["age"] = *spec.Int32Property() rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsNullable) assert.True(t, rt.IsAnonymous) assert.True(t, rt.IsComplexObject) } parent.Extensions = make(spec.Extensions) parent.Extensions[xIsNullable] = true rt, err = resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsNullable) assert.True(t, rt.IsAnonymous) assert.True(t, rt.IsComplexObject) } // Also test that it's nullable with just x-nullable parent.Extensions[xIsNullable] = false parent.Extensions[xNullable] = false rt, err = resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsNullable) assert.True(t, rt.IsAnonymous) assert.True(t, rt.IsComplexObject) } } }
func (_ TestInput) OpenAPIDefinition() *common.OpenAPIDefinition { schema := spec.Schema{} schema.Description = "Test input" schema.Properties = map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ Description: "Name of the input", Type: []string{"string"}, Format: "", }, }, "id": { SchemaProps: spec.SchemaProps{ Description: "ID of the input", Type: []string{"integer"}, Format: "int32", }, }, "tags": { SchemaProps: spec.SchemaProps{ Description: "", Type: []string{"array"}, Items: &spec.SchemaOrArray{ Schema: &spec.Schema{ SchemaProps: spec.SchemaProps{ Type: []string{"string"}, Format: "", }, }, }, }, }, } return &common.OpenAPIDefinition{ Schema: schema, Dependencies: []string{}, } }
func (_ TestOutput) OpenAPIDefinition() *common.OpenAPIDefinition { schema := spec.Schema{} schema.Description = "Test output" schema.Properties = map[string]spec.Schema{ "name": { SchemaProps: spec.SchemaProps{ Description: "Name of the output", Type: []string{"string"}, Format: "", }, }, "count": { SchemaProps: spec.SchemaProps{ Description: "Number of outputs", Type: []string{"integer"}, Format: "int32", }, }, } return &common.OpenAPIDefinition{ Schema: schema, Dependencies: []string{}, } }
func (sg *schemaGenContext) buildItems() error { presentsAsSingle := sg.Schema.Items != nil && sg.Schema.Items.Schema != nil if presentsAsSingle && sg.Schema.AdditionalItems != nil { // unsure if htis a valid of invalid schema return fmt.Errorf("single schema (%s) can't have additional items", sg.Name) } if presentsAsSingle { return sg.buildArray() } if sg.Schema.Items == nil { return nil } // This is a tuple, build a new model that represents this if sg.Named { sg.GenSchema.Name = sg.Name sg.GenSchema.GoType = sg.TypeResolver.goTypeName(sg.Name) for i, s := range sg.Schema.Items.Schemas { elProp := sg.NewTupleElement(&s, i) if err := elProp.makeGenSchema(); err != nil { return err } sg.MergeResult(elProp, false) elProp.GenSchema.Name = "p" + strconv.Itoa(i) sg.GenSchema.Properties = append(sg.GenSchema.Properties, elProp.GenSchema) } return nil } // for an anonoymous object, first build the new object // and then replace the current one with a $ref to the // new tuple object var sch spec.Schema sch.Typed("object", "") sch.Properties = make(map[string]spec.Schema) for i, v := range sg.Schema.Items.Schemas { sch.Required = append(sch.Required, "P"+strconv.Itoa(i)) sch.Properties["P"+strconv.Itoa(i)] = v } sch.AdditionalItems = sg.Schema.AdditionalItems tup := sg.makeNewStruct(sg.GenSchema.Name+"Tuple"+strconv.Itoa(sg.Index), sch) tup.IsTuple = true if err := tup.makeGenSchema(); err != nil { return err } tup.GenSchema.IsTuple = true tup.GenSchema.IsComplexObject = false tup.GenSchema.Title = tup.GenSchema.Name + " a representation of an anonymous Tuple type" tup.GenSchema.Description = "" sg.ExtraSchemas[tup.Name] = tup.GenSchema sg.Schema = *spec.RefProperty("#/definitions/" + tup.Name) if err := sg.makeGenSchema(); err != nil { return err } sg.MergeResult(tup, false) return nil }
func TestTypeResolver_TupleTypes(t *testing.T) { _, resolver, err := basicTaskListResolver(t) if assert.NoError(t, err) { // tuple type (items with multiple schemas) parent := new(spec.Schema) parent.Typed("array", "") parent.Items = new(spec.SchemaOrArray) parent.Items.Schemas = append( parent.Items.Schemas, *spec.StringProperty(), *spec.Int64Property(), *spec.Float64Property(), *spec.BoolProperty(), *spec.ArrayProperty(spec.StringProperty()), *spec.RefProperty("#/definitions/Comment"), ) rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.False(t, rt.IsArray) assert.True(t, rt.IsTuple) } } }
func (scp *schemaParser) createParser(nm string, schema, ps *spec.Schema, fld *ast.Field) *sectionedParser { sp := new(sectionedParser) sp.setDescription = func(lines []string) { ps.Description = joinDropLast(lines) } if ps.Ref.String() == "" { sp.taggers = []tagParser{ newSingleLineTagParser("maximum", &setMaximum{schemaValidations{ps}, rxf(rxMaximumFmt, "")}), newSingleLineTagParser("minimum", &setMinimum{schemaValidations{ps}, rxf(rxMinimumFmt, "")}), newSingleLineTagParser("multipleOf", &setMultipleOf{schemaValidations{ps}, rxf(rxMultipleOfFmt, "")}), newSingleLineTagParser("minLength", &setMinLength{schemaValidations{ps}, rxf(rxMinLengthFmt, "")}), newSingleLineTagParser("maxLength", &setMaxLength{schemaValidations{ps}, rxf(rxMaxLengthFmt, "")}), newSingleLineTagParser("pattern", &setPattern{schemaValidations{ps}, rxf(rxPatternFmt, "")}), newSingleLineTagParser("minItems", &setMinItems{schemaValidations{ps}, rxf(rxMinItemsFmt, "")}), newSingleLineTagParser("maxItems", &setMaxItems{schemaValidations{ps}, rxf(rxMaxItemsFmt, "")}), newSingleLineTagParser("unique", &setUnique{schemaValidations{ps}, rxf(rxUniqueFmt, "")}), newSingleLineTagParser("required", &setRequiredSchema{schema, nm}), newSingleLineTagParser("readOnly", &setReadOnlySchema{ps}), newSingleLineTagParser("discriminator", &setDiscriminator{schema, nm}), } itemsTaggers := func(items *spec.Schema, level int) []tagParser { // the expression is 1-index based not 0-index itemsPrefix := fmt.Sprintf(rxItemsPrefixFmt, level+1) return []tagParser{ newSingleLineTagParser(fmt.Sprintf("items%dMaximum", level), &setMaximum{schemaValidations{items}, rxf(rxMaximumFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMinimum", level), &setMinimum{schemaValidations{items}, rxf(rxMinimumFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMultipleOf", level), &setMultipleOf{schemaValidations{items}, rxf(rxMultipleOfFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMinLength", level), &setMinLength{schemaValidations{items}, rxf(rxMinLengthFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMaxLength", level), &setMaxLength{schemaValidations{items}, rxf(rxMaxLengthFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dPattern", level), &setPattern{schemaValidations{items}, rxf(rxPatternFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMinItems", level), &setMinItems{schemaValidations{items}, rxf(rxMinItemsFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dMaxItems", level), &setMaxItems{schemaValidations{items}, rxf(rxMaxItemsFmt, itemsPrefix)}), newSingleLineTagParser(fmt.Sprintf("items%dUnique", level), &setUnique{schemaValidations{items}, rxf(rxUniqueFmt, itemsPrefix)}), } } // check if this is a primitive, if so parse the validations from the // doc comments of the slice declaration. if ftped, ok := fld.Type.(*ast.ArrayType); ok { ftpe := ftped items, level := ps.Items, 0 for items != nil && items.Schema != nil { switch iftpe := ftpe.Elt.(type) { case *ast.ArrayType: eleTaggers := itemsTaggers(items.Schema, level) sp.taggers = append(eleTaggers, sp.taggers...) ftpe = iftpe case *ast.Ident: if iftpe.Obj == nil { sp.taggers = append(itemsTaggers(items.Schema, level), sp.taggers...) } break //default: //return fmt.Errorf("unknown field type (%T) ele for %q", iftpe, nm) } items = items.Schema.Items level = level + 1 } } } else { sp.taggers = []tagParser{ newSingleLineTagParser("required", &setRequiredSchema{schema, nm}), } } return sp }
func (scp *schemaParser) parseStructType(gofile *ast.File, bschema *spec.Schema, tpe *ast.StructType, seenPreviously map[string]struct{}) error { if tpe.Fields == nil { return nil } var schema *spec.Schema seenProperties := seenPreviously hasAllOf := false for _, fld := range tpe.Fields.List { if len(fld.Names) == 0 { // if this created an allOf property then we have to rejig the schema var // because all the fields collected that aren't from embedded structs should go in // their own proper schema // first process embedded structs in order of embedding if allOfMember(fld.Doc) { hasAllOf = true if schema == nil { schema = new(spec.Schema) } var newSch spec.Schema // when the embedded struct is annotated with swagger:allOf it will be used as allOf property // otherwise the fields will just be included as normal properties if err := scp.parseAllOfMember(gofile, &newSch, fld.Type, seenProperties); err != nil { return err } if fld.Doc != nil { for _, cmt := range fld.Doc.List { for _, ln := range strings.Split(cmt.Text, "\n") { matches := rxAllOf.FindStringSubmatch(ln) ml := len(matches) if ml > 1 { mv := matches[ml-1] if mv != "" { bschema.AddExtension("x-class", mv) } } } } } bschema.AllOf = append(bschema.AllOf, newSch) continue } if schema == nil { schema = bschema } // when the embedded struct is annotated with swagger:allOf it will be used as allOf property // otherwise the fields will just be included as normal properties if err := scp.parseEmbeddedType(gofile, schema, fld.Type, seenProperties); err != nil { return err } } } if schema == nil { schema = bschema } // then add and possibly override values if schema.Properties == nil { schema.Properties = make(map[string]spec.Schema) } schema.Typed("object", "") for _, fld := range tpe.Fields.List { var tag string if fld.Tag != nil { val, err := strconv.Unquote(fld.Tag.Value) if err == nil { tag = reflect.StructTag(val).Get("json") } } if len(fld.Names) > 0 && fld.Names[0] != nil && fld.Names[0].IsExported() && (tag == "" || tag[0] != '-') { var nm, gnm string nm = fld.Names[0].Name gnm = nm if fld.Tag != nil && len(strings.TrimSpace(fld.Tag.Value)) > 0 /*&& fld.Tag.Value[0] != '-'*/ { tv, err := strconv.Unquote(fld.Tag.Value) if err != nil { return err } if strings.TrimSpace(tv) != "" { st := reflect.StructTag(tv) if st.Get("json") != "" { nm = strings.Split(st.Get("json"), ",")[0] } } } ps := schema.Properties[nm] if err := parseProperty(scp, gofile, fld.Type, schemaTypable{&ps, 0}); err != nil { return err } if err := scp.createParser(nm, schema, &ps, fld).Parse(fld.Doc); err != nil { return err } if nm != gnm { ps.AddExtension("x-go-name", gnm) } seenProperties[nm] = struct{}{} schema.Properties[nm] = ps } } if schema != nil && hasAllOf { bschema.AllOf = append(bschema.AllOf, *schema) } for k := range schema.Properties { if _, ok := seenProperties[k]; !ok { delete(schema.Properties, k) } } return nil }
func (scp *schemaParser) parseInterfaceType(gofile *ast.File, bschema *spec.Schema, tpe *ast.InterfaceType, seenPreviously map[string]struct{}) error { if tpe.Methods == nil { return nil } // first check if this has embedded interfaces, if so make sure to refer to those by ref // when they are decorated with an allOf annotation // go over the method list again and this time collect the nullary methods and parse the comments // as if they are properties on a struct var schema *spec.Schema seenProperties := seenPreviously hasAllOf := false for _, fld := range tpe.Methods.List { if len(fld.Names) == 0 { // if this created an allOf property then we have to rejig the schema var // because all the fields collected that aren't from embedded structs should go in // their own proper schema // first process embedded structs in order of embedding if allOfMember(fld.Doc) { hasAllOf = true if schema == nil { schema = new(spec.Schema) } var newSch spec.Schema // when the embedded struct is annotated with swagger:allOf it will be used as allOf property // otherwise the fields will just be included as normal properties if err := scp.parseAllOfMember(gofile, &newSch, fld.Type, seenProperties); err != nil { return err } if fld.Doc != nil { for _, cmt := range fld.Doc.List { for _, ln := range strings.Split(cmt.Text, "\n") { matches := rxAllOf.FindStringSubmatch(ln) ml := len(matches) if ml > 1 { mv := matches[ml-1] if mv != "" { bschema.AddExtension("x-class", mv) } } } } } bschema.AllOf = append(bschema.AllOf, newSch) continue } var newSch spec.Schema // when the embedded struct is annotated with swagger:allOf it will be used as allOf property // otherwise the fields will just be included as normal properties if err := scp.parseEmbeddedType(gofile, &newSch, fld.Type, seenProperties); err != nil { return err } bschema.AllOf = append(bschema.AllOf, newSch) hasAllOf = true } } if schema == nil { schema = bschema } // then add and possibly override values if schema.Properties == nil { schema.Properties = make(map[string]spec.Schema) } schema.Typed("object", "") for _, fld := range tpe.Methods.List { if mtpe, ok := fld.Type.(*ast.FuncType); ok && mtpe.Params.NumFields() == 0 && mtpe.Results.NumFields() == 1 { gnm := fld.Names[0].Name nm := gnm if fld.Doc != nil { for _, cmt := range fld.Doc.List { for _, ln := range strings.Split(cmt.Text, "\n") { matches := rxName.FindStringSubmatch(ln) ml := len(matches) if ml > 1 { nm = matches[ml-1] } } } } ps := schema.Properties[nm] if err := parseProperty(scp, gofile, mtpe.Results.List[0].Type, schemaTypable{&ps, 0}); err != nil { return err } if err := scp.createParser(nm, schema, &ps, fld).Parse(fld.Doc); err != nil { return err } if nm != gnm { ps.AddExtension("x-go-name", gnm) } seenProperties[nm] = struct{}{} schema.Properties[nm] = ps } } if schema != nil && hasAllOf { bschema.AllOf = append(bschema.AllOf, *schema) } for k := range schema.Properties { if _, ok := seenProperties[k]; !ok { delete(schema.Properties, k) } } return nil }
func assertBuiltinAdditionalPropertiesElem(t testing.TB, resolver *typeResolver, aliased bool, i int, val builtinVal) bool { val.Nullable = false if nullableExtension(val.Extensions) != nil { val.Nullable = *nullableExtension(val.Extensions) } sliceType := "map[string]" + val.Expected if val.Nullable { sliceType = "map[string]*" + val.Expected } val.Expected = sliceType val.Aliased = aliased if aliased { val.AliasedType = val.Expected val.Expected = "models.MyAliasedThing" } items := new(spec.Schema) items.Typed(val.Type, val.Format) items.Default = val.Default items.ReadOnly = val.ReadOnly items.Extensions = val.Extensions items.Minimum = val.Minimum items.Maximum = val.Maximum items.MultipleOf = val.MultipleOf items.MinLength = val.MinLength items.MaxLength = val.MaxLength sch := spec.MapProperty(items) rt, err := resolver.ResolveSchema(sch, !aliased, val.Required) if assert.NoError(t, err) { // pretty.Println(rt) if val.Nullable { if !assert.True(t, rt.ElemType.IsNullable, "expected nullable for item at: %d", i) { // fmt.Println("isRequired:", val.Required) // pretty.Println(sch) return false } } else { if !assert.False(t, rt.ElemType != nil && rt.ElemType.IsNullable, "expected not nullable for item at: %d", i) { // fmt.Println("isRequired:", val.Required) // pretty.Println(sch) return false } } if !assert.Equal(t, val.Aliased, rt.IsAliased, "expected (%q, %q) to be an aliased type at %d", val.Type, val.Format, i) { return false } if val.Aliased { if !assert.Equal(t, val.AliasedType, rt.AliasedType, "expected %q (%q, %q) to be aliased as %q, but got %q at %d", val.Expected, val.Type, val.Format, val.AliasedType, rt.AliasedType, i) { return false } } if !assertBuiltinSliceElemnResolve(t, val.Type, val.Format, val.Expected, rt, i) { return false } } return true }
func assertBuiltinVal(t testing.TB, resolver *typeResolver, aliased bool, i int, val builtinVal) bool { val.Aliased = aliased if aliased { val.AliasedType = val.Expected val.Expected = "models.MyAliasedThing" } sch := new(spec.Schema) sch.Typed(val.Type, val.Format) sch.Default = val.Default sch.ReadOnly = val.ReadOnly sch.Extensions = val.Extensions sch.Minimum = val.Minimum sch.Maximum = val.Maximum sch.MultipleOf = val.MultipleOf sch.MinLength = val.MinLength sch.MaxLength = val.MaxLength rt, err := resolver.ResolveSchema(sch, !aliased, val.Required) if assert.NoError(t, err) { if val.Nullable { if !assert.True(t, rt.IsNullable, "expected nullable for item at: %d", i) { // fmt.Println("isRequired:", val.Required) // pretty.Println(sch) return false } } else { if !assert.False(t, rt.IsNullable, "expected not nullable for item at: %d", i) { // fmt.Println("isRequired:", val.Required) // pretty.Println(sch) return false } } if !assert.Equal(t, val.Aliased, rt.IsAliased, "expected (%q, %q) to be an aliased type", val.Type, val.Format) { return false } if val.Aliased { if !assert.Equal(t, val.AliasedType, rt.AliasedType, "expected %q (%q, %q) to be aliased as %q, but got %q", val.Expected, val.Type, val.Format, val.AliasedType, rt.AliasedType) { return false } } if !assertBuiltinResolve(t, val.Type, val.Format, val.Expected, rt, i) { return false } } return true }
func TestTypeResolver_BasicTypes(t *testing.T) { _, resolver, err := basicTaskListResolver(t) if assert.NoError(t, err) { // primitives and string formats for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) rt, err := resolver.ResolveSchema(sch, true, false) if assert.NoError(t, err) { assert.False(t, rt.IsNullable, "expected %s with format %q to not be nullable", val.Type, val.Format) assertPrimitiveResolve(t, val.Type, val.Format, val.Expected, rt) } } // arrays of primitives and string formats for _, val := range schTypeVals { var sch spec.Schema sch.Typed(val.Type, val.Format) rt, err := resolver.ResolveSchema(new(spec.Schema).CollectionOf(sch), true, true) if assert.NoError(t, err) { assert.True(t, rt.IsArray) } } // primitives and string formats for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) sch.Extensions = make(spec.Extensions) sch.Extensions[xIsNullable] = true rt, err := resolver.ResolveSchema(sch, true, false) if assert.NoError(t, err) { assert.True(t, rt.IsNullable, "expected %q (%q) to be nullable", val.Type, val.Format) assertPrimitiveResolve(t, val.Type, val.Format, val.Expected, rt) } // Test x-nullable overrides x-isnullable sch.Extensions[xIsNullable] = false sch.Extensions[xNullable] = true rt, err = resolver.ResolveSchema(sch, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsNullable, "expected %q (%q) to be nullable", val.Type, val.Format) assertPrimitiveResolve(t, val.Type, val.Format, val.Expected, rt) } // Test x-nullable without x-isnullable delete(sch.Extensions, xIsNullable) sch.Extensions[xNullable] = true rt, err = resolver.ResolveSchema(sch, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsNullable, "expected %q (%q) to be nullable", val.Type, val.Format) assertPrimitiveResolve(t, val.Type, val.Format, val.Expected, rt) } } // arrays of primitives and string formats for _, val := range schTypeVals { var sch spec.Schema sch.Typed(val.Type, val.Format) sch.AddExtension(xIsNullable, true) rt, err := resolver.ResolveSchema(new(spec.Schema).CollectionOf(sch), true, true) if assert.NoError(t, err) { assert.True(t, rt.IsArray) } } } }
func TestTypeResolver_ObjectType(t *testing.T) { _, resolver, err := basicTaskListResolver(t) resolver.ModelName = "TheModel" resolver.KnownDefs["TheModel"] = struct{}{} defer func() { resolver.ModelName = "" }() if assert.NoError(t, err) { //very poor schema definitions (as in none) types := []string{"object", ""} for _, tpe := range types { sch := new(spec.Schema) sch.Typed(tpe, "") rt, err := resolver.ResolveSchema(sch, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "interface{}", rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } sch.Properties = make(map[string]spec.Schema) var ss spec.Schema sch.Properties["tags"] = *(&ss).CollectionOf(*spec.StringProperty()) rt, err = resolver.ResolveSchema(sch, false, true) assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "models.TheModel", rt.GoType) assert.Equal(t, "object", rt.SwaggerType) sch.Properties = nil nsch := new(spec.Schema) nsch.Typed(tpe, "") nsch.AllOf = []spec.Schema{*sch} rt, err = resolver.ResolveSchema(nsch, false, true) if assert.NoError(t, err) { assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "models.TheModel", rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } sch := new(spec.Schema) rt, err := resolver.ResolveSchema(sch, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "interface{}", rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } sch = new(spec.Schema) var sp spec.Schema sp.Typed("object", "") sch.AllOf = []spec.Schema{sp} rt, err = resolver.ResolveSchema(sch, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "models.TheModel", rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } }
func TestTypeResolver_AdditionalProperties(t *testing.T) { _, resolver, err := basicTaskListResolver(t) if assert.NoError(t, err) { // primitives as additional properties for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) parent := new(spec.Schema) parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = sch rt, err := resolver.ResolveSchema(parent, true, false) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "map[string]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } // array of primitives as additional properties for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) parent := new(spec.Schema) parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = new(spec.Schema).CollectionOf(*sch) rt, err := resolver.ResolveSchema(parent, true, false) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "map[string][]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } // refs as additional properties for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) parent := new(spec.Schema) parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = sch rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "map[string]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } // when additional properties and properties present, it's a complex object // primitives as additional properties for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) parent := new(spec.Schema) parent.Properties = make(map[string]spec.Schema) parent.Properties["id"] = *spec.Int32Property() parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = sch rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "map[string]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } // array of primitives as additional properties for _, val := range schTypeVals { sch := new(spec.Schema) sch.Typed(val.Type, val.Format) parent := new(spec.Schema) parent.Properties = make(map[string]spec.Schema) parent.Properties["id"] = *spec.Int32Property() parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = new(spec.Schema).CollectionOf(*sch) rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "map[string][]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } // refs as additional properties for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) parent := new(spec.Schema) parent.Properties = make(map[string]spec.Schema) parent.Properties["id"] = *spec.Int32Property() parent.AdditionalProperties = new(spec.SchemaOrBool) parent.AdditionalProperties.Schema = sch rt, err := resolver.ResolveSchema(parent, true, true) if assert.NoError(t, err) { assert.True(t, rt.IsComplexObject) assert.False(t, rt.IsMap) assert.Equal(t, "map[string]"+val.Expected, rt.GoType) assert.Equal(t, "object", rt.SwaggerType) } } } }