Skip to content

Commit

Permalink
feat: parse only specific extension tag (#1219)
Browse files Browse the repository at this point in the history
* feat: parse only specific extension tag

* chore: add tests

* fix: Exclude operation before it's parsed

Previous implementation had an issue where it was excluding operation after schema
and other parts were parsed. That caused schema definition to be included in the
output file, even though operation didn't match the extension.

New implementation won't even start processing operation if extension isn't matching.

Only potential issue/problem I see with this approach is that we are duplicating logic
for comment line parsing (I basically c/p it from other places) but not sure how big
of an issue that actually is as I noticed we are doing that at other places as well.

* fix: detaching matchExtension from parser as per PR review suggestions
  • Loading branch information
igorkova committed Dec 11, 2022
1 parent 3fe9ca2 commit 7c20f30
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 1 deletion.
7 changes: 7 additions & 0 deletions cmd/swag/main.go
Expand Up @@ -34,6 +34,7 @@ const (
parseGoListFlag = "parseGoList"
quietFlag = "quiet"
tagsFlag = "tags"
parseExtensionFlag = "parseExtension"
)

var initFlags = []cli.Flag{
Expand Down Expand Up @@ -129,6 +130,11 @@ var initFlags = []cli.Flag{
Value: true,
Usage: "Parse dependency via 'go list'",
},
&cli.StringFlag{
Name: parseExtensionFlag,
Value: "",
Usage: "Parse only those operations that match given extension",
},
&cli.StringFlag{
Name: tagsFlag,
Aliases: []string{"t"},
Expand Down Expand Up @@ -158,6 +164,7 @@ func initAction(ctx *cli.Context) error {
return gen.New().Build(&gen.Config{
SearchDir: ctx.String(searchDirFlag),
Excludes: ctx.String(excludeFlag),
ParseExtension: ctx.String(parseExtensionFlag),
MainAPIFile: ctx.String(generalInfoFlag),
PropNamingStrategy: strategy,
OutputDir: ctx.String(outputFlag),
Expand Down
4 changes: 4 additions & 0 deletions gen/gen.go
Expand Up @@ -72,6 +72,9 @@ type Config struct {
// excludes dirs and files in SearchDir,comma separated
Excludes string

// outputs only specific extension
ParseExtension string

// OutputDir represents the output directory for all the generated files
OutputDir string

Expand Down Expand Up @@ -167,6 +170,7 @@ func (g *Gen) Build(config *Config) error {
swag.SetMarkdownFileDirectory(config.MarkdownFilesDir),
swag.SetDebugger(config.Debugger),
swag.SetExcludedDirsAndFiles(config.Excludes),
swag.SetParseExtension(config.ParseExtension),
swag.SetCodeExamplesDirectory(config.CodeExampleFilesDir),
swag.SetStrict(config.Strict),
swag.SetOverrides(overrides),
Expand Down
29 changes: 28 additions & 1 deletion parser.go
Expand Up @@ -137,6 +137,9 @@ type Parser struct {
// excludes excludes dirs and files in SearchDir
excludes map[string]struct{}

// tells parser to include only specific extension
parseExtension string

// debugging output goes here
debug Debugger

Expand Down Expand Up @@ -265,6 +268,13 @@ func SetTags(include string) func(*Parser) {
}
}

// SetParseExtension parses only those operations which match given extension
func SetParseExtension(parseExtension string) func(*Parser) {
return func(p *Parser) {
p.parseExtension = parseExtension
}
}

// SetStrict sets whether swag should error or warn when it detects cases which are most likely user errors.
func SetStrict(strict bool) func(*Parser) {
return func(p *Parser) {
Expand Down Expand Up @@ -828,12 +838,29 @@ func (parser *Parser) matchTags(comments []*ast.Comment) (match bool) {
return true
}

func matchExtension(extensionToMatch string, comments []*ast.Comment) (match bool) {
if len(extensionToMatch) != 0 {
for _, comment := range comments {
commentLine := strings.TrimSpace(strings.TrimLeft(comment.Text, "/"))
fields := FieldsByAnySpace(commentLine, 2)
lowerAttribute := strings.ToLower(fields[0])

if lowerAttribute == fmt.Sprintf("@x-%s", strings.ToLower(extensionToMatch)) {
return true
}
}
return false
}
return true
}

// ParseRouterAPIInfo parses router api info for given astFile.
func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error {
for _, astDescription := range astFile.Decls {
astDeclaration, ok := astDescription.(*ast.FuncDecl)
if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
if parser.matchTags(astDeclaration.Doc.List) {
if parser.matchTags(astDeclaration.Doc.List) &&
matchExtension(parser.parseExtension, astDeclaration.Doc.List) {
// for per 'function' comment, create a new 'Operation' object
operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
for _, comment := range astDeclaration.Doc.List {
Expand Down
48 changes: 48 additions & 0 deletions parser_test.go
Expand Up @@ -3878,3 +3878,51 @@ func TestParser_matchTags(t *testing.T) {
})
}
}

func TestParser_parseExtension(t *testing.T) {

src, err := os.ReadFile("testdata/parseExtension/parseExtension.go")
assert.NoError(t, err)

f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
assert.NoError(t, err)

tests := []struct {
name string
parser *Parser
expectedPaths map[string]bool
}{
{
name: "when no flag is set, everything is exported",
parser: New(),
expectedPaths: map[string]bool{"/without-extension": true, "/with-another-extension": true, "/with-correct-extension": true},
},
{
name: "when nonexistent flag is set, nothing is exported",
parser: New(SetParseExtension("nonexistent-extension-filter")),
expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": false},
},
{
name: "when correct flag is set, only that Path is exported",
parser: New(SetParseExtension("google-backend")),
expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": true},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err = tt.parser.ParseRouterAPIInfo("", f)
assert.NoError(t, err)
for p, isExpected := range tt.expectedPaths {
_, ok := tt.parser.swagger.Paths.Paths[p]
assert.Equal(t, isExpected, ok)
}

for p := range tt.parser.swagger.Paths.Paths {
_, isExpected := tt.expectedPaths[p]
assert.Equal(t, isExpected, true)
}
})

}
}
12 changes: 12 additions & 0 deletions testdata/parseExtension/parseExtension.go
@@ -0,0 +1,12 @@
package main

// @Router /without-extension [get]
func Fun() {}

// @Router /with-another-extension [get]
// @x-another-extension {"address": "http://backend"}
func Fun2() {}

// @Router /with-correct-extension [get]
// @x-google-backend {"address": "http://backend"}
func Fun3() {}

0 comments on commit 7c20f30

Please sign in to comment.