diff --git a/action/action.go b/action/action.go index 627aff8..a439f85 100644 --- a/action/action.go +++ b/action/action.go @@ -10,6 +10,7 @@ import ( func Generator(ctx *cli.Context) error { pkg := ctx.String("package") + fileName := ctx.String("filename") std, err := ioutil.ReadAll(os.Stdin) if err != nil { return err @@ -17,7 +18,11 @@ func Generator(ctx *cli.Context) error { var plugin generate.Plugin plugin.ParentPackage = pkg + plugin.FileName = fileName + if len(plugin.FileName) == 0 { + plugin.FileName = "rest.swagger.json" + } err = json.Unmarshal(std, &plugin) if err != nil { return err diff --git a/example/generate.go b/example/generate.go index be25825..ca5ac61 100644 --- a/example/generate.go +++ b/example/generate.go @@ -1,3 +1,3 @@ package example -//go:generate goctl api plugin -plugin goctl-swagger="swagger" -api user.api -dir . +//go:generate goctl api plugin -plugin goctl-swagger="swagger -filename user.json" -api user.api -dir . diff --git a/example/user.api b/example/user.api index 1eb14ee..42301b5 100644 --- a/example/user.api +++ b/example/user.api @@ -14,13 +14,13 @@ type ( } // 登录请求 LoginReq { - // 用户名 Username string `json:"username" desc:"用户名"` Password string `json:"password"` //密码 } // 登录请求 UserInfoReq { - Id string `path:"id"` + Id string `path:"id"` // 会员ID + Page string `form:"page,default=1"` //分页 } UserInfoReply { @@ -36,7 +36,8 @@ type ( } UserSearchReply { - KeyWord []UserInfoReply + KeyWord []*UserInfoReply + KeyWords []*UserSearchReq } ) diff --git a/generate/entities.go b/generate/entities.go index 6915e07..5a9464b 100644 --- a/generate/entities.go +++ b/generate/entities.go @@ -14,6 +14,7 @@ type ( Style string Dir string ParentPackage string + FileName string } ) @@ -24,6 +25,9 @@ var ( "int32": reflect.Int, "int64": reflect.Int64, "[]string": reflect.Slice, + "[]int": reflect.Slice, + "[]int64": reflect.Slice, + "[]int32": reflect.Slice, "bool": reflect.Bool, "struct": reflect.Struct, } @@ -153,7 +157,6 @@ type schemaCore struct { Example json.RawMessage `json:"example,omitempty"` Items *swaggerItemsObject `json:"items,omitempty"` - // If the item is an enumeration include a list of all the *NAMES* of the // enum values. I'm not sure how well this will work but assuming all enums // start from 0 index it will be great. I don't think that is a good assumption. diff --git a/generate/generate.go b/generate/generate.go index 6a03c03..b0d8a20 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -21,7 +21,7 @@ func Do(in Plugin) error { } - output := in.Dir + "/rest.swagger.json" + output := in.Dir + "/" + in.FileName err = ioutil.WriteFile(output, formatted.Bytes(), 0666) diff --git a/generate/parser.go b/generate/parser.go index fc7c5c2..80631ce 100644 --- a/generate/parser.go +++ b/generate/parser.go @@ -15,6 +15,16 @@ var ( strColon = []byte(":") ) +const ( + defaultOption = "default" + stringOption = "string" + optionalOption = "optional" + optionsOption = "options" + rangeOption = "range" + optionSeparator = "|" + equalToken = "=" +) + func applyGenerate(p Plugin) (*swaggerObject, error) { s := swaggerObject{ @@ -43,6 +53,7 @@ func applyGenerate(p Plugin) (*swaggerObject, error) { renderServiceRoutes(p.Api.Service, p.Api.Service.Groups, s.Paths, requestResponseRefs) m := messageMap{} renderReplyAsDefinition(s.Definitions, m, p.Api.Types, requestResponseRefs) + return &s, nil } @@ -70,19 +81,54 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge } } - reqRef := fmt.Sprintf("#/definitions/%s", route.RequestType.Name) - if len(route.RequestType.Name) > 0 { - var schema = swaggerSchemaObject{ - schemaCore: schemaCore{ - Ref: reqRef, - }, + if strings.ToUpper(route.Method) == http.MethodGet { + for _, member := range route.RequestType.Members { + if strings.Contains(member.Tag, "path") { + continue + } + tempKind := swaggerMapTypes[strings.Replace(member.Type, "[]", "", -1)] + ftype, format, ok := primitiveSchema(tempKind, member.Type) + if !ok { + ftype = tempKind.String() + format = "UNKNOWN" + } + sp := swaggerParameterObject{In: "query", Type: ftype, Format: format} + + for _, tag := range member.Tags() { + sp.Name = tag.Name + if len(tag.Options) == 0 { + sp.Required = true + continue + } + for _, option := range tag.Options { + if strings.HasPrefix(option, defaultOption) { + segs := strings.Split(option, equalToken) + if len(segs) == 2 { + sp.Default = segs[1] + } + } else if !strings.HasPrefix(option, optionalOption) { + sp.Required = true + } + } + } + parameters = append(parameters, sp) + } + + } else { + reqRef := fmt.Sprintf("#/definitions/%s", route.RequestType.Name) + if len(route.RequestType.Name) > 0 { + var schema = swaggerSchemaObject{ + schemaCore: schemaCore{ + Ref: reqRef, + }, + } + parameters = append(parameters, swaggerParameterObject{ + Name: "body", + In: "body", + Required: true, + Schema: &schema, + }) } - parameters = append(parameters, swaggerParameterObject{ - Name: "body", - In: "body", - Required: true, - Schema: &schema, - }) } pathItemObject, ok := paths[path] @@ -172,7 +218,25 @@ func renderReplyAsDefinition(d swaggerDefinitionsObject, m messageMap, p []spec. schema.Properties = &swaggerSchemaObjectProperties{} } *schema.Properties = append(*schema.Properties, kv) + + for _, tag := range member.Tags() { + if len(tag.Options) == 0 { + schema.Required = append(schema.Required, tag.Name) + continue + } + for _, option := range tag.Options { + switch { + case !strings.HasPrefix(option, optionalOption): + if !contains(schema.Required, tag.Name) { + schema.Required = append(schema.Required, tag.Name) + } + case strings.HasPrefix(option, defaultOption): + case strings.HasPrefix(option, optionsOption): + } + } + } } + d[i2.Name] = schema } @@ -182,18 +246,32 @@ func schemaOfField(member spec.Member) swaggerSchemaObject { ret := swaggerSchemaObject{} var core schemaCore - + //spew.Dump(member) kind := swaggerMapTypes[member.Type] var props *swaggerSchemaObjectProperties + comment := member.GetComment() + comment = strings.Replace(comment, "//", "", -1) + switch ft := kind; ft { case reflect.Invalid: //[]Struct 也有可能是 Struct // []Struct + //map[ArrayType:map[Star:map[StringExpr:UserSearchReq] StringExpr:*UserSearchReq] StringExpr:[]*UserSearchReq] refTypeName := strings.Replace(member.Type, "[", "", 1) refTypeName = strings.Replace(refTypeName, "]", "", 1) + refTypeName = strings.Replace(refTypeName, "*", "", 1) core = schemaCore{ Ref: "#/definitions/" + refTypeName, } + case reflect.Slice: + tempKind := swaggerMapTypes[strings.Replace(member.Type, "[]", "", -1)] + ftype, format, ok := primitiveSchema(tempKind, member.Type) + + if ok { + core = schemaCore{Type: ftype, Format: format} + } else { + core = schemaCore{Type: ft.String(), Format: "UNKNOWN"} + } default: ftype, format, ok := primitiveSchema(ft, member.Type) if ok { @@ -221,7 +299,6 @@ func schemaOfField(member spec.Member) swaggerSchemaObject { }, } } else { - ret = swaggerSchemaObject{ schemaCore: core, Properties: props, @@ -233,6 +310,24 @@ func schemaOfField(member spec.Member) swaggerSchemaObject { Properties: props, } } + ret.Description = comment + + for _, tag := range member.Tags() { + if len(tag.Options) == 0 { + continue + } + for _, option := range tag.Options { + switch { + case strings.HasPrefix(option, defaultOption): + segs := strings.Split(option, equalToken) + if len(segs) == 2 { + ret.Default = segs[1] + } + case strings.HasPrefix(option, optionsOption): + + } + } + } return ret } @@ -271,3 +366,12 @@ func countParams(path string) uint16 { n += uint16(bytes.Count(s, strColon)) return n } +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} diff --git a/go.sum b/go.sum index 393f9de..8ed580a 100644 --- a/go.sum +++ b/go.sum @@ -250,7 +250,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= diff --git a/main.go b/main.go index 7b1604b..7a5f7d6 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,12 @@ var ( Name: "swagger", Usage: "generates swagger.json", Action: action.Generator, - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "filename", + Usage: "swagger save file name", + }, + }, }, } )