Merge pull request #30 from fachebot/main

添加options、range和omitempty选项的支持
This commit is contained in:
MaxToby 2022-04-18 23:10:50 +08:00 committed by GitHub
commit a7775c3e28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 145 additions and 73 deletions

112
README.md
View File

@ -3,93 +3,103 @@
### 1. 编译goctl-swagger插件 ### 1. 编译goctl-swagger插件
``` ```
$ GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/zeromicro/goctl-swagger GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/fachebot/goctl-swagger
``` ```
### 2. 配置环境 ### 2. 配置环境
将$GOPATH/bin中的goctl-swagger添加到环境变量 将$GOPATH/bin中的goctl-swagger添加到环境变量
### 3. 使用姿势 ### 3. 使用姿势
* 创建api文件 * 创建api文件
```go ```go
info( info(
title: "type title here" title: "type title here"
desc: "type desc here" desc: "type desc here"
author: "type author here" author: "type author here"
email: "type email here" email: "type email here"
version: "type version here" version: "type version here"
) )
type ( type (
RegisterReq { RegisterReq {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Mobile string `json:"mobile"` Mobile string `json:"mobile"`
} }
LoginReq { LoginReq {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
} }
UserInfoReq { UserInfoReq {
Id string `path:"id"` Id string `path:"id"`
} }
UserInfoReply { UserInfoReply {
Name string `json:"name"` Name string `json:"name"`
Age int `json:"age"` Age int `json:"age"`
Birthday string `json:"birthday"` Birthday string `json:"birthday"`
Description string `json:"description"` Description string `json:"description"`
Tag []string `json:"tag"` Tag []string `json:"tag"`
} }
UserSearchReq { UserSearchReq {
KeyWord string `form:"keyWord"` KeyWord string `form:"keyWord"`
} }
) )
service user-api { service user-api {
@doc( @doc(
summary: "注册" summary: "注册"
) )
@handler register @handler register
post /api/user/register (RegisterReq) post /api/user/register (RegisterReq)
@doc( @doc(
summary: "登录" summary: "登录"
) )
@handler login @handler login
post /api/user/login (LoginReq) post /api/user/login (LoginReq)
@doc( @doc(
summary: "获取用户信息" summary: "获取用户信息"
) )
@handler getUserInfo @handler getUserInfo
get /api/user/:id (UserInfoReq) returns (UserInfoReply) get /api/user/:id (UserInfoReq) returns (UserInfoReply)
@doc( @doc(
summary: "用户搜索" summary: "用户搜索"
) )
@handler searchUser @handler searchUser
get /api/user/search (UserSearchReq) returns (UserInfoReply) get /api/user/search (UserSearchReq) returns (UserInfoReply)
} }
``` ```
* 生成swagger.json 文件 * 生成swagger.json 文件
```shell script ```shell script
$ goctl api plugin -plugin goctl-swagger="swagger -filename user.json" -api user.api -dir . goctl api plugin -plugin goctl-swagger="swagger -filename user.json" -api user.api -dir .
``` ```
* 指定HostbasePath [api-host-and-base-path](https://swagger.io/docs/specification/2-0/api-host-and-base-path/) * 指定HostbasePath [api-host-and-base-path](https://swagger.io/docs/specification/2-0/api-host-and-base-path/)
```shell script ```shell script
$ goctl api plugin -plugin goctl-swagger="swagger -filename user.json -host 127.0.0.2 -basepath /api" -api user.api -dir . goctl api plugin -plugin goctl-swagger="swagger -filename user.json -host 127.0.0.2 -basepath /api" -api user.api -dir .
``` ```
* swagger ui 查看生成的文档 * swagger ui 查看生成的文档
```shell script ```shell script
$ docker run --rm -p 8083:8080 -e SWAGGER_JSON=/foo/user.json -v $PWD:/foo swaggerapi/swagger-ui docker run --rm -p 8083:8080 -e SWAGGER_JSON=/foo/user.json -v $PWD:/foo swaggerapi/swagger-ui
``` ```
* Swagger Codegen 生成客户端调用代码(go,javascript,php) * Swagger Codegen 生成客户端调用代码(go,javascript,php)
```shell script ```shell script
for l in go javascript php; do for l in go javascript php; do
docker run --rm -v "$(pwd):/go-work" swaggerapi/swagger-codegen-cli generate \ docker run --rm -v "$(pwd):/go-work" swaggerapi/swagger-codegen-cli generate \

View File

@ -1,9 +1,9 @@
package action package action
import ( import (
"github.com/fachebot/goctl-swagger/generate"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/zeromicro/go-zero/tools/goctl/plugin" "github.com/zeromicro/go-zero/tools/goctl/plugin"
"github.com/zeromicro/goctl-swagger/generate"
) )
func Generator(ctx *cli.Context) error { func Generator(ctx *cli.Context) error {

View File

@ -124,7 +124,7 @@ type swaggerOperationObject struct {
OperationID string `json:"operationId"` OperationID string `json:"operationId"`
Responses swaggerResponsesObject `json:"responses"` Responses swaggerResponsesObject `json:"responses"`
Parameters swaggerParametersObject `json:"parameters,omitempty"` Parameters swaggerParametersObject `json:"parameters,omitempty"`
RequestBody struct { RequestBody *struct {
Content swaggerContentObject `json:"content,omitempty"` Content swaggerContentObject `json:"content,omitempty"`
} `json:"requestBody,omitempty"` } `json:"requestBody,omitempty"`
Tags []string `json:"tags,omitempty"` Tags []string `json:"tags,omitempty"`

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"reflect" "reflect"
"regexp"
"strconv" "strconv"
"strings" "strings"
"unsafe" "unsafe"
@ -19,12 +20,36 @@ const (
defaultOption = "default" defaultOption = "default"
stringOption = "string" stringOption = "string"
optionalOption = "optional" optionalOption = "optional"
omitemptyOption = "omitempty"
optionsOption = "options" optionsOption = "options"
rangeOption = "range" rangeOption = "range"
optionSeparator = "|" optionSeparator = "|"
equalToken = "=" equalToken = "="
) )
func parseRangeOption(option string) (float64, float64, bool) {
const str = "\\[([+-]?\\d+(\\.\\d+)?):([+-]?\\d+(\\.\\d+)?)\\]"
result := regexp.MustCompile(str).FindStringSubmatch(option)
if len(result) != 5 {
return 0, 0, false
}
min, err := strconv.ParseFloat(result[1], 64)
if err != nil {
return 0, 0, false
}
max, err := strconv.ParseFloat(result[3], 64)
if err != nil {
return 0, 0, false
}
if max < min {
return min, min, true
}
return min, max, true
}
func applyGenerate(p *plugin.Plugin, host string, basePath string) (*swaggerObject, error) { func applyGenerate(p *plugin.Plugin, host string, basePath string) (*swaggerObject, error) {
title, _ := strconv.Unquote(p.Api.Info.Properties["title"]) title, _ := strconv.Unquote(p.Api.Info.Properties["title"])
version, _ := strconv.Unquote(p.Api.Info.Properties["version"]) version, _ := strconv.Unquote(p.Api.Info.Properties["version"])
@ -136,16 +161,37 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge
sp.Required = true sp.Required = true
continue continue
} }
required := true
for _, option := range tag.Options { for _, option := range tag.Options {
if strings.HasPrefix(option, optionsOption) {
segs := strings.SplitN(option, equalToken, 2)
if len(segs) == 2 {
sp.Enum = strings.Split(segs[1], optionSeparator)
}
}
if strings.HasPrefix(option, rangeOption) {
segs := strings.SplitN(option, equalToken, 2)
if len(segs) == 2 {
min, max, ok := parseRangeOption(segs[1])
if ok {
sp.Schema.Minimum = min
sp.Schema.Maximum = max
}
}
}
if strings.HasPrefix(option, defaultOption) { if strings.HasPrefix(option, defaultOption) {
segs := strings.Split(option, equalToken) segs := strings.Split(option, equalToken)
if len(segs) == 2 { if len(segs) == 2 {
sp.Default = segs[1] sp.Default = segs[1]
} }
} else if !strings.HasPrefix(option, optionalOption) { } else if strings.HasPrefix(option, optionalOption) || strings.HasPrefix(option, omitemptyOption) {
sp.Required = true required = false
} }
} }
sp.Required = required
} }
if len(member.Comment) > 0 { if len(member.Comment) > 0 {
@ -288,16 +334,20 @@ func renderReplyAsDefinition(d swaggerDefinitionsObject, m messageMap, p []spec.
} }
continue continue
} }
required := true
for _, option := range tag.Options { for _, option := range tag.Options {
switch { // case strings.HasPrefix(option, defaultOption):
case !strings.HasPrefix(option, optionalOption): // case strings.HasPrefix(option, optionsOption):
if !contains(schema.Required, tag.Name) {
schema.Required = append(schema.Required, tag.Name) if strings.HasPrefix(option, optionalOption) || strings.HasPrefix(option, omitemptyOption) {
} required = false
// case strings.HasPrefix(option, defaultOption):
// case strings.HasPrefix(option, optionsOption):
} }
} }
if required && !contains(schema.Required, tag.Name) {
schema.Required = append(schema.Required, tag.Name)
}
} }
} }
@ -409,7 +459,19 @@ func schemaOfField(member spec.Member) swaggerSchemaObject {
ret.Default = segs[1] ret.Default = segs[1]
} }
case strings.HasPrefix(option, optionsOption): case strings.HasPrefix(option, optionsOption):
segs := strings.SplitN(option, equalToken, 2)
if len(segs) == 2 {
ret.Enum = strings.Split(segs[1], optionSeparator)
}
case strings.HasPrefix(option, rangeOption):
segs := strings.SplitN(option, equalToken, 2)
if len(segs) == 2 {
min, max, ok := parseRangeOption(segs[1])
if ok {
ret.Minimum = min
ret.Maximum = max
}
}
} }
} }
} }

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/zeromicro/goctl-swagger module github.com/fachebot/goctl-swagger
go 1.16 go 1.16

View File

@ -5,8 +5,8 @@ import (
"os" "os"
"runtime" "runtime"
"github.com/fachebot/goctl-swagger/action"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/zeromicro/goctl-swagger/action"
) )
var ( var (