Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(parser.go): 添加form-data对上传文件的支持 & 添加@doc描述 #91

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,26 @@ GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/goctl-swagger
UserSearchReq {
KeyWord string `form:"keyWord"`
}

UploadReq {
Type int `form:"type"`
Key string `form:"key,optional"`
}

UploadResp {
Name string `json:"name"`
Age int `json:"age"`
Birthday string `json:"birthday"`
Description string `json:"description"`
Tag []string `json:"tag"`
}
)

@server(
// 这里是为了给service添加swagger的security,用于使用Authorize添加到接口的header
// 如果添加了jwt,这个描述可以省略
security: true
)
service user-api {
@doc(
summary: "注册"
Expand All @@ -77,6 +95,13 @@ GOPROXY=https://goproxy.cn/,direct go install github.com/zeromicro/goctl-swagger
)
@handler searchUser
get /api/user/search (UserSearchReq) returns (UserInfoReply)

@doc(
summary: "上传文件,body修改为formData,并添加一个file参数"
inject_formdata_param: "file"
)
@handler UploadHandler
post /file/upload (UploadReq) returns (UploadResp)
}
```

Expand Down
57 changes: 57 additions & 0 deletions generate/generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package generate

import (
"testing"

"github.com/zeromicro/go-zero/tools/goctl/api/parser"
"github.com/zeromicro/go-zero/tools/goctl/plugin"
)

func TestDo(t *testing.T) {

type args struct {
apiFile string
filename string
host string
basePath string
schemes string
in *plugin.Plugin
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "",
args: args{
apiFile: "./upload.api",
filename: "upload.json",
host: "127.0.0.1:8890",
basePath: "/",
schemes: "http",
in: &plugin.Plugin{
Api: nil,
ApiFilePath: "./upload.api",
Style: "",
Dir: "./",
},
},
wantErr: false,
},
}
for _, tt := range tests {
// 解析api文件
api, err := parser.Parse(tt.args.apiFile)
if err != nil {
t.Errorf("Parse() error = %v", err)
}
tt.args.in.Api = api

t.Run(tt.name, func(t *testing.T) {
if err := Do(tt.args.filename, tt.args.host, tt.args.basePath, tt.args.schemes, tt.args.in); (err != nil) != tt.wantErr {
t.Errorf("Do() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
92 changes: 73 additions & 19 deletions generate/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,30 +241,82 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge
}
} else {

reqRef := fmt.Sprintf("#/definitions/%s", route.RequestType.Name())

if len(route.RequestType.Name()) > 0 {
schema := swaggerSchemaObject{
schemaCore: schemaCore{
Ref: reqRef,
},
}
// If there is a file key in the @doc, add the file parameter, and the parameter will be changed to formData
// Example:
// @doc(
// inject_formdata_param: "file"
// )
if fileKey, ok := route.AtDoc.Properties["inject_formdata_param"]; ok {
// First, add a file parameter to the form data
fileParameter := swaggerParameterObject{
Name: strings.Trim(fileKey, "\""),
Type: "file",
In: "formData",
Required: true,
}

parameter := swaggerParameterObject{
Name: "body",
In: "body",
Required: true,
Schema: &schema,
}
parameters = append(parameters, fileParameter)

doc := strings.Join(route.RequestType.Documents(), ",")
doc = strings.Replace(doc, "//", "", -1)
// Construct the remaining parameters
for _, member := range defineStruct.Members {
required := true
// Whether the parameter is mandatory
for _, tag := range member.Tags() {
for _, option := range tag.Options {
if strings.HasPrefix(option, optionalOption) || strings.HasPrefix(option, omitemptyOption) {
required = false
}
}
}

// Obtain the parameter type
tempKind := swaggerMapTypes[strings.Replace(member.Type.Name(), "[]", "", -1)]
ftype, format, ok := primitiveSchema(tempKind, member.Type.Name())
if !ok {
ftype = tempKind.String()
format = "UNKNOWN"
}

// Construction parameters
fileParameter := swaggerParameterObject{
Name: strings.ToLower(strings.Trim(member.Name, "\"")),
Type: ftype,
Format: format,
In: "formData",
Required: required,
Description: member.GetComment(),
}

parameters = append(parameters, fileParameter)
}
} else {
reqRef := fmt.Sprintf("#/definitions/%s", route.RequestType.Name())

schema := swaggerSchemaObject{
schemaCore: schemaCore{
Ref: reqRef,
},
}

parameter := swaggerParameterObject{
Name: "body",
In: "body",
Required: true,
Schema: &schema,
}

doc := strings.Join(route.RequestType.Documents(), ",")
doc = strings.Replace(doc, "//", "", -1)

if doc != "" {
parameter.Description = doc
}

parameters = append(parameters, parameter)

if doc != "" {
parameter.Description = doc
}

parameters = append(parameters, parameter)
}
}
}
Expand Down Expand Up @@ -384,6 +436,8 @@ func renderServiceRoutes(service spec.Service, groups []spec.Group, paths swagge

if group.Annotation.Properties["jwt"] != "" {
operationObject.Security = &[]swaggerSecurityRequirementObject{{"apiKey": []string{}}}
} else if group.Annotation.Properties["security"] == "true" {
operationObject.Security = &[]swaggerSecurityRequirementObject{{"apiKey": []string{}}}
}

switch strings.ToUpper(route.Method) {
Expand Down Expand Up @@ -572,7 +626,7 @@ func schemaOfField(member spec.Member) swaggerSchemaObject {
comment = strings.Replace(comment, "//", "", -1)

switch ft := kind; ft {
case reflect.Invalid: //[]Struct 也有可能是 Struct
case reflect.Invalid: // []Struct 也有可能是 Struct
// []Struct
// map[ArrayType:map[Star:map[StringExpr:UserSearchReq] StringExpr:*UserSearchReq] StringExpr:[]*UserSearchReq]
refTypeName := strings.Replace(member.Type.Name(), "[", "", 1)
Expand Down
34 changes: 34 additions & 0 deletions generate/upload.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
syntax = "v1"

// 上传文件
type UploadRequest {
Type int `form:"type"` // Type 1视频 2音频 3图片 4文档 5附件
Key string `form:"key,optional"` // Key 用于自定义保存到COS的文件名称,会自动加上传的文件后缀
}

type UploadResp {
Msg string `json:"msg"`
ID string `json:"id"`
Src string `json:"src"`
}

type Resp {
Data UploadResp `json:"data"`
Msg string `json:"msg"`
Code int `json:"code"`
}

// 文件
@server (
middleware: Auth
group: api
security: true
)
service upload {
@doc (
summary : "上传文件"
inject_formdata_param: "file"
)
@handler UploadHandler
post /file/upload (UploadRequest) returns (Resp)
}
137 changes: 137 additions & 0 deletions generate/upload.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"swagger": "2.0",
"info": {
"title": "",
"version": ""
},
"host": "127.0.0.1:8890",
"basePath": "/",
"schemes": [
"http"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": {
"/file/upload": {
"post": {
"summary": "上传文件",
"operationId": "UploadHandler",
"responses": {
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/Resp"
}
}
},
"parameters": [
{
"name": "file",
"in": "formData",
"required": true,
"type": "file"
},
{
"name": "type",
"description": "// Type 1视频 2音频 3图片 4文档 5附件",
"in": "formData",
"required": true,
"type": "integer",
"format": "int32"
},
{
"name": "key",
"description": "// Key 用于自定义保存到COS的文件名称,会自动加上传的文件后缀",
"in": "formData",
"required": false,
"type": "string"
}
],
"tags": [
"api"
],
"consumes": [
"multipart/form-data"
],
"security": [
{
"apiKey": []
}
]
}
}
},
"definitions": {
"Resp": {
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/UploadResp"
},
"msg": {
"type": "string"
},
"code": {
"type": "integer",
"format": "int32"
}
},
"title": "Resp",
"required": [
"data",
"msg",
"code"
]
},
"UploadRequest": {
"type": "object",
"properties": {
"type": {
"type": "integer",
"format": "int32",
"description": " Type 1视频 2音频 3图片 4文档 5附件"
},
"key": {
"type": "string",
"description": " Key 用于自定义保存到COS的文件名称,会自动加上传的文件后缀"
}
},
"title": "UploadRequest",
"required": [
"type"
]
},
"UploadResp": {
"type": "object",
"properties": {
"msg": {
"type": "string"
},
"id": {
"type": "string"
},
"src": {
"type": "string"
}
},
"title": "UploadResp",
"required": [
"msg",
"id",
"src"
]
}
},
"securityDefinitions": {
"apiKey": {
"type": "apiKey",
"description": "Enter JWT Bearer token **_only_**",
"name": "Authorization",
"in": "header"
}
}
}
Binary file added goctl-swagger
Binary file not shown.
Binary file added goctl-swagger.exe
Binary file not shown.