Skip to content

Commit

Permalink
stable renaming to fix issue #745; uncapitalize tow members of parser (
Browse files Browse the repository at this point in the history
…#746)

* stable renaming to fix issue 745; uncapitalize tow members of parser

* optimize

* stable renaming to fix issue 745;

* const
  • Loading branch information
sdghchj committed Jul 12, 2020
1 parent cbfd47b commit ec7a5ee
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 25 deletions.
2 changes: 2 additions & 0 deletions go.sum
Expand Up @@ -77,6 +77,8 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
Expand Down
105 changes: 80 additions & 25 deletions parser.go
Expand Up @@ -10,6 +10,7 @@ import (
"go/token"
"io/ioutil"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -53,11 +54,20 @@ type Parser struct {
//packages store entities of APIs, definitions, file, package path etc. and their relations
packages *PackagesDefinitions

//ParsedSchemas store schemas which have been parsed from ast.TypeSpec
ParsedSchemas map[*TypeSpecDef]*Schema
//parsedSchemas store schemas which have been parsed from ast.TypeSpec
parsedSchemas map[*TypeSpecDef]*Schema

//OutputSchemas store schemas which will be export to swagger
OutputSchemas map[*TypeSpecDef]*Schema
//outputSchemas store schemas which will be export to swagger
outputSchemas map[*TypeSpecDef]*Schema

//existSchemaNames store names of models for conflict determination
existSchemaNames map[string]*Schema

//toBeRenamedSchemas names of models to be renamed
toBeRenamedSchemas map[string]string

//toBeRenamedSchemas URLs of ref models to be renamed
toBeRenamedRefURLs []*url.URL

PropNamingStrategy string

Expand Down Expand Up @@ -99,10 +109,12 @@ func New(options ...func(*Parser)) *Parser {
Definitions: make(map[string]spec.Schema),
},
},
packages: NewPackagesDefinitions(),
ParsedSchemas: make(map[*TypeSpecDef]*Schema),
OutputSchemas: make(map[*TypeSpecDef]*Schema),
excludes: make(map[string]bool),
packages: NewPackagesDefinitions(),
parsedSchemas: make(map[*TypeSpecDef]*Schema),
outputSchemas: make(map[*TypeSpecDef]*Schema),
existSchemaNames: make(map[string]*Schema),
toBeRenamedSchemas: make(map[string]string),
excludes: make(map[string]bool),
}

for _, option := range options {
Expand Down Expand Up @@ -172,7 +184,7 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
return err
}

parser.ParsedSchemas, err = parser.packages.ParseTypes()
parser.parsedSchemas, err = parser.packages.ParseTypes()
if err != nil {
return err
}
Expand All @@ -181,6 +193,8 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
return err
}

parser.renameRefSchemas()

return parser.checkOperationIDUniqueness()
}

Expand Down Expand Up @@ -561,7 +575,7 @@ func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (
return nil, fmt.Errorf("cannot find type definition: %s", typeName)
}

schema, ok := parser.ParsedSchemas[typeSpecDef]
schema, ok := parser.parsedSchemas[typeSpecDef]
if !ok {
var err error
schema, err = parser.ParseDefinition(typeSpecDef)
Expand All @@ -575,27 +589,68 @@ func (parser *Parser) getTypeSchema(typeName string, file *ast.File, ref bool) (
}
}

if ref && len(schema.Schema.Type) > 0 && schema.Schema.Type[0] == "object" {
if ref && len(schema.Schema.Type) > 0 && schema.Schema.Type[0] == OBJECT {
return parser.getRefTypeSchema(typeSpecDef, schema), nil
}
return schema.Schema, nil
}

func (parser *Parser) renameRefSchemas() {
if len(parser.toBeRenamedSchemas) == 0 {
return
}

//rename schemas in swagger.Definitions
for name, pkgPath := range parser.toBeRenamedSchemas {
if schema, ok := parser.swagger.Definitions[name]; ok {
delete(parser.swagger.Definitions, name)
name = parser.renameSchema(name, pkgPath)
parser.swagger.Definitions[name] = schema
}
}

//rename URLs if match
for _, url := range parser.toBeRenamedRefURLs {
parts := strings.Split(url.Fragment, "/")
name := parts[len(parts)-1]
if pkgPath, ok := parser.toBeRenamedSchemas[name]; ok {
parts[len(parts)-1] = parser.renameSchema(name, pkgPath)
url.Fragment = strings.Join(parts, "/")
}
}
}

