func TestTypeResolver_Refs(t *testing.T) { _, resolver, err := basicTaskListResolver(t) if assert.NoError(t, err) { // referenced objects for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) rt, err := resolver.ResolveSchema(sch, true) if assert.NoError(t, err) { assert.Equal(t, val.Expected, rt.GoType) assert.False(t, rt.IsAnonymous) assert.True(t, rt.IsNullable) assert.Equal(t, "object", rt.SwaggerType) } } // referenced array objects for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) rt, err := resolver.ResolveSchema(new(spec.Schema).CollectionOf(*sch), true) if assert.NoError(t, err) { assert.True(t, rt.IsArray) assert.Equal(t, "[]*"+val.Expected, rt.GoType) } } // for named objects // referenced objects for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) rt, err := resolver.ResolveSchema(sch, false) if assert.NoError(t, err) { assert.Equal(t, val.Expected, rt.GoType) assert.False(t, rt.IsAnonymous) assert.True(t, rt.IsNullable) assert.Equal(t, "object", rt.SwaggerType) } } // referenced array objects for _, val := range schRefVals { sch := new(spec.Schema) sch.Ref, _ = spec.NewRef("#/definitions/" + val.Type) rt, err := resolver.ResolveSchema(new(spec.Schema).CollectionOf(*sch), false) if assert.NoError(t, err) { assert.True(t, rt.IsArray) assert.Equal(t, "[]*"+val.Expected, rt.GoType) } } } }
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_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) 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) 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) 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) 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) 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) 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) } } } }
func (scp *schemaParser) parseIdentProperty(pkg *loader.PackageInfo, expr *ast.Ident, prop swaggerTypable) error { // find the file this selector points to file, gd, ts, err := findSourceFile(pkg, expr.Name) if err != nil { return swaggerSchemaForType(expr.Name, prop) } if at, ok := ts.Type.(*ast.ArrayType); ok { // the swagger spec defines strfmt base64 as []byte. // in that case we don't actually want to turn it into an array // but we want to turn it into a string if _, ok := at.Elt.(*ast.Ident); ok { if strfmtName, ok := strfmtName(gd.Doc); ok { prop.Typed("string", strfmtName) return nil } } // this is a selector, so most likely not base64 if strfmtName, ok := strfmtName(gd.Doc); ok { prop.Items().Typed("string", strfmtName) return nil } } // look at doc comments for swagger:strfmt [name] // when found this is the format name, create a schema with that name if strfmtName, ok := strfmtName(gd.Doc); ok { prop.Typed("string", strfmtName) return nil } switch tpe := ts.Type.(type) { case *ast.ArrayType: switch atpe := tpe.Elt.(type) { case *ast.Ident: return scp.parseIdentProperty(pkg, atpe, prop.Items()) case *ast.SelectorExpr: return scp.typeForSelector(file, atpe, prop.Items()) case *ast.StarExpr: return parseProperty(scp, file, atpe.X, prop.Items()) default: return fmt.Errorf("unknown selector type: %#v", atpe) } case *ast.StructType: sd := newSchemaDecl(file, gd, ts) sd.inferNames() ref, err := spec.NewRef("#/definitions/" + sd.Name) if err != nil { return err } prop.SetRef(ref) scp.postDecls = append(scp.postDecls, *sd) return nil case *ast.Ident: return scp.parseIdentProperty(pkg, tpe, prop) case *ast.SelectorExpr: return scp.typeForSelector(file, tpe, prop) case *ast.InterfaceType: sd := newSchemaDecl(file, gd, ts) sd.inferNames() ref, err := spec.NewRef("#/definitions/" + sd.Name) if err != nil { return err } prop.SetRef(ref) scp.postDecls = append(scp.postDecls, *sd) return nil default: return swaggerSchemaForType(expr.Name, prop) } }
func (ss *setOpResponses) Parse(lines []string) error { if len(lines) == 0 || (len(lines) == 1 && len(lines[0]) == 0) { return nil } var def *spec.Response var scr map[int]spec.Response for _, line := range lines { kv := strings.SplitN(line, ":", 2) var key, value string if len(kv) > 1 { key = strings.TrimSpace(kv[0]) if key == "" { // this must be some weird empty line continue } value = strings.TrimSpace(kv[1]) if value == "" { var resp spec.Response if strings.EqualFold("default", key) { if def == nil { def = &resp } } else { if sc, err := strconv.Atoi(key); err == nil { if scr == nil { scr = make(map[int]spec.Response) } scr[sc] = resp } } continue } var arrays int for strings.HasPrefix(value, "[]") { arrays++ value = value[2:] } var isDefinitionRef bool var ref spec.Ref var err error if arrays == 0 { ref, err = spec.NewRef("#/responses/" + value) } else { isDefinitionRef = true ref, err = spec.NewRef("#/definitions/" + value) } if _, ok := ss.responses[value]; !ok { if _, ok := ss.definitions[value]; ok { isDefinitionRef = true ref, err = spec.NewRef("#/definitions/" + value) } } else { } if err != nil { return err } var resp spec.Response if !isDefinitionRef { resp.Ref = ref } else { resp.Schema = new(spec.Schema) if arrays == 0 { resp.Schema.Ref = ref } else { cs := resp.Schema for i := 0; i < arrays; i++ { cs.Typed("array", "") cs.Items = new(spec.SchemaOrArray) cs.Items.Schema = new(spec.Schema) cs = cs.Items.Schema } cs.Ref = ref } } if strings.EqualFold("default", key) { if def == nil { def = &resp } } else { if sc, err := strconv.Atoi(key); err == nil { if scr == nil { scr = make(map[int]spec.Response) } scr[sc] = resp } } } } ss.set(def, scr) return nil }