Merge pull request #30 from fachebot/main
添加options、range和omitempty选项的支持
This commit is contained in:
commit
a7775c3e28
112
README.md
112
README.md
@ -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 .
|
||||||
```
|
```
|
||||||
|
|
||||||
* 指定Host,basePath [api-host-and-base-path](https://swagger.io/docs/specification/2-0/api-host-and-base-path/)
|
* 指定Host,basePath [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 \
|
||||||
|
@ -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 {
|
||||||
|
@ -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"`
|
||||||
|
@ -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
2
go.mod
@ -1,4 +1,4 @@
|
|||||||
module github.com/zeromicro/goctl-swagger
|
module github.com/fachebot/goctl-swagger
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user