Skip to content

Commit

Permalink
feat(plugin/gin): support for ctx.BindUri(), ctx.ShouldBindUri() #51
Browse files Browse the repository at this point in the history
  • Loading branch information
link-duan committed Mar 20, 2023
1 parent 6640aa2 commit 4c72f78
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 13 deletions.
37 changes: 36 additions & 1 deletion plugins/gin/handler_analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import (
analyzer "github.com/gotomicro/eapi"
"github.com/gotomicro/eapi/plugins/common"
"github.com/gotomicro/eapi/spec"
"github.com/gotomicro/eapi/tag"
"github.com/iancoleman/strcase"
"github.com/samber/lo"
)

const ginContextIdentName = "*github.com/gin-gonic/gin.Context"
Expand Down Expand Up @@ -94,7 +96,7 @@ func (p *handlerAnalyzer) Parse() {
case "BindTOML", "ShouldBindTOML":
p.parseBindWithContentType(call, "application/toml")
case "BindUri", "ShouldBindUri":
// TODO
p.parseBindUri(call)
case "BindHeader", "ShouldBindHeader":
// TODO
case "JSON":
Expand Down Expand Up @@ -337,3 +339,36 @@ func (p *handlerAnalyzer) getDefaultContentType() string {
return analyzer.MimeTypeJson
}
}

func (p *handlerAnalyzer) parseBindUri(call *ast.CallExpr) {
if len(call.Args) != 1 {
return
}
arg0 := call.Args[0]

analyzer.NewSchemaBuilder(p.ctx, "").WithFieldNameParser(p.parseUriFieldName).ParseExpr(arg0)
schema := p.ctx.GetSchemaByExpr(arg0, "")
if schema == nil {
return
}
schema = schema.Unref(p.ctx.Doc())
if schema == nil {
return
}
for name, property := range schema.Properties {
p.api.Spec.Parameters = lo.Filter(p.api.Spec.Parameters, func(ref *spec.ParameterRef, i int) bool { return ref.Name != name })
param := spec.NewPathParameter(name).WithSchema(property)
param.Description = property.Description
p.api.Spec.AddParameter(param)
}
}

func (p *handlerAnalyzer) parseUriFieldName(name string, field *ast.Field) string {
tags := tag.Parse(field.Tag.Value)
uriTag, ok := tags["uri"]
if !ok {
return name
}
res, _, _ := strings.Cut(uriTag, ",")
return res
}
23 changes: 18 additions & 5 deletions schema_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,15 @@ const (
MimeTypeFormUrlencoded = "application/x-www-form-urlencoded"
)

type FieldNameParser func(fieldName string, field *ast.Field) string

type SchemaBuilder struct {
ctx *Context
contentType string
stack Stack[string]
typeArgs []*spec.SchemaRef
typeParams []*spec.TypeParam
ctx *Context
contentType string
stack Stack[string]
typeArgs []*spec.SchemaRef
typeParams []*spec.TypeParam
fieldNameParser FieldNameParser
}

func NewSchemaBuilder(ctx *Context, contentType string) *SchemaBuilder {
Expand All @@ -41,6 +44,11 @@ func newSchemaBuilderWithStack(ctx *Context, contentType string, stack Stack[str
return &SchemaBuilder{ctx: ctx, contentType: contentType, stack: stack}
}

func (s *SchemaBuilder) WithFieldNameParser(parser FieldNameParser) *SchemaBuilder {
s.fieldNameParser = parser
return s
}

func (s *SchemaBuilder) clone() *SchemaBuilder {
ret := *s
return &ret
Expand Down Expand Up @@ -283,6 +291,10 @@ func (s *SchemaBuilder) parseSelectorExpr(expr *ast.SelectorExpr) *spec.SchemaRe
}

func (s *SchemaBuilder) getPropName(fieldName string, field *ast.Field, contentType string) (propName string) {
if s.fieldNameParser != nil {
return s.fieldNameParser(fieldName, field)
}

if field.Tag == nil {
return fieldName
}
Expand Down Expand Up @@ -389,6 +401,7 @@ func (s *SchemaBuilder) parseType(t types.Type) *spec.SchemaRef {
defer s.stack.Pop()

schema = newSchemaBuilderWithStack(s.ctx.WithPackage(typeDef.pkg).WithFile(typeDef.file), s.contentType, s.stack).
WithFieldNameParser(s.fieldNameParser).
setTypeArgs().
parseTypeDef(typeDef)
s.ctx.Doc().Components.Schemas[typeDef.ModelKey()] = schema
Expand Down
20 changes: 17 additions & 3 deletions test/testdata/gin/docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,19 @@
},
"title": "ViewSelfRefType",
"type": "object"
},
"server_pkg_shop.GoodsInfoPathParams": {
"title": "ShopGoodsInfoPathParams",
"type": "object",
"ext": {
"type": "object"
},
"properties": {
"guid": {
"description": "Goods Guid",
"type": "integer"
}
}
}
},
"securitySchemes": {
Expand Down Expand Up @@ -616,9 +629,10 @@
"in": "path",
"name": "guid",
"required": true,
"description": "Goods Guid",
"schema": {
"title": "guid",
"type": "string"
"description": "Goods Guid",
"type": "integer"
}
}
],
Expand Down Expand Up @@ -698,4 +712,4 @@
}
}
}
}
}
11 changes: 7 additions & 4 deletions test/testdata/gin/pkg/shop/shop.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,17 @@ func GoodsDown(c *gin.Context) {
c.XML(http.StatusOK, view.GoodsDownRes{})
}

type GoodsInfoPathParams struct {
// Goods Guid
Guid int `uri:"guid"`
}

// GoodsInfo 商品详情
// @consume application/json
// @produce application/json
func GoodsInfo(c *gin.Context) {
guid := c.Param("guid")

// get goods info by guid
_ = guid
var params GoodsInfoPathParams
_ = c.BindUri(&params)

c.JSON(http.StatusOK, view.GoodsInfoRes{})
}
Expand Down

0 comments on commit 4c72f78

Please sign in to comment.