add short typed tag mapping for summary/description for package goai;add suffix checks for request/response struct naming

This commit is contained in:
John Guo
2021-10-21 20:17:02 +08:00
parent 862ef57e0f
commit aacfa1bd96
8 changed files with 100 additions and 12 deletions

View File

@ -11,7 +11,7 @@ func main() {
s.BindHandler("/page/ajax", func(r *ghttp.Request) {
page := r.GetPage(100, 10)
page.AjaxActionName = "DoAjax"
buffer, _ := gview.ParseContent(`
buffer, _ := gview.ParseContent(r.Context(), `
<html>
<head>
<style>

View File

@ -161,6 +161,24 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
)
return
}
if !gstr.HasSuffix(reflectType.In(1).String(), `Req`) {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid struct naming for request: defined as "%s", but it should be named with "Req" suffix like "xxxReq"`,
reflectType.In(1).String(),
)
return
}
if !gstr.HasSuffix(reflectType.Out(0).String(), `Res`) {
err = gerror.NewCodef(
gcode.CodeInvalidParameter,
`invalid struct naming for response: defined as "%s", but it should be named with "Res" suffix like "xxxRes"`,
reflectType.Out(0).String(),
)
return
}
}
info.Func = handlerFunc
info.Type = reflect.TypeOf(f)

View File

@ -87,6 +87,10 @@ const (
var (
defaultReadContentTypes = []string{`application/json`}
defaultWriteContentTypes = []string{`application/json`}
shortTypeMapForTag = map[string]string{
"sum": "summary",
"des": "description",
}
)
// New creates and returns a OpenApiV3 implements object.
@ -213,6 +217,15 @@ func (oai *OpenApiV3) golangTypeToSchemaName(t reflect.Type) string {
return schemaName
}
func (oai *OpenApiV3) fileMapWithShortTags(m map[string]string) map[string]string {
for k, v := range shortTypeMapForTag {
if m[v] == "" && m[k] != "" {
m[v] = m[k]
}
}
return m
}
func formatRefToBytes(ref string) []byte {
return []byte(fmt.Sprintf(`{"$ref":"#/components/schemas/%s"}`, ref))
}

View File

@ -52,7 +52,7 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field *structs.Field, meth
parameter.Name = field.Name()
}
if len(tagMap) > 0 {
err := gconv.Struct(tagMap, parameter)
err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter)
if err != nil {
return nil, gerror.WrapCode(gcode.CodeInternalError, err, `mapping struct tags to Parameter failed`)
}

View File

@ -130,10 +130,10 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
}
if len(inputMetaMap) > 0 {
if err := gconv.Struct(inputMetaMap, &path); err != nil {
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &path); err != nil {
return gerror.WrapCode(gcode.CodeInternalError, err, `mapping struct tags to Path failed`)
}
if err := gconv.Struct(inputMetaMap, &operation); err != nil {
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &operation); err != nil {
return gerror.WrapCode(gcode.CodeInternalError, err, `mapping struct tags to Operation failed`)
}
}
@ -208,7 +208,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
}
)
if len(outputMetaMap) > 0 {
if err := gconv.Struct(outputMetaMap, &response); err != nil {
if err := gconv.Struct(oai.fileMapWithShortTags(outputMetaMap), &response); err != nil {
return gerror.WrapCode(gcode.CodeInternalError, err, `mapping struct tags to Response failed`)
}
}

View File

@ -32,7 +32,7 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
}
)
if len(tagMap) > 0 {
if err := gconv.Struct(tagMap, schema); err != nil {
if err := gconv.Struct(oai.fileMapWithShortTags(tagMap), schema); err != nil {
return nil, gerror.WrapCode(gcode.CodeInternalError, err, `mapping struct tags to Schema failed`)
}
}

View File

@ -561,3 +561,61 @@ func TestOpenApiV3_CommonResponse_SubDataField(t *testing.T) {
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties[`Response`].Value.Properties), 7)
})
}
func TestOpenApiV3_ShortTags(t *testing.T) {
type CommonReq struct {
AppId int64 `json:"appId" v:"required" in:"path" des:"应用Id" sum:"应用Id Summary"`
ResourceId string `json:"resourceId" in:"query" des:"资源Id" sum:"资源Id Summary"`
}
type SetSpecInfo struct {
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" des:"StorageType"`
Shards int32 `des:"shards 分片数" sum:"Shards Summary"`
Params []string `des:"默认参数(json 串-ClickHouseParams)" sum:"Params Summary"`
}
type CreateResourceReq struct {
CommonReq
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default" sum:"CreateResourceReq sum"`
Name string `des:"实例名称"`
Product string `des:"业务类型"`
Region string `v:"required" des:"区域"`
SetMap map[string]*SetSpecInfo `v:"required" des:"配置Map"`
SetSlice []SetSpecInfo `v:"required" des:"配置Slice"`
}
type CreateResourceRes struct {
gmeta.Meta `des:"Demo Response Struct"`
FlowId int64 `des:"创建实例流程id"`
}
f := func(ctx context.Context, req *CreateResourceReq) (res *CreateResourceRes, err error) {
return
}
gtest.C(t, func(t *gtest.T) {
var (
err error
oai = goai.New()
)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPut,
Object: f,
})
t.AssertNil(err)
err = oai.Add(goai.AddInput{
Path: "/test1/{appId}",
Method: goai.HttpMethodPost,
Object: f,
})
t.AssertNil(err)
//fmt.Println(oai.String())
// Schema asserts.
t.Assert(len(oai.Components.Schemas), 3)
t.Assert(oai.Paths[`/test1/{appId}`].Summary, `CreateResourceReq sum`)
t.Assert(oai.
Components.
Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].
Value.Properties[`resourceId`].Value.Description, `资源Id`)
})
}

View File

@ -21,8 +21,7 @@ const (
)
// Data retrieves and returns all metadata from `object`.
// It automatically parses and caches the tag string from "Mata" attribute as its metadata.
func Data(object interface{}) map[string]interface{} {
func Data(object interface{}) map[string]string {
reflectType, err := structs.StructType(object)
if err != nil {
panic(err)
@ -30,20 +29,20 @@ func Data(object interface{}) map[string]interface{} {
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
var (
tags = structs.ParseTag(string(field.Tag))
data = make(map[string]interface{}, len(tags))
data = make(map[string]string, len(tags))
)
for k, v := range tags {
data[k] = v
}
return data
}
return map[string]interface{}{}
return map[string]string{}
}
// Get retrieves and returns specified metadata by `key` from `object`.
func Get(object interface{}, key string) *gvar.Var {
v := Data(object)[key]
if v == nil {
v, ok := Data(object)[key]
if !ok {
return nil
}
return gvar.New(v)