func (parser *Parser) renameSchema(name, pkgPath string) string {
parts := strings.Split(name, ".")
name = fullTypeName(pkgPath, parts[len(parts)-1])
name = strings.ReplaceAll(name, "/", "_")
return name
}

func (parser *Parser) getRefTypeSchema(typeSpecDef *TypeSpecDef, schema *Schema) *spec.Schema {
if _, ok := parser.OutputSchemas[typeSpecDef]; !ok {
if _, ok := parser.swagger.Definitions[schema.Name]; ok {
schema.Name = fullTypeName(schema.PkgPath, strings.Split(schema.Name, ".")[1])
schema.Name = strings.ReplaceAll(schema.Name, "/", "_")
if _, ok := parser.outputSchemas[typeSpecDef]; !ok {
if existSchema, ok := parser.existSchemaNames[schema.Name]; ok {
//store the first one to be renamed after parsing over
if _, ok = parser.toBeRenamedSchemas[existSchema.Name]; !ok {
parser.toBeRenamedSchemas[existSchema.Name] = existSchema.PkgPath
}
//rename not the first one
schema.Name = parser.renameSchema(schema.Name, schema.PkgPath)
} else {
parser.existSchemaNames[schema.Name] = schema
}
if schema.Schema != nil {
parser.swagger.Definitions[schema.Name] = *schema.Schema
} else {
parser.swagger.Definitions[schema.Name] = spec.Schema{}
}
parser.OutputSchemas[typeSpecDef] = schema
parser.outputSchemas[typeSpecDef] = schema
}

return RefSchema(schema.Name)
refSchema := RefSchema(schema.Name)
//store every URL
parser.toBeRenamedRefURLs = append(parser.toBeRenamedRefURLs, refSchema.Ref.Ref.GetURL())
return refSchema
}

func (parser *Parser) isInStructStack(typeSpecDef *TypeSpecDef) bool {
Expand All @@ -614,7 +669,7 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error)
typeName := typeSpecDef.FullName()
refTypeName := TypeDocName(typeName, typeSpecDef.TypeSpec)

if schema, ok := parser.ParsedSchemas[typeSpecDef]; ok {
if schema, ok := parser.parsedSchemas[typeSpecDef]; ok {
Println("Skipping '" + typeName + "', already parsed.")
return schema, nil
}
Expand All @@ -636,10 +691,10 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef) (*Schema, error)
return nil, err
}
s := &Schema{Name: refTypeName, PkgPath: typeSpecDef.PkgPath, Schema: schema}
parser.ParsedSchemas[typeSpecDef] = s
parser.parsedSchemas[typeSpecDef] = s

//update an empty schema as a result of recursion
if s2, ok := parser.OutputSchemas[typeSpecDef]; ok {
if s2, ok := parser.outputSchemas[typeSpecDef]; ok {
parser.swagger.Definitions[s2.Name] = *schema
}

Expand Down Expand Up @@ -787,7 +842,7 @@ func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (map[st
if err != nil {
return nil, nil, err
}
if len(schema.Type) > 0 && schema.Type[0] == "object" {
if len(schema.Type) > 0 && schema.Type[0] == OBJECT {
if len(schema.Properties) == 0 {
return nil, nil, nil
}
Expand Down Expand Up @@ -1088,7 +1143,7 @@ func (parser *Parser) GetSchemaTypePath(schema *spec.Schema, depth int) []string
depth--
s := []string{schema.Type[0]}
return append(s, parser.GetSchemaTypePath(schema.Items.Schema, depth)...)
} else if schema.Type[0] == "object" {
} else if schema.Type[0] == OBJECT {
if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil {
// for map
depth--
Expand Down Expand Up @@ -1361,15 +1416,15 @@ func (parser *Parser) GetSwagger() *spec.Swagger {

//addTestType just for tests
func (parser *Parser) addTestType(typename string) {
if parser.ParsedSchemas == nil {
parser.ParsedSchemas = make(map[*TypeSpecDef]*Schema)
if parser.parsedSchemas == nil {
parser.parsedSchemas = make(map[*TypeSpecDef]*Schema)
}
if parser.packages.uniqueDefinitions == nil {
parser.packages.uniqueDefinitions = make(map[string]*TypeSpecDef)
}
typeDef := &TypeSpecDef{}
parser.packages.uniqueDefinitions[typename] = typeDef
parser.ParsedSchemas[typeDef] = &Schema{
parser.parsedSchemas[typeDef] = &Schema{
PkgPath: "",
Name: typename,
Schema: PrimitiveSchema(OBJECT),
Expand Down
13 changes: 13 additions & 0 deletions parser_test.go
Expand Up @@ -1549,6 +1549,19 @@ func TestParseDuplicated(t *testing.T) {
assert.Errorf(t, err, "duplicated @id declarations successfully found")
}

func TestParseConflictSchemaName(t *testing.T) {
searchDir := "testdata/conflict_name"
mainAPIFile := "main.go"
p := New()
p.ParseDependency = true
err := p.ParseAPI(searchDir, mainAPIFile)
assert.NoError(t, err)
b, _ := json.MarshalIndent(p.swagger, "", " ")
expected, err := ioutil.ReadFile(filepath.Join(searchDir, "expected.json"))
assert.NoError(t, err)
assert.Equal(t, string(expected), string(b))
}

func TestParser_ParseStructArrayObject(t *testing.T) {
src := `
package api
Expand Down
17 changes: 17 additions & 0 deletions testdata/conflict_name/api/api1.go
@@ -0,0 +1,17 @@
package api

import (
_ "github.com/swaggo/swag/testdata/conflict_name/model"
"net/http"
)

// @Tags Health
// @Description Check if Health of service it's OK!
// @ID health
// @Accept json
// @Produce json
// @Success 200 {object} model.ErrorsResponse
// @Router /health [get]
func Get1(w http.ResponseWriter, r *http.Request) {

}
17 changes: 17 additions & 0 deletions testdata/conflict_name/api/api2.go
@@ -0,0 +1,17 @@
package api

import (
_ "github.com/swaggo/swag/testdata/conflict_name/model2"
"net/http"
)

// @Tags Health
// @Description Check if Health of service it's OK!
// @ID health2
// @Accept json
// @Produce json
// @Success 200 {object} model.ErrorsResponse
// @Router /health2 [get]
func Get2(w http.ResponseWriter, r *http.Request) {

}
114 changes: 114 additions & 0 deletions testdata/conflict_name/expected.json
@@ -0,0 +1,114 @@
{
"swagger": "2.0",
"info": {
"description": "test for conflict name",
"title": "Swag test",
"contact": {},
"license": {},
"version": "1.0"
},
"paths": {
"/health": {
"get": {
"description": "Check if Health of service it's OK!",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Health"
],
"operationId": "health",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github.com_swaggo_swag_testdata_conflict_name_model.ErrorsResponse"
}
}
}
}
},
"/health2": {
"get": {
"description": "Check if Health of service it's OK!",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Health"
],
"operationId": "health2",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/github.com_swaggo_swag_testdata_conflict_name_model2.ErrorsResponse"
}
}
}
}
}
},
"definitions": {
"github.com_swaggo_swag_testdata_conflict_name_model.ErrorsResponse": {
"type": "object",
"properties": {
"newTime": {
"$ref": "#/definitions/model.MyPayload"
}
}
},
"github.com_swaggo_swag_testdata_conflict_name_model.MyStruct": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"github.com_swaggo_swag_testdata_conflict_name_model2.ErrorsResponse": {
"type": "object",
"properties": {
"newTime": {
"$ref": "#/definitions/model.MyPayload2"
}
}
},
"github.com_swaggo_swag_testdata_conflict_name_model2.MyStruct": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"model.MyPayload": {
"type": "object",
"properties": {
"my": {
"$ref": "#/definitions/github.com_swaggo_swag_testdata_conflict_name_model.MyStruct"
},
"name": {
"type": "string"
}
}
},
"model.MyPayload2": {
"type": "object",
"properties": {
"my": {
"$ref": "#/definitions/github.com_swaggo_swag_testdata_conflict_name_model2.MyStruct"
},
"name": {
"type": "string"
}
}
}
}
}

0 comments on commit ec7a5ee

Please sign in to comment.