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

fix(net/goai): #3660, support multiple file upload parameters for OpenAPIv3 #3662

Merged
merged 5 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions example/httpserver/swagger-set-template/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,42 @@ func (Hello) Say(ctx context.Context, req *HelloReq) (res *HelloRes, err error)
return
}

// upload file request
type UploadReq struct {
g.Meta `path:"/upload" method:"POST" tags:"Upload" mime:"multipart/form-data" summary:"上传文件"`
Files []*ghttp.UploadFile `json:"files" type:"file" dc:"选择上传多文件"`
File *ghttp.UploadFile `p:"file" type:"file" dc:"选择上传文件"`
Msg string `dc:"消息"`
}

// upload file response
type UploadRes struct {
FilesName []string `json:"files_name"`
FileName string `json:"file_name"`
Msg string `json:"msg"`
}

// upload file
func (Hello) Upload(ctx context.Context, req *UploadReq) (res *UploadRes, err error) {
g.Log().Debugf(ctx, `receive say: %+v`, req)
res = &UploadRes{
Msg: req.Msg,
}
if req.File != nil {
res.FileName = req.File.Filename
}
if len(req.Files) > 0 {
var filesName []string
for _, file := range req.Files {
filesName = append(filesName, file.Filename)
}
res.FilesName = filesName
}
return
}

const (
// MySwaggerUITemplate is the custom Swagger UI template.
MySwaggerUITemplate = `
<!DOCTYPE html>
<html lang="en">
Expand All @@ -55,6 +90,20 @@ const (
</script>
</body>
</html>
`
// OpenapiUITemplate is the OpenAPI UI template.
OpenapiUITemplate = `
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>test</title>
</head>
<body>
<div id="openapi-ui-container" spec-url="{SwaggerUIDocUrl}" theme="light"></div>
<script src="https://cdn.jsdelivr.net/npm/openapi-ui-dist@latest/lib/openapi-ui.umd.js"></script>
</body>
</html>
`
)

Expand All @@ -67,5 +116,6 @@ func main() {
)
})
s.SetSwaggerUITemplate(MySwaggerUITemplate)
// s.SetSwaggerUITemplate(OpenapiUITemplate) // files support
s.Run()
}
2 changes: 1 addition & 1 deletion net/goai/goai_shema.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type Schema struct {
MaxItems *uint64 `json:"maxItems,omitempty"`
Items *SchemaRef `json:"items,omitempty"`
Required []string `json:"required,omitempty"`
Properties Schemas `json:"properties,omitempty"`
Properties *Schemas `json:"properties,omitempty"`
MinProps uint64 `json:"minProperties,omitempty"`
MaxProps *uint64 `json:"maxProperties,omitempty"`
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"`
Expand Down
9 changes: 5 additions & 4 deletions net/goai/goai_shema_ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
if err := oai.tagMapToSchema(tagMap, schema); err != nil {
return nil, err
}
if oaiType == TypeArray && schema.Type == TypeFile {
schema.Type = TypeArray
}
}
schemaRef.Value = schema
switch oaiType {
Expand Down Expand Up @@ -111,8 +114,7 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
schemaRef.Value.Example = gconv.Bool(schemaRef.Value.Example)
}
// keep the example value as nil.
case
TypeArray:
case TypeArray:
subSchemaRef, err := oai.newSchemaRefWithGolangType(golangType.Elem(), nil)
if err != nil {
return nil, err
Expand All @@ -123,8 +125,7 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
schema.Enum = nil
}

case
TypeObject:
case TypeObject:
for golangType.Kind() == reflect.Ptr {
golangType = golangType.Elem()
}
Expand Down
6 changes: 3 additions & 3 deletions net/goai/goai_shemas.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ type Schemas struct {
refs *gmap.ListMap // map[string]SchemaRef
}

