Skip to content

Commit 7c20f30

Browse files
authored
feat: parse only specific extension tag (#1219)
* 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
1 parent 3fe9ca2 commit 7c20f30

File tree

5 files changed

+99
-1
lines changed

5 files changed

+99
-1
lines changed

cmd/swag/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ const (
3434
parseGoListFlag = "parseGoList"
3535
quietFlag = "quiet"
3636
tagsFlag = "tags"
37+
parseExtensionFlag = "parseExtension"
3738
)
3839

3940
var initFlags = []cli.Flag{
@@ -129,6 +130,11 @@ var initFlags = []cli.Flag{
129130
Value: true,
130131
Usage: "Parse dependency via 'go list'",
131132
},
133+
&cli.StringFlag{
134+
Name: parseExtensionFlag,
135+
Value: "",
136+
Usage: "Parse only those operations that match given extension",
137+
},
132138
&cli.StringFlag{
133139
Name: tagsFlag,
134140
Aliases: []string{"t"},
@@ -158,6 +164,7 @@ func initAction(ctx *cli.Context) error {
158164
return gen.New().Build(&gen.Config{
159165
SearchDir: ctx.String(searchDirFlag),
160166
Excludes: ctx.String(excludeFlag),
167+
ParseExtension: ctx.String(parseExtensionFlag),
161168
MainAPIFile: ctx.String(generalInfoFlag),
162169
PropNamingStrategy: strategy,
163170
OutputDir: ctx.String(outputFlag),

gen/gen.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ type Config struct {
7272
// excludes dirs and files in SearchDir,comma separated
7373
Excludes string
7474

75+
// outputs only specific extension
76+
ParseExtension string
77+
7578
// OutputDir represents the output directory for all the generated files
7679
OutputDir string
7780

@@ -167,6 +170,7 @@ func (g *Gen) Build(config *Config) error {
167170
swag.SetMarkdownFileDirectory(config.MarkdownFilesDir),
168171
swag.SetDebugger(config.Debugger),
169172
swag.SetExcludedDirsAndFiles(config.Excludes),
173+
swag.SetParseExtension(config.ParseExtension),
170174
swag.SetCodeExamplesDirectory(config.CodeExampleFilesDir),
171175
swag.SetStrict(config.Strict),
172176
swag.SetOverrides(overrides),

parser.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ type Parser struct {
137137
// excludes excludes dirs and files in SearchDir
138138
excludes map[string]struct{}
139139

140+
// tells parser to include only specific extension
141+
parseExtension string
142+
140143
// debugging output goes here
141144
debug Debugger
142145

@@ -265,6 +268,13 @@ func SetTags(include string) func(*Parser) {
265268
}
266269
}
267270

271+
// SetParseExtension parses only those operations which match given extension
272+
func SetParseExtension(parseExtension string) func(*Parser) {
273+
return func(p *Parser) {
274+
p.parseExtension = parseExtension
275+
}
276+
}
277+
268278
// SetStrict sets whether swag should error or warn when it detects cases which are most likely user errors.
269279
func SetStrict(strict bool) func(*Parser) {
270280
return func(p *Parser) {
@@ -828,12 +838,29 @@ func (parser *Parser) matchTags(comments []*ast.Comment) (match bool) {
828838
return true
829839
}
830840

841+
func matchExtension(extensionToMatch string, comments []*ast.Comment) (match bool) {
842+
if len(extensionToMatch) != 0 {
843+
for _, comment := range comments {
844+
commentLine := strings.TrimSpace(strings.TrimLeft(comment.Text, "/"))
845+
fields := FieldsByAnySpace(commentLine, 2)
846+
lowerAttribute := strings.ToLower(fields[0])
847+
848+
if lowerAttribute == fmt.Sprintf("@x-%s", strings.ToLower(extensionToMatch)) {
849+
return true
850+
}
851+
}
852+
return false
853+
}
854+
return true
855+
}
856+
831857
// ParseRouterAPIInfo parses router api info for given astFile.
832858
func (parser *Parser) ParseRouterAPIInfo(fileName string, astFile *ast.File) error {
833859
for _, astDescription := range astFile.Decls {
834860
astDeclaration, ok := astDescription.(*ast.FuncDecl)
835861
if ok && astDeclaration.Doc != nil && astDeclaration.Doc.List != nil {
836-
if parser.matchTags(astDeclaration.Doc.List) {
862+
if parser.matchTags(astDeclaration.Doc.List) &&
863+
matchExtension(parser.parseExtension, astDeclaration.Doc.List) {
837864
// for per 'function' comment, create a new 'Operation' object
838865
operation := NewOperation(parser, SetCodeExampleFilesDirectory(parser.codeExampleFilesDir))
839866
for _, comment := range astDeclaration.Doc.List {

parser_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3878,3 +3878,51 @@ func TestParser_matchTags(t *testing.T) {
38783878
})
38793879
}
38803880
}
3881+
3882+
func TestParser_parseExtension(t *testing.T) {
3883+
3884+
src, err := os.ReadFile("testdata/parseExtension/parseExtension.go")
3885+
assert.NoError(t, err)
3886+
3887+
f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments)
3888+
assert.NoError(t, err)
3889+
3890+
tests := []struct {
3891+
name string
3892+
parser *Parser
3893+
expectedPaths map[string]bool
3894+
}{
3895+
{
3896+
name: "when no flag is set, everything is exported",
3897+
parser: New(),
3898+
expectedPaths: map[string]bool{"/without-extension": true, "/with-another-extension": true, "/with-correct-extension": true},
3899+
},
3900+
{
3901+
name: "when nonexistent flag is set, nothing is exported",
3902+
parser: New(SetParseExtension("nonexistent-extension-filter")),
3903+
expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": false},
3904+
},
3905+
{
3906+
name: "when correct flag is set, only that Path is exported",
3907+
parser: New(SetParseExtension("google-backend")),
3908+
expectedPaths: map[string]bool{"/without-extension": false, "/with-another-extension": false, "/with-correct-extension": true},
3909+
},
3910+
}
3911+
3912+
for _, tt := range tests {
3913+
t.Run(tt.name, func(t *testing.T) {
3914+
err = tt.parser.ParseRouterAPIInfo("", f)
3915+
assert.NoError(t, err)
3916+
for p, isExpected := range tt.expectedPaths {
3917+
_, ok := tt.parser.swagger.Paths.Paths[p]
3918+
assert.Equal(t, isExpected, ok)
3919+
}
3920+
3921+
for p := range tt.parser.swagger.Paths.Paths {
3922+
_, isExpected := tt.expectedPaths[p]
3923+
assert.Equal(t, isExpected, true)
3924+
}
3925+
})
3926+
3927+
}
3928+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package main
2+
3+
// @Router /without-extension [get]
4+
func Fun() {}
5+
6+
// @Router /with-another-extension [get]
7+
// @x-another-extension {"address": "http://backend"}
8+
func Fun2() {}
9+
10+
// @Router /with-correct-extension [get]
11+
// @x-google-backend {"address": "http://backend"}
12+
func Fun3() {}

0 commit comments

Comments
 (0)