diff --git a/example/simple/api/api.go b/example/simple/api/api.go index 83b58c19d..7d4a9348f 100644 --- a/example/simple/api/api.go +++ b/example/simple/api/api.go @@ -48,6 +48,12 @@ func Upload(ctx *gin.Context) { //write your code } +// @Summary use Anonymous field +// @Success 200 {object} web.RevValue "ok" +func AnonymousField() { + +} + type Pet3 struct { ID int `json:"id"` } diff --git a/example/simple/docs/docs.go b/example/simple/docs/docs.go index afa47d388..51b264137 100644 --- a/example/simple/docs/docs.go +++ b/example/simple/docs/docs.go @@ -1,6 +1,6 @@ // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT // This file was generated by swaggo/swag at -// 2017-11-09 17:06:49.294949095 -0800 PST m=+0.071072332 +// 2018-02-04 17:07:16.008489 +0800 CST m=+0.080555450 package docs @@ -28,6 +28,50 @@ var doc = `{ "host": "petstore.swagger.io", "basePath": "/v2", "paths": { + "/file/upload": { + "post": { + "description": "Upload file", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "summary": "Upload file", + "operationId": "file.upload", + "parameters": [ + { + "type": "file", + "description": "this is a test file", + "name": "file", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "ok", + "schema": { + "type": "string" + } + }, + "400": { + "description": "We need ID!!", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + }, + "404": { + "description": "Can not find ID", + "schema": { + "type": "object", + "$ref": "#/definitions/web.APIError" + } + } + } + } + }, "/testapi/get-string-by-int/{some_id}": { "get": { "description": "get string by ID", @@ -148,7 +192,7 @@ var doc = `{ "type": "string" }, "ErrorCode": { - "type": "int" + "type": "integer" }, "ErrorMessage": { "type": "string" @@ -162,7 +206,7 @@ var doc = `{ "type": "object" }, "ID": { - "type": "int" + "type": "integer" }, "Name": { "type": "string" @@ -177,6 +221,20 @@ var doc = `{ "type": "array" } } + }, + "web.RevValue": { + "type": "object", + "properties": { + "Data": { + "type": "integer" + }, + "Err": { + "type": "int32" + }, + "Status": { + "type": "bool" + } + } } } }` diff --git a/example/simple/web/handler.go b/example/simple/web/handler.go index 503f5ab4e..8b37d58c4 100644 --- a/example/simple/web/handler.go +++ b/example/simple/web/handler.go @@ -1,6 +1,8 @@ package web -import "time" +import ( + "time" +) type Pet struct { ID int `json:"id"` @@ -26,3 +28,14 @@ type APIError struct { ErrorMessage string CreatedAt time.Time } + +type RevValueBase struct { + Status bool `json:"Status"` + + Err int32 `json:"Err"` +} +type RevValue struct { + RevValueBase + + Data int `json:"Data"` +} diff --git a/operation_test.go b/operation_test.go index 5f9f8f484..2717c1ada 100644 --- a/operation_test.go +++ b/operation_test.go @@ -131,6 +131,10 @@ func TestParseResponseCommentWithObjectType(t *testing.T) { assert.Equal(t, expected, string(b)) } +func TestParseResponseCommentWithObjectTypeAnonymousField(t *testing.T) { + //TODO: test Anonymous +} + func TestParseResponseCommentWithObjectTypeErr(t *testing.T) { comment := `@Success 200 {object} model.OrderRow "Error message, if code != 200"` operation := NewOperation() diff --git a/parser.go b/parser.go index e23f4f36e..d3cbf455b 100644 --- a/parser.go +++ b/parser.go @@ -187,26 +187,10 @@ func (parser *Parser) ParseDefinitions() { var properties map[string]spec.Schema properties = make(map[string]spec.Schema) - switch typeSpec.Type.(type) { - case *ast.StructType: - structDecl := typeSpec.Type.(*ast.StructType) - fields := structDecl.Fields.List - - for _, field := range fields { - name := field.Names[0].Name - propName := getPropertyName(field) - properties[name] = spec.Schema{ - SchemaProps: spec.SchemaProps{Type: []string{propName}}, - } - } + ss := strings.Split(refTypeName, ".") + pkgName := ss[0] - case *ast.ArrayType: - log.Panic("ParseDefinitions not supported 'Array' yet.") - case *ast.InterfaceType: - log.Panic("ParseDefinitions not supported 'Interface' yet.") - case *ast.MapType: - log.Panic("ParseDefinitions not supported 'Map' yet.") - } + parser.parseTypeSpec(pkgName, typeSpec, properties) parser.swagger.Definitions[refTypeName] = spec.Schema{ SchemaProps: spec.SchemaProps{ @@ -218,15 +202,63 @@ func (parser *Parser) ParseDefinitions() { } } +func (parser *Parser) parseTypeSpec(pkgName string, typeSpec *ast.TypeSpec, properties map[string]spec.Schema) { + switch typeSpec.Type.(type) { + case *ast.StructType: + structDecl := typeSpec.Type.(*ast.StructType) + fields := structDecl.Fields.List + + for _, field := range fields { + if field.Names == nil { //anonymous field + parser.parseAnonymousField(pkgName, field, properties) + } else { + name, propName := parser.parseField(field) + properties[name] = spec.Schema{ + SchemaProps: spec.SchemaProps{Type: []string{propName}}, + } + } + } + + case *ast.ArrayType: + log.Panic("ParseDefinitions not supported 'Array' yet.") + case *ast.InterfaceType: + log.Panic("ParseDefinitions not supported 'Interface' yet.") + case *ast.MapType: + log.Panic("ParseDefinitions not supported 'Map' yet.") + } +} + +func (parser *Parser) parseAnonymousField(pkgName string, field *ast.Field, properties map[string]spec.Schema) { + if astTypeIdent, ok := field.Type.(*ast.Ident); ok { + findPgkName := pkgName + findBaseTypeName := astTypeIdent.Name + ss := strings.Split(astTypeIdent.Name, ".") + if len(ss) > 1 { + findPgkName = ss[0] + findBaseTypeName = ss[1] + } + + baseTypeSpec := parser.TypeDefinitions[findPgkName][findBaseTypeName] + parser.parseTypeSpec(findPgkName, baseTypeSpec, properties) + } +} + +func (parser *Parser) parseField(field *ast.Field) (propName, schemaType string) { + return field.Names[0].Name, getPropertyName(field) +} + // GetAllGoFileInfo gets all Go source files information for gived searchDir. func (parser *Parser) getAllGoFileInfo(searchDir string) { filepath.Walk(searchDir, func(path string, f os.FileInfo, err error) error { //exclude vendor folder if ext := filepath.Ext(path); ext == ".go" && !strings.Contains(string(os.PathSeparator)+path, string(os.PathSeparator)+"vendor"+string(os.PathSeparator)) { - astFile, err := goparser.ParseFile(token.NewFileSet(), path, nil, goparser.ParseComments) + fset := token.NewFileSet() // positions are relative to fset + astFile, err := goparser.ParseFile(fset, path, nil, goparser.ParseComments) + if err != nil { log.Panicf("ParseFile panic:%+v", err) } + parser.files[path] = astFile } diff --git a/parser_test.go b/parser_test.go index 61722b6ea..2fc54ee31 100644 --- a/parser_test.go +++ b/parser_test.go @@ -299,6 +299,20 @@ func TestParseSimpleApi(t *testing.T) { "type": "array" } } + }, + "web.RevValue": { + "type": "object", + "properties": { + "Data": { + "type": "integer" + }, + "Err": { + "type": "int32" + }, + "Status": { + "type": "bool" + } + } } } }`