Skip to content

Commit

Permalink
adds support for complex types with function scope (#1813)
Browse files Browse the repository at this point in the history
  • Loading branch information
KristofferFJ authored Jun 28, 2024
1 parent 7204462 commit f32d4d3
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 2 deletions.
22 changes: 20 additions & 2 deletions packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as
for _, astDeclaration := range astFile.Decls {
funcDeclaration, ok := astDeclaration.(*ast.FuncDecl)
if ok && funcDeclaration.Body != nil {
functionScopedTypes := make(map[string]*TypeSpecDef)
for _, stmt := range funcDeclaration.Body.List {
if declStmt, ok := (stmt).(*ast.DeclStmt); ok {
if genDecl, ok := (declStmt.Decl).(*ast.GenDecl); ok && genDecl.Tok == token.TYPE {
Expand All @@ -212,12 +213,28 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as
}
}

fullName := typeSpecDef.TypeName()
if structType, ok := typeSpecDef.TypeSpec.Type.(*ast.StructType); ok {
for _, field := range structType.Fields.List {
if idt, ok := field.Type.(*ast.Ident); ok && !IsGolangPrimitiveType(idt.Name) {
if functype, ok := functionScopedTypes[idt.Name]; ok {
idt.Name = functype.TypeName()
}
}
if art, ok := field.Type.(*ast.ArrayType); ok {
if idt, ok := art.Elt.(*ast.Ident); ok && !IsGolangPrimitiveType(idt.Name) {
if functype, ok := functionScopedTypes[idt.Name]; ok {
idt.Name = functype.TypeName()
}
}
}
}
}

if pkgDefs.uniqueDefinitions == nil {
pkgDefs.uniqueDefinitions = make(map[string]*TypeSpecDef)
}

fullName := typeSpecDef.TypeName()

anotherTypeDef, ok := pkgDefs.uniqueDefinitions[fullName]
if ok {
if anotherTypeDef == nil {
Expand All @@ -234,6 +251,7 @@ func (pkgDefs *PackagesDefinitions) parseFunctionScopedTypesFromFile(astFile *as
}
} else {
pkgDefs.uniqueDefinitions[fullName] = typeSpecDef
functionScopedTypes[typeSpec.Name.Name] = typeSpecDef
}

if pkgDefs.packages[typeSpecDef.PkgPath] == nil {
Expand Down
167 changes: 167 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3389,6 +3389,49 @@ func Fun() {
assert.True(t, ok)
}

func TestParseFunctionScopedComplexStructDefinition(t *testing.T) {
t.Parallel()

src := `
package main
// @Param request body main.Fun.request true "query params"
// @Success 200 {object} main.Fun.response
// @Router /test [post]
func Fun() {
type request struct {
Name string
}
type grandChild struct {
Name string
}
type child struct {
GrandChild grandChild
}
type response struct {
Children []child
}
}
`
p := New()
_ = p.packages.ParseFile("api", "api/api.go", src, ParseAll)
_, err := p.packages.ParseTypes()
assert.NoError(t, err)

err = p.packages.RangeFiles(p.ParseRouterAPIInfo)
assert.NoError(t, err)

_, ok := p.swagger.Definitions["main.Fun.response"]
assert.True(t, ok)
_, ok = p.swagger.Definitions["main.Fun.child"]
assert.True(t, ok)
_, ok = p.swagger.Definitions["main.Fun.grandChild"]
assert.True(t, ok)
}

func TestParseFunctionScopedStructRequestResponseJSON(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -3474,6 +3517,130 @@ func Fun() {
assert.Equal(t, expected, string(b))
}

func TestParseFunctionScopedComplexStructRequestResponseJSON(t *testing.T) {
t.Parallel()

src := `
package main
type PublicChild struct {
Name string
}
// @Param request body main.Fun.request true "query params"
// @Success 200 {object} main.Fun.response
// @Router /test [post]
func Fun() {
type request struct {
Name string
}
type grandChild struct {
Name string
}
type child struct {
GrandChild grandChild
}
type response struct {
Children []child
PublicChild PublicChild
}
}
`
expected := `{
"info": {
"contact": {}
},
"paths": {
"/test": {
"post": {
"parameters": [
{
"description": "query params",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/main.Fun.request"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/main.Fun.response"
}
}
}
}
}
},
"definitions": {
"main.Fun.child": {
"type": "object",
"properties": {
"grandChild": {
"$ref": "#/definitions/main.Fun.grandChild"
}
}
},
"main.Fun.grandChild": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"main.Fun.request": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"main.Fun.response": {
"type": "object",
"properties": {
"children": {
"type": "array",
"items": {
"$ref": "#/definitions/main.Fun.child"
}
},
"publicChild": {
"$ref": "#/definitions/main.PublicChild"
}
}
},
"main.PublicChild": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
}
}
}`

p := New()
_ = p.packages.ParseFile("api", "api/api.go", src, ParseAll)

_, err := p.packages.ParseTypes()
assert.NoError(t, err)

err = p.packages.RangeFiles(p.ParseRouterAPIInfo)
assert.NoError(t, err)

b, _ := json.MarshalIndent(p.swagger, "", " ")
assert.Equal(t, expected, string(b))
}

func TestPackagesDefinitions_CollectAstFileInit(t *testing.T) {
t.Parallel()

Expand Down
12 changes: 12 additions & 0 deletions testdata/simple/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,15 @@ func GetPet6FunctionScopedResponse() {
Name string
}
}

// @Success 200 {object} api.GetPet6FunctionScopedComplexResponse.response "ok"
// @Router /GetPet6FunctionScopedComplexResponse [get]
func GetPet6FunctionScopedComplexResponse() {
type child struct {
Name string
}

type response struct {
Child child
}
}
31 changes: 31 additions & 0 deletions testdata/simple/expected.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@
}
}
},
"/GetPet6FunctionScopedComplexResponse": {
"get": {
"responses": {
"200": {
"description": "ok",
"schema": {
"$ref": "#/definitions/api.GetPet6FunctionScopedComplexResponse.response"
}
}
}
}
},
"/GetPet6MapString": {
"get": {
"responses": {
Expand Down Expand Up @@ -401,6 +413,25 @@
}
},
"definitions": {
"api.GetPet6FunctionScopedComplexResponse.pet": {
"type": "object",
"properties": {
"name": {
"type": "string"
}
}
},
"api.GetPet6FunctionScopedComplexResponse.response": {
"type": "object",
"properties": {
"Pets": {
"type": "array",
"items": {
"$ref": "#/definitions/api.GetPet6FunctionScopedComplexResponse.pet"
}
}
}
},
"api.GetPet6FunctionScopedResponse.response": {
"type": "object",
"properties": {
Expand Down

0 comments on commit f32d4d3

Please sign in to comment.