func createSchemas() Schemas {
return Schemas{
func createSchemas() *Schemas {
return &Schemas{
refs: gmap.NewListMap(),
}
}
Expand All @@ -26,7 +26,7 @@ func (s *Schemas) init() {
}
}

func (s *Schemas) Clone() Schemas {
func (s *Schemas) Clone() *Schemas {
newSchemas := createSchemas()
newSchemas.refs = s.refs.Clone()
return newSchemas
Expand Down
40 changes: 36 additions & 4 deletions net/goai/goai_z_unit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/gogf/gf/v2/encoding/gjson"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/net/goai"
"github.com/gogf/gf/v2/test/gtest"
"github.com/gogf/gf/v2/util/gmeta"
Expand Down Expand Up @@ -466,6 +467,37 @@ func TestOpenApiV3_CommonRequest_SubDataField(t *testing.T) {
})
}

func TestOpenApiV3_CommonRequest_Files(t *testing.T) {
type Req struct {
g.Meta `path:"/upload" method:"POST" tags:"Upload" mime:"multipart/form-data" summary:"上传文件"`
Files []*ghttp.UploadFile `json:"files" type:"file" dc:"选择上传文件"`
File *ghttp.UploadFile `json:"file" type:"file" dc:"选择上传文件"`
}
type Res struct {
}

f := func(ctx context.Context, req *Req) (res *Res, err error) {
return
}

gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/upload",
Method: http.MethodGet,
Object: f,
})
t.AssertNil(err)

// fmt.Println(oai.String())
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.net.goai_test.Req`).Value.Properties.Get("files").Value.Type, goai.TypeArray)
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.net.goai_test.Req`).Value.Properties.Get("file").Value.Type, goai.TypeFile)
})
}

func TestOpenApiV3_CommonResponse(t *testing.T) {
type CommonResponse struct {
Code int `json:"code" description:"Error code"`
Expand Down Expand Up @@ -1007,7 +1039,7 @@ func Test_EmbeddedStructAttribute(t *testing.T) {

b, err := json.Marshal(oai)
t.AssertNil(err)
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateResourceReq":{"properties":{"Name":{"description":"This is name.","format":"string","properties":{},"type":"string"},"Embedded":{"properties":{"Age":{"description":"This is embedded age.","format":"uint","properties":{},"type":"integer"}},"type":"object"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateResourceReq":{"properties":{"Name":{"description":"This is name.","format":"string","type":"string"},"Embedded":{"properties":{"Age":{"description":"This is embedded age.","format":"uint","type":"integer"}},"type":"object"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
})
}

Expand All @@ -1031,7 +1063,7 @@ func Test_NameFromJsonTag(t *testing.T) {

b, err := json.Marshal(oai)
t.AssertNil(err)
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateReq":{"properties":{"nick_name":{"format":"string","properties":{},"type":"string"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateReq":{"properties":{"nick_name":{"format":"string","type":"string"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
})
// GET
gtest.C(t, func(t *gtest.T) {
Expand All @@ -1052,7 +1084,7 @@ func Test_NameFromJsonTag(t *testing.T) {
b, err := json.Marshal(oai)
t.AssertNil(err)
fmt.Println(string(b))
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateReq":{"properties":{"nick_name":{"format":"string","properties":{},"type":"string"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
t.Assert(b, `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.CreateReq":{"properties":{"nick_name":{"format":"string","type":"string"}},"type":"object"}}},"info":{"title":"","version":""},"paths":null}`)
})
}

Expand Down Expand Up @@ -1188,6 +1220,6 @@ func Test_XExtension(t *testing.T) {
Object: req,
})
t.AssertNil(err)
t.Assert(oai.String(), `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.GetListReq":{"properties":{"Page":{"default":1,"description":"Page number","format":"int","properties":{},"type":"integer","x-sort":"1"},"Size":{"default":10,"description":"Size for per page.","format":"int","properties":{},"type":"integer","x-sort":"2"}},"type":"object","x-group":"User/Info"}}},"info":{"title":"","version":""},"paths":null}`)
t.Assert(oai.String(), `{"openapi":"3.0.0","components":{"schemas":{"github.com.gogf.gf.v2.net.goai_test.GetListReq":{"properties":{"Page":{"default":1,"description":"Page number","format":"int","type":"integer","x-sort":"1"},"Size":{"default":10,"description":"Size for per page.","format":"int","type":"integer","x-sort":"2"}},"type":"object","x-group":"User/Info"}}},"info":{"title":"","version":""},"paths":null}`)
})
}