diff --git a/gen/gen.go b/gen/gen.go index b80fd21f4..d51b42246 100644 --- a/gen/gen.go +++ b/gen/gen.go @@ -8,7 +8,7 @@ import ( "io" "log" "os" - "path" + "path/filepath" "strings" "text/template" "time" @@ -95,10 +95,14 @@ func (g *Gen) Build(config *Config) error { return err } - packageName := path.Base(config.OutputDir) - docFileName := path.Join(config.OutputDir, "docs.go") - jsonFileName := path.Join(config.OutputDir, "swagger.json") - yamlFileName := path.Join(config.OutputDir, "swagger.yaml") + absOutputDir, err := filepath.Abs(config.OutputDir) + if err != nil { + return err + } + packageName := filepath.Base(absOutputDir) + docFileName := filepath.Join(config.OutputDir, "docs.go") + jsonFileName := filepath.Join(config.OutputDir, "swagger.json") + yamlFileName := filepath.Join(config.OutputDir, "swagger.yaml") docs, err := os.Create(docFileName) if err != nil { diff --git a/gen/gen_test.go b/gen/gen_test.go index ac7f2bf9e..a61ccdec0 100644 --- a/gen/gen_test.go +++ b/gen/gen_test.go @@ -4,7 +4,6 @@ import ( "errors" "os" "os/exec" - "path" "path/filepath" "testing" @@ -24,9 +23,9 @@ func TestGen_Build(t *testing.T) { assert.NoError(t, New().Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -48,9 +47,9 @@ func TestGen_BuildSnakecase(t *testing.T) { assert.NoError(t, New().Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -72,9 +71,9 @@ func TestGen_BuildLowerCamelcase(t *testing.T) { assert.NoError(t, New().Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -116,8 +115,8 @@ func TestGen_jsonToYAML(t *testing.T) { assert.Error(t, gen.Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -226,9 +225,9 @@ func TestGen_configWithOutputDir(t *testing.T) { assert.NoError(t, New().Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -334,9 +333,9 @@ func TestGen_GeneratedDoc(t *testing.T) { assert.NoError(t, cmd.Run()) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { @@ -360,9 +359,9 @@ func TestGen_cgoImports(t *testing.T) { assert.NoError(t, New().Build(config)) expectedFiles := []string{ - path.Join(config.OutputDir, "docs.go"), - path.Join(config.OutputDir, "swagger.json"), - path.Join(config.OutputDir, "swagger.yaml"), + filepath.Join(config.OutputDir, "docs.go"), + filepath.Join(config.OutputDir, "swagger.json"), + filepath.Join(config.OutputDir, "swagger.yaml"), } for _, expectedFile := range expectedFiles { if _, err := os.Stat(expectedFile); os.IsNotExist(err) { diff --git a/operation.go b/operation.go index 4450bf697..6975360aa 100644 --- a/operation.go +++ b/operation.go @@ -13,7 +13,6 @@ import ( "strconv" "strings" - "github.com/go-openapi/jsonreference" "github.com/go-openapi/spec" "golang.org/x/tools/go/loader" ) @@ -261,19 +260,11 @@ func (operation *Operation) ParseParamComment(commentLine string, astFile *ast.F return nil } case "body": - switch objectType { - case "primitive": - param.Schema.Type = spec.StringOrArray{refType} - case "array": - refType = "[]" + refType - fallthrough - case "object": - schema, err := operation.parseObjectSchema(refType, astFile) - if err != nil { - return err - } - param.Schema = schema + schema, err := operation.parseAPIObjectSchema(objectType, refType, astFile) + if err != nil { + return err } + param.Schema = schema default: return fmt.Errorf("%s is not supported paramType", paramType) } @@ -631,21 +622,18 @@ var combinedPattern = regexp.MustCompile(`^([\w\-\.\/\[\]]+)\{(.*)\}$`) func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) { switch { case refType == "interface{}": - return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{"object"}}}, nil + return PrimitiveSchema("object"), nil case IsGolangPrimitiveType(refType): refType = TransToValidSchemeType(refType) - return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}}, nil + return PrimitiveSchema(refType), nil case IsPrimitiveType(refType): - return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}}, nil + return PrimitiveSchema(refType), nil case strings.HasPrefix(refType, "[]"): schema, err := operation.parseObjectSchema(refType[2:], astFile) if err != nil { return nil, err } - return &spec.Schema{SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{Schema: schema}}, - }, nil + return spec.ArrayProperty(schema), nil case strings.HasPrefix(refType, "map["): //ignore key type idx := strings.Index(refType, "]") @@ -653,24 +641,17 @@ func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File) return nil, fmt.Errorf("invalid type: %s", refType) } refType = refType[idx+1:] - var valueSchema spec.SchemaOrBool if refType == "interface{}" { - valueSchema.Allows = true - } else { - schema, err := operation.parseObjectSchema(refType, astFile) - if err != nil { - return &spec.Schema{}, err - } - valueSchema.Schema = schema + return spec.MapProperty(nil), nil + } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &valueSchema, - }, - }, nil + schema, err := operation.parseObjectSchema(refType, astFile) + if err != nil { + return nil, err + } + return spec.MapProperty(schema), nil case strings.Contains(refType, "{"): - return operation.parseResponseCombinedObjectSchema(refType, astFile) + return operation.parseCombinedObjectSchema(refType, astFile) default: if operation.parser != nil { // checking refType has existing in 'TypeDefinitions' refNewType, typeSpec, err := operation.registerSchemaType(refType, astFile) @@ -679,13 +660,12 @@ func (operation *Operation) parseObjectSchema(refType string, astFile *ast.File) } refType = TypeDocName(refNewType, typeSpec) } - return &spec.Schema{SchemaProps: spec.SchemaProps{Ref: spec.Ref{ - Ref: jsonreference.MustCreateRef("#/definitions/" + refType), - }}}, nil + + return RefSchema(refType), nil } } -func (operation *Operation) parseResponseCombinedObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) { +func (operation *Operation) parseCombinedObjectSchema(refType string, astFile *ast.File) (*spec.Schema, error) { matches := combinedPattern.FindStringSubmatch(refType) if len(matches) != 3 { return nil, fmt.Errorf("invalid type: %s", refType) @@ -714,44 +694,26 @@ func (operation *Operation) parseResponseCombinedObjectSchema(refType string, as props := map[string]spec.Schema{} for _, field := range fields { if matches := strings.SplitN(field, "=", 2); len(matches) == 2 { - if strings.HasPrefix(matches[1], "[]") { - itemSchema, err := operation.parseObjectSchema(matches[1][2:], astFile) - if err != nil { - return nil, err - } - props[matches[0]] = spec.Schema{SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{Schema: itemSchema}}, - } - } else { - schema, err := operation.parseObjectSchema(matches[1], astFile) - if err != nil { - return nil, err - } - props[matches[0]] = *schema + schema, err := operation.parseObjectSchema(matches[1], astFile) + if err != nil { + return nil, err } + props[matches[0]] = *schema } } if len(props) == 0 { return schema, nil } - return &spec.Schema{ + return spec.ComposedSchema(*schema, spec.Schema{ SchemaProps: spec.SchemaProps{ - AllOf: []spec.Schema{ - *schema, - { - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - Properties: props, - }, - }, - }, + Type: []string{"object"}, + Properties: props, }, - }, nil + }), nil } -func (operation *Operation) parseResponseSchema(schemaType, refType string, astFile *ast.File) (*spec.Schema, error) { +func (operation *Operation) parseAPIObjectSchema(schemaType, refType string, astFile *ast.File) (*spec.Schema, error) { switch schemaType { case "object": if !strings.HasPrefix(refType, "[]") { @@ -764,12 +726,9 @@ func (operation *Operation) parseResponseSchema(schemaType, refType string, astF if err != nil { return nil, err } - return &spec.Schema{SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{Schema: schema}}, - }, nil + return spec.ArrayProperty(schema), nil default: - return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{schemaType}}}, nil + return PrimitiveSchema(schemaType), nil } } @@ -794,7 +753,7 @@ func (operation *Operation) ParseResponseComment(commentLine string, astFile *as schemaType := strings.Trim(matches[2], "{}") refType := matches[3] - schema, err := operation.parseResponseSchema(schemaType, refType, astFile) + schema, err := operation.parseAPIObjectSchema(schemaType, refType, astFile) if err != nil { return err } diff --git a/parser.go b/parser.go index b4871f433..4f972b78e 100644 --- a/parser.go +++ b/parser.go @@ -704,13 +704,7 @@ func (parser *Parser) parseTypeExpr(pkgName, typeName string, typeExpr ast.Expr) } } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: spec.Ref{ - Ref: jsonreference.MustCreateRef("#/definitions/" + refTypeName), - }, - }, - }, nil + return RefSchema(refTypeName), nil // type Foo *Baz case *ast.StarExpr: @@ -722,14 +716,7 @@ func (parser *Parser) parseTypeExpr(pkgName, typeName string, typeExpr ast.Expr) if err != nil { return &spec.Schema{}, err } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"array"}, - Items: &spec.SchemaOrArray{ - Schema: itemSchema, - }, - }, - }, nil + return spec.ArrayProperty(itemSchema), nil // type Foo pkg.Bar case *ast.SelectorExpr: @@ -739,32 +726,20 @@ func (parser *Parser) parseTypeExpr(pkgName, typeName string, typeExpr ast.Expr) // type Foo map[string]Bar case *ast.MapType: - var valueSchema spec.SchemaOrBool if _, ok := expr.Value.(*ast.InterfaceType); ok { - valueSchema.Allows = true - } else { - schema, err := parser.parseTypeExpr(pkgName, "", expr.Value) - if err != nil { - return &spec.Schema{}, err - } - valueSchema.Schema = schema + return spec.MapProperty(nil), nil } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - AdditionalProperties: &valueSchema, - }, - }, nil + schema, err := parser.parseTypeExpr(pkgName, "", expr.Value) + if err != nil { + return &spec.Schema{}, err + } + return spec.MapProperty(schema), nil // ... default: Printf("Type definition of type '%T' is not supported yet. Using 'object' instead.\n", typeExpr) } - return &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{"object"}, - }, - }, nil + return PrimitiveSchema("object"), nil } func (parser *Parser) parseStruct(pkgName string, fields *ast.FieldList) (*spec.Schema, error) { @@ -966,25 +941,11 @@ func (parser *Parser) parseStructField(pkgName string, field *ast.Field) (map[st if structField.isRequired { required = append(required, structField.name) } - properties[structField.name] = spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{structField.schemaType}, - Description: structField.desc, - Required: required, - Items: &spec.SchemaOrArray{ - Schema: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Ref: spec.Ref{ - Ref: jsonreference.MustCreateRef("#/definitions/" + TypeDocName(pkgName, typeSpec)), - }, - }, - }, - }, - }, - SwaggerSchemaProps: spec.SwaggerSchemaProps{ - ReadOnly: structField.readOnly, - }, - } + schema := spec.ArrayProperty(RefSchema(TypeDocName(pkgName, typeSpec))) + schema.Description = structField.desc + schema.Required = required + schema.ReadOnly = structField.readOnly + properties[structField.name] = *schema } else if structField.arrayType == "object" { // Anonymous struct if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { // if array @@ -1019,35 +980,19 @@ func (parser *Parser) parseStructField(pkgName string, field *ast.Field) (map[st } } else { schema, _ := parser.parseTypeExpr(pkgName, "", astTypeArray.Elt) - properties[structField.name] = spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{structField.schemaType}, - Description: structField.desc, - Items: &spec.SchemaOrArray{ - Schema: schema, - }, - }, - SwaggerSchemaProps: spec.SwaggerSchemaProps{ - ReadOnly: structField.readOnly, - }, - } + schema = spec.ArrayProperty(schema) + schema.Description = structField.desc + schema.ReadOnly = structField.readOnly + properties[structField.name] = *schema } } } else if structField.arrayType == "array" { if astTypeArray, ok := field.Type.(*ast.ArrayType); ok { schema, _ := parser.parseTypeExpr(pkgName, "", astTypeArray.Elt) - properties[structField.name] = spec.Schema{ - SchemaProps: spec.SchemaProps{ - Type: []string{structField.schemaType}, - Description: structField.desc, - Items: &spec.SchemaOrArray{ - Schema: schema, - }, - }, - SwaggerSchemaProps: spec.SwaggerSchemaProps{ - ReadOnly: structField.readOnly, - }, - } + schema = spec.ArrayProperty(schema) + schema.Description = structField.desc + schema.ReadOnly = structField.readOnly + properties[structField.name] = *schema } } else { // standard type in array diff --git a/parser_test.go b/parser_test.go index b6cdd4632..c172dab6d 100644 --- a/parser_test.go +++ b/parser_test.go @@ -6,7 +6,6 @@ import ( "go/token" "io/ioutil" "os" - "path" "path/filepath" "testing" @@ -2256,7 +2255,7 @@ func TestParseComposition(t *testing.T) { err := p.ParseAPI(searchDir, mainAPIFile) assert.NoError(t, err) - expected, err := ioutil.ReadFile(path.Join(searchDir, "expected.json")) + expected, err := ioutil.ReadFile(filepath.Join(searchDir, "expected.json")) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") @@ -2272,7 +2271,7 @@ func TestParseImportAliases(t *testing.T) { err := p.ParseAPI(searchDir, mainAPIFile) assert.NoError(t, err) - expected, err := ioutil.ReadFile(path.Join(searchDir, "expected.json")) + expected, err := ioutil.ReadFile(filepath.Join(searchDir, "expected.json")) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") @@ -2288,7 +2287,7 @@ func TestParseNested(t *testing.T) { err := p.ParseAPI(searchDir, mainAPIFile) assert.NoError(t, err) - expected, err := ioutil.ReadFile(path.Join(searchDir, "expected.json")) + expected, err := ioutil.ReadFile(filepath.Join(searchDir, "expected.json")) assert.NoError(t, err) b, _ := json.MarshalIndent(p.swagger, "", " ") diff --git a/schema.go b/schema.go index 539340fae..425c5d185 100644 --- a/schema.go +++ b/schema.go @@ -2,6 +2,7 @@ package swag import ( "fmt" + "github.com/go-openapi/spec" "go/ast" "strings" ) @@ -115,3 +116,13 @@ func TypeDocName(pkgName string, spec *ast.TypeSpec) string { return pkgName } + +//RefSchema build a reference schema +func RefSchema(refType string) *spec.Schema { + return spec.RefSchema("#/definitions/" + refType) +} + +//PrimitiveSchema build a primitive schema +func PrimitiveSchema(refType string) *spec.Schema { + return &spec.Schema{SchemaProps: spec.SchemaProps{Type: []string{refType}}} +}