Skip to content

Commit b9f659d

Browse files
authored
Merge pull request #64 from gotomicro/feature/gin-BindUri
feat(plugin/gin): support for ctx.BindUri(), ctx.ShouldBindUri() #51
2 parents 6640aa2 + 4c72f78 commit b9f659d

File tree

4 files changed

+78
-13
lines changed

4 files changed

+78
-13
lines changed

plugins/gin/handler_analyzer.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import (
99
analyzer "github.com/gotomicro/eapi"
1010
"github.com/gotomicro/eapi/plugins/common"
1111
"github.com/gotomicro/eapi/spec"
12+
"github.com/gotomicro/eapi/tag"
1213
"github.com/iancoleman/strcase"
14+
"github.com/samber/lo"
1315
)
1416

1517
const ginContextIdentName = "*github.com/gin-gonic/gin.Context"
@@ -94,7 +96,7 @@ func (p *handlerAnalyzer) Parse() {
9496
case "BindTOML", "ShouldBindTOML":
9597
p.parseBindWithContentType(call, "application/toml")
9698
case "BindUri", "ShouldBindUri":
97-
// TODO
99+
p.parseBindUri(call)
98100
case "BindHeader", "ShouldBindHeader":
99101
// TODO
100102
case "JSON":
@@ -337,3 +339,36 @@ func (p *handlerAnalyzer) getDefaultContentType() string {
337339
return analyzer.MimeTypeJson
338340
}
339341
}
342+
343+
func (p *handlerAnalyzer) parseBindUri(call *ast.CallExpr) {
344+
if len(call.Args) != 1 {
345+
return
346+
}
347+
arg0 := call.Args[0]
348+
349+
analyzer.NewSchemaBuilder(p.ctx, "").WithFieldNameParser(p.parseUriFieldName).ParseExpr(arg0)
350+
schema := p.ctx.GetSchemaByExpr(arg0, "")
351+
if schema == nil {
352+
return
353+
}
354+
schema = schema.Unref(p.ctx.Doc())
355+
if schema == nil {
356+
return
357+
}
358+
for name, property := range schema.Properties {
359+
p.api.Spec.Parameters = lo.Filter(p.api.Spec.Parameters, func(ref *spec.ParameterRef, i int) bool { return ref.Name != name })
360+
param := spec.NewPathParameter(name).WithSchema(property)
361+
param.Description = property.Description
362+
p.api.Spec.AddParameter(param)
363+
}
364+
}
365+
366+
func (p *handlerAnalyzer) parseUriFieldName(name string, field *ast.Field) string {
367+
tags := tag.Parse(field.Tag.Value)
368+
uriTag, ok := tags["uri"]
369+
if !ok {
370+
return name
371+
}
372+
res, _, _ := strings.Cut(uriTag, ",")
373+
return res
374+
}

schema_builder.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,15 @@ const (
2222
MimeTypeFormUrlencoded = "application/x-www-form-urlencoded"
2323
)
2424

25+
type FieldNameParser func(fieldName string, field *ast.Field) string
26+
2527
type SchemaBuilder struct {
26-
ctx *Context
27-
contentType string
28-
stack Stack[string]
29-
typeArgs []*spec.SchemaRef
30-
typeParams []*spec.TypeParam
28+
ctx *Context
29+
contentType string
30+
stack Stack[string]
31+
typeArgs []*spec.SchemaRef
32+
typeParams []*spec.TypeParam
33+
fieldNameParser FieldNameParser
3134
}
3235

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

47+
func (s *SchemaBuilder) WithFieldNameParser(parser FieldNameParser) *SchemaBuilder {
48+
s.fieldNameParser = parser
49+
return s
50+
}
51+
4452
func (s *SchemaBuilder) clone() *SchemaBuilder {
4553
ret := *s
4654
return &ret
@@ -283,6 +291,10 @@ func (s *SchemaBuilder) parseSelectorExpr(expr *ast.SelectorExpr) *spec.SchemaRe
283291
}
284292

285293
func (s *SchemaBuilder) getPropName(fieldName string, field *ast.Field, contentType string) (propName string) {
294+
if s.fieldNameParser != nil {
295+
return s.fieldNameParser(fieldName, field)
296+
}
297+
286298
if field.Tag == nil {
287299
return fieldName
288300
}
@@ -389,6 +401,7 @@ func (s *SchemaBuilder) parseType(t types.Type) *spec.SchemaRef {
389401
defer s.stack.Pop()
390402

391403
schema = newSchemaBuilderWithStack(s.ctx.WithPackage(typeDef.pkg).WithFile(typeDef.file), s.contentType, s.stack).
404+
WithFieldNameParser(s.fieldNameParser).
392405
setTypeArgs().
393406
parseTypeDef(typeDef)
394407
s.ctx.Doc().Components.Schemas[typeDef.ModelKey()] = schema

test/testdata/gin/docs/openapi.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,19 @@
448448
},
449449
"title": "ViewSelfRefType",
450450
"type": "object"
451+
},
452+
"server_pkg_shop.GoodsInfoPathParams": {
453+
"title": "ShopGoodsInfoPathParams",
454+
"type": "object",
455+
"ext": {
456+
"type": "object"
457+
},
458+
"properties": {
459+
"guid": {
460+
"description": "Goods Guid",
461+
"type": "integer"
462+
}
463+
}
451464
}
452465
},
453466
"securitySchemes": {
@@ -616,9 +629,10 @@
616629
"in": "path",
617630
"name": "guid",
618631
"required": true,
632+
"description": "Goods Guid",
619633
"schema": {
620-
"title": "guid",
621-
"type": "string"
634+
"description": "Goods Guid",
635+
"type": "integer"
622636
}
623637
}
624638
],
@@ -698,4 +712,4 @@
698712
}
699713
}
700714
}
701-
}
715+
}

test/testdata/gin/pkg/shop/shop.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,17 @@ func GoodsDown(c *gin.Context) {
4545
c.XML(http.StatusOK, view.GoodsDownRes{})
4646
}
4747

48+
type GoodsInfoPathParams struct {
49+
// Goods Guid
50+
Guid int `uri:"guid"`
51+
}
52+
4853
// GoodsInfo 商品详情
4954
// @consume application/json
5055
// @produce application/json
5156
func GoodsInfo(c *gin.Context) {
52-
guid := c.Param("guid")
53-
54-
// get goods info by guid
55-
_ = guid
57+
var params GoodsInfoPathParams
58+
_ = c.BindUri(&params)
5659

5760
c.JSON(http.StatusOK, view.GoodsInfoRes{})
5861
}

0 commit comments

Comments
 (0)