func (scp *schemaParser) parseStructType(gofile *ast.File, bschema *spec.Schema, tpe *ast.StructType, seenPreviously map[string]struct{}) error { if tpe.Fields != nil { var schema *spec.Schema seenProperties := seenPreviously 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) { 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 } 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.parseEmbeddedStruct(gofile, schema, fld.Type, seenProperties); err != nil { return err } } } if schema != nil && len(bschema.AllOf) > 0 { bschema.AllOf = append(bschema.AllOf, *schema) } 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 { if len(fld.Names) > 0 && fld.Names[0] != nil && fld.Names[0].IsExported() { var nm, gnm string nm = fld.Names[0].Name gnm = nm if fld.Tag != nil && len(strings.TrimSpace(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}); err != nil { return err } sp := new(sectionedParser) sp.setDescription = func(lines []string) { ps.Description = joinDropLast(lines) } if ps.Ref.GetURL() == nil { 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}), } // check if this is a primitive, if so parse the validations from the // doc comments of the slice declaration. if ftpe, ok := fld.Type.(*ast.ArrayType); ok { if iftpe, ok := ftpe.Elt.(*ast.Ident); ok && iftpe.Obj == nil { if ps.Items != nil && ps.Items.Schema != nil { itemsTaggers := []tagParser{ newSingleLineTagParser("itemsMaximum", &setMaximum{schemaValidations{ps.Items.Schema}, rxf(rxMaximumFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMinimum", &setMinimum{schemaValidations{ps.Items.Schema}, rxf(rxMinimumFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMultipleOf", &setMultipleOf{schemaValidations{ps.Items.Schema}, rxf(rxMultipleOfFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMinLength", &setMinLength{schemaValidations{ps.Items.Schema}, rxf(rxMinLengthFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMaxLength", &setMaxLength{schemaValidations{ps.Items.Schema}, rxf(rxMaxLengthFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsPattern", &setPattern{schemaValidations{ps.Items.Schema}, rxf(rxPatternFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMinItems", &setMinItems{schemaValidations{ps.Items.Schema}, rxf(rxMinItemsFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsMaxItems", &setMaxItems{schemaValidations{ps.Items.Schema}, rxf(rxMaxItemsFmt, rxItemsPrefix)}), newSingleLineTagParser("itemsUnique", &setUnique{schemaValidations{ps.Items.Schema}, rxf(rxUniqueFmt, rxItemsPrefix)}), } // items matchers should go before the default matchers so they match first sp.taggers = append(itemsTaggers, sp.taggers...) } } } } else { sp.taggers = []tagParser{ newSingleLineTagParser("required", &setRequiredSchema{schema, nm}), } } if err := sp.Parse(fld.Doc); err != nil { return err } if nm != gnm { ps.AddExtension("x-go-name", gnm) } seenProperties[nm] = struct{}{} schema.Properties[nm] = ps } } for k := range schema.Properties { if _, ok := seenProperties[k]; !ok { delete(schema.Properties, k) } } } return nil }
func TestTypeResolver_ObjectType(t *testing.T) { _, resolver, err := basicTaskListResolver(t) resolver.ModelName = "TheModel" 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) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "map[string]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) 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) 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) if assert.NoError(t, err) { assert.True(t, rt.IsMap) assert.False(t, rt.IsComplexObject) assert.Equal(t, "map[string]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) 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) } } }