Skip to content

Commit

Permalink
Add support to specify multiple routes for the same function (#957)
Browse files Browse the repository at this point in the history
  • Loading branch information
pc-coder authored Jul 31, 2021
1 parent e8632e2 commit 60ec167
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 36 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,17 @@ type DeepObject struct { //in `proto` package
// @Router /examples/groups/{group_id}/accounts/{account_id} [get]
```

### Add multiple paths

```go
/// ...
// @Param group_id path int true "Group ID"
// @Param user_id path int true "User ID"
// ...
// @Router /examples/groups/{group_id}/user/{user_id}/address [put]
// @Router /examples/user/{user_id}/address [put]
```

### Example value of struct

```go
Expand Down
20 changes: 14 additions & 6 deletions operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ import (
"golang.org/x/tools/go/loader"
)

// RouteProperties describes HTTP properties of a single router comment.
type RouteProperties struct {
HTTPMethod string
Path string
}

// Operation describes a single API operation on a path.
// For more information: https://github.com/swaggo/swag#api-operation
type Operation struct {
HTTPMethod string
Path string
RouterProperties []RouteProperties
spec.Operation

parser *Parser
Expand Down Expand Up @@ -54,8 +59,8 @@ func NewOperation(parser *Parser, options ...func(*Operation)) *Operation {
}

result := &Operation{
parser: parser,
HTTPMethod: "get",
parser: parser,
RouterProperties: []RouteProperties{},
Operation: spec.Operation{
OperationProps: spec.OperationProps{},
VendorExtensible: spec.VendorExtensible{
Expand Down Expand Up @@ -522,8 +527,11 @@ func (operation *Operation) ParseRouterComment(commentLine string) error {
path := matches[1]
httpMethod := matches[2]

operation.Path = path
operation.HTTPMethod = strings.ToUpper(httpMethod)
signature := RouteProperties{
Path: path,
HTTPMethod: strings.ToUpper(httpMethod),
}
operation.RouterProperties = append(operation.RouterProperties, signature)

return nil
}
Expand Down
38 changes: 30 additions & 8 deletions operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,35 +106,57 @@ func TestParseRouterComment(t *testing.T) {
operation := NewOperation(nil)
err := operation.ParseComment(comment, nil)
assert.NoError(t, err)
assert.Equal(t, "/customer/get-wishlist/{wishlist_id}", operation.Path)
assert.Equal(t, "GET", operation.HTTPMethod)
assert.Len(t, operation.RouterProperties, 1)
assert.Equal(t, "/customer/get-wishlist/{wishlist_id}", operation.RouterProperties[0].Path)
assert.Equal(t, "GET", operation.RouterProperties[0].HTTPMethod)
}

func TestParseRouterMultipleComments(t *testing.T) {
comment := `/@Router /customer/get-wishlist/{wishlist_id} [get]`
anotherComment := `/@Router /customer/get-the-wishlist/{wishlist_id} [post]`
operation := NewOperation(nil)

err := operation.ParseComment(comment, nil)
assert.NoError(t, err)

err = operation.ParseComment(anotherComment, nil)
assert.NoError(t, err)

assert.Len(t, operation.RouterProperties, 2)
assert.Equal(t, "/customer/get-wishlist/{wishlist_id}", operation.RouterProperties[0].Path)
assert.Equal(t, "GET", operation.RouterProperties[0].HTTPMethod)
assert.Equal(t, "/customer/get-the-wishlist/{wishlist_id}", operation.RouterProperties[1].Path)
assert.Equal(t, "POST", operation.RouterProperties[1].HTTPMethod)
}

func TestParseRouterOnlySlash(t *testing.T) {
comment := `// @Router / [get]`
operation := NewOperation(nil)
err := operation.ParseComment(comment, nil)
assert.NoError(t, err)
assert.Equal(t, "/", operation.Path)
assert.Equal(t, "GET", operation.HTTPMethod)
assert.Len(t, operation.RouterProperties, 1)
assert.Equal(t, "/", operation.RouterProperties[0].Path)
assert.Equal(t, "GET", operation.RouterProperties[0].HTTPMethod)
}

func TestParseRouterCommentWithPlusSign(t *testing.T) {
comment := `/@Router /customer/get-wishlist/{proxy+} [post]`
operation := NewOperation(nil)
err := operation.ParseComment(comment, nil)
assert.NoError(t, err)
assert.Equal(t, "/customer/get-wishlist/{proxy+}", operation.Path)
assert.Equal(t, "POST", operation.HTTPMethod)
assert.Len(t, operation.RouterProperties, 1)
assert.Equal(t, "/customer/get-wishlist/{proxy+}", operation.RouterProperties[0].Path)
assert.Equal(t, "POST", operation.RouterProperties[0].HTTPMethod)
}

func TestParseRouterCommentWithColonSign(t *testing.T) {
comment := `/@Router /customer/get-wishlist/{wishlist_id}:move [post]`
operation := NewOperation(nil)
err := operation.ParseComment(comment, nil)
assert.NoError(t, err)
assert.Equal(t, "/customer/get-wishlist/{wishlist_id}:move", operation.Path)
assert.Equal(t, "POST", operation.HTTPMethod)
assert.Len(t, operation.RouterProperties, 1)
assert.Equal(t, "/customer/get-wishlist/{wishlist_id}:move", operation.RouterProperties[0].Path)
assert.Equal(t, "POST", operation.RouterProperties[0].HTTPMethod)
}

func TestParseRouterCommentNoColonSignAtPathStartErr(t *testing.T) {
Expand Down
47 changes: 25 additions & 22 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,30 +574,33 @@ func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) err
return fmt.Errorf("ParseComment error in file %s :%+v", fileName, err)
}
}
var pathItem spec.PathItem
var ok bool

if pathItem, ok = parser.swagger.Paths.Paths[operation.Path]; !ok {
pathItem = spec.PathItem{}
}
switch strings.ToUpper(operation.HTTPMethod) {
case http.MethodGet:
pathItem.Get = &operation.Operation
case http.MethodPost:
pathItem.Post = &operation.Operation
case http.MethodDelete:
pathItem.Delete = &operation.Operation
case http.MethodPut:
pathItem.Put = &operation.Operation
case http.MethodPatch:
pathItem.Patch = &operation.Operation
case http.MethodHead:
pathItem.Head = &operation.Operation
case http.MethodOptions:
pathItem.Options = &operation.Operation
}
for _, routeProperties := range operation.RouterProperties {
var pathItem spec.PathItem
var ok bool

if pathItem, ok = parser.swagger.Paths.Paths[routeProperties.Path]; !ok {
pathItem = spec.PathItem{}
}
switch strings.ToUpper(routeProperties.HTTPMethod) {
case http.MethodGet:
pathItem.Get = &operation.Operation
case http.MethodPost:
pathItem.Post = &operation.Operation
case http.MethodDelete:
pathItem.Delete = &operation.Operation
case http.MethodPut:
pathItem.Put = &operation.Operation
case http.MethodPatch:
pathItem.Patch = &operation.Operation
case http.MethodHead:
pathItem.Head = &operation.Operation
case http.MethodOptions:
pathItem.Options = &operation.Operation
}

parser.swagger.Paths.Paths[operation.Path] = pathItem
parser.swagger.Paths.Paths[routeProperties.Path] = pathItem
}
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2075,6 +2075,35 @@ func Test(){
assert.NotNil(t, val.Options)
}

func TestParser_ParseRouterApiMultipleRoutesForSameFunction(t *testing.T) {
src := `
package test

// @Router /api/v1/{id} [get]
// @Router /api/v2/{id} [post]
func Test(){
}
`
f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
assert.NoError(t, err)

p := New()
err = p.ParseRouterAPIInfo("", f)
assert.NoError(t, err)

ps := p.swagger.Paths.Paths

val, ok := ps["/api/v1/{id}"]

assert.True(t, ok)
assert.NotNil(t, val.Get)

val, ok = ps["/api/v2/{id}"]

assert.True(t, ok)
assert.NotNil(t, val.Post)
}

func TestParser_ParseRouterApiMultiple(t *testing.T) {
src := `
package test
Expand Down

0 comments on commit 60ec167

Please sign in to comment.