Skip to content

Commit

Permalink
Add operationId uniqueness check (#732)
Browse files Browse the repository at this point in the history
* Add operationId uniqueness check.

* Fix test

* Fix linter rules

* Move operationsIds map and function to local scope

Co-authored-by: Eason Lin <[email protected]>
Co-authored-by: sdghchj <[email protected]>
  • Loading branch information
3 people authored Jun 18, 2020
1 parent c5fb1a1 commit 366e536
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 3 deletions.
73 changes: 70 additions & 3 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
Printf("warning: failed to get package name in dir: %s, error: %s", searchDir, err.Error())
}

if err := parser.getAllGoFileInfo(packageDir, searchDir); err != nil {
if err = parser.getAllGoFileInfo(packageDir, searchDir); err != nil {
return err
}

Expand All @@ -168,7 +168,7 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
}
}

if err := parser.ParseGeneralAPIInfo(absMainAPIFilePath); err != nil {
if err = parser.ParseGeneralAPIInfo(absMainAPIFilePath); err != nil {
return err
}

Expand All @@ -177,7 +177,11 @@ func (parser *Parser) ParseAPI(searchDir string, mainAPIFile string) error {
return err
}

return parser.packages.RangeFiles(parser.ParseRouterAPIInfo)
if err = parser.packages.RangeFiles(parser.ParseRouterAPIInfo); err != nil {
return err
}

return parser.checkOperationIDUniqueness()
}

func getPkgName(searchDir string) (string, error) {
Expand Down Expand Up @@ -1262,6 +1266,69 @@ func (parser *Parser) parseFile(packageDir, path string, src interface{}) error
return nil
}

func (parser *Parser) checkOperationIDUniqueness() error {
// operationsIds contains all operationId annotations to check it's unique
operationsIds := make(map[string]string)
saveOperationID := func(operationID, currentPath string) error {
if operationID == "" {
return nil
}
if previousPath, ok := operationsIds[operationID]; ok {
return fmt.Errorf(
"duplicated @id annotation '%s' found in '%s', previously declared in: '%s'",
operationID, currentPath, previousPath)
}
operationsIds[operationID] = currentPath
return nil
}

for path, itm := range parser.swagger.Paths.Paths {
if itm.Get != nil {
currentPath := fmt.Sprintf("%s %s", "GET", path)
if err := saveOperationID(itm.Get.ID, currentPath); err != nil {
return err
}
}
if itm.Put != nil {
currentPath := fmt.Sprintf("%s %s", "PUT", path)
if err := saveOperationID(itm.Put.ID, currentPath); err != nil {
return err
}
}
if itm.Post != nil {
currentPath := fmt.Sprintf("%s %s", "POST", path)
if err := saveOperationID(itm.Post.ID, currentPath); err != nil {
return err
}
}
if itm.Delete != nil {
currentPath := fmt.Sprintf("%s %s", "DELETE", path)
if err := saveOperationID(itm.Delete.ID, currentPath); err != nil {
return err
}
}
if itm.Options != nil {
currentPath := fmt.Sprintf("%s %s", "OPTIONS", path)
if err := saveOperationID(itm.Options.ID, currentPath); err != nil {
return err
}
}
if itm.Head != nil {
currentPath := fmt.Sprintf("%s %s", "HEAD", path)
if err := saveOperationID(itm.Head.ID, currentPath); err != nil {
return err
}
}
if itm.Patch != nil {
currentPath := fmt.Sprintf("%s %s", "PATCH", path)
if err := saveOperationID(itm.Patch.ID, currentPath); err != nil {
return err
}
}
}
return nil
}

// Skip returns filepath.SkipDir error if match vendor and hidden folder
func (parser *Parser) Skip(path string, f os.FileInfo) error {
if f.IsDir() {
Expand Down
9 changes: 9 additions & 0 deletions parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,15 @@ func TestParseNested(t *testing.T) {
assert.Equal(t, string(expected), string(b))
}

func TestParseDuplicated(t *testing.T) {
searchDir := "testdata/duplicated"
mainAPIFile := "main.go"
p := New()
p.ParseDependency = true
err := p.ParseAPI(searchDir, mainAPIFile)
assert.Errorf(t, err, "duplicated @id declarations successfully found")
}

func TestParser_ParseStructArrayObject(t *testing.T) {
src := `
package api
Expand Down
17 changes: 17 additions & 0 deletions testdata/duplicated/api/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package api

import (
"github.com/gin-gonic/gin"
)

// @Description get Foo
// @ID get-foo
// @Success 200 {string} string
// @Router /testapi/get-foo [get]
func GetFoo(c *gin.Context) {}

// @Description post Bar
// @ID get-foo
// @Success 200 {string} string
// @Router /testapi/post-bar [post]
func PostBar(c *gin.Context) {}
22 changes: 22 additions & 0 deletions testdata/duplicated/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package composition

import (
"github.com/gin-gonic/gin"

"github.com/swaggo/swag/testdata/duplicated/api"
)

// @title Swagger Example API
// @version 1.0
// @description This is a sample server
// @termsOfService http://swagger.io/terms/

// @host petstore.swagger.io
// @BasePath /v2

func main() {
r := gin.New()
r.GET("/testapi/get-foo", api.GetFoo)
r.POST("/testapi/post-bar", api.PostBar)
r.Run()
}

0 comments on commit 366e536

Please sign in to comment.