From 435615ad0e101587079a303e8d9042d167fe6735 Mon Sep 17 00:00:00 2001 From: Helder Alves Date: Thu, 20 Jan 2022 15:01:05 -0300 Subject: [PATCH 1/4] feat: escape comma with double comma (,,) in extensions param --- field_parser.go | 2 ++ field_parser_test.go | 15 ++++++++++++ operation.go | 2 ++ parser_test.go | 57 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/field_parser.go b/field_parser.go index fba3d78f4..974556814 100644 --- a/field_parser.go +++ b/field_parser.go @@ -207,7 +207,9 @@ func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error { extensionsTag := ps.tag.Get(extensionsTag) if extensionsTag != "" { structField.extensions = map[string]interface{}{} + extensionsTag := strings.ReplaceAll(extensionsTag, `,,`, `{{escape-comma}}`) for _, val := range strings.Split(extensionsTag, ",") { + val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") parts := strings.SplitN(val, "=", 2) if len(parts) == 2 { structField.extensions[parts[0]] = parts[1] diff --git a/field_parser_test.go b/field_parser_test.go index 07b803d19..0619ddfc6 100644 --- a/field_parser_test.go +++ b/field_parser_test.go @@ -72,7 +72,22 @@ func TestDefaultFieldParser(t *testing.T) { }) t.Run("Extensions tag", func(t *testing.T) { + t.Parallel() + schema := spec.Schema{} + schema.Type = []string{"int"} + schema.Extensions = map[string]interface{}{} + err := newTagBaseFieldParser( + &Parser{}, + &ast.Field{Tag: &ast.BasicLit{ + Value: `json:"test" extensions:"x-nullable,x-abc=def,!x-omitempty,x-example=[0,, 9]"`, + }}, + ).ComplementSchema(&schema) + assert.NoError(t, err) + assert.Equal(t, true, schema.Extensions["x-nullable"]) + assert.Equal(t, "def", schema.Extensions["x-abc"]) + assert.Equal(t, false, schema.Extensions["x-omitempty"]) + assert.Equal(t, "[0,, 9]", schema.Extensions["x-example"]) }) t.Run("Enums tag", func(t *testing.T) { diff --git a/operation.go b/operation.go index 54c19ba01..7197dcb4c 100644 --- a/operation.go +++ b/operation.go @@ -495,8 +495,10 @@ func setEnumParam(param *spec.Parameter, attr, objectType, schemaType string) er } func setExtensionParam(param *spec.Parameter, attr string) error { + attr = strings.ReplaceAll(attr, `,,`, `{{escape-comma}}`) param.Extensions = map[string]interface{}{} for _, val := range strings.Split(attr, ",") { + val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") parts := strings.SplitN(val, "=", 2) if len(parts) == 2 { param.Extensions.Add(parts[0], parts[1]) diff --git a/parser_test.go b/parser_test.go index 5282f8e88..afa9846c9 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2951,6 +2951,63 @@ func Fun() { assert.Equal(t, expected, string(b)) } +func TestParseParamCommentExtension(t *testing.T) { + t.Parallel() + + src := ` +package main + +// @Param request query string true "query params" extensions(x-example=[0,, 9],x-foo=bar) +// @Success 200 +// @Router /test [get] +func Fun() { + +} +` + expected := `{ + "info": { + "contact": {} + }, + "paths": { + "/test": { + "get": { + "parameters": [ + { + "type": "string", + "x-example": "[0, 9]", + "x-foo": "bar", + "description": "query params", + "name": "request", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "" + } + } + } + } + } +}` + + f, err := goparser.ParseFile(token.NewFileSet(), "", src, goparser.ParseComments) + assert.NoError(t, err) + + p := New() + _ = p.packages.CollectAstFile("api", "api/api.go", f) + + _, err = p.packages.ParseTypes() + assert.NoError(t, err) + + err = p.ParseRouterAPIInfo("", f) + assert.NoError(t, err) + + b, _ := json.MarshalIndent(p.swagger, "", " ") + assert.JSONEq(t, expected, string(b)) +} + func TestParseRenamedStructDefinition(t *testing.T) { t.Parallel() From bc9b616d9f428fe9b748779920b5e116e2de717f Mon Sep 17 00:00:00 2001 From: Helder Alves Date: Fri, 21 Jan 2022 12:13:37 -0300 Subject: [PATCH 2/4] fix: test --- field_parser_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/field_parser_test.go b/field_parser_test.go index 0619ddfc6..c48e27517 100644 --- a/field_parser_test.go +++ b/field_parser_test.go @@ -87,7 +87,7 @@ func TestDefaultFieldParser(t *testing.T) { assert.Equal(t, true, schema.Extensions["x-nullable"]) assert.Equal(t, "def", schema.Extensions["x-abc"]) assert.Equal(t, false, schema.Extensions["x-omitempty"]) - assert.Equal(t, "[0,, 9]", schema.Extensions["x-example"]) + assert.Equal(t, "[0, 9]", schema.Extensions["x-example"]) }) t.Run("Enums tag", func(t *testing.T) { From f174849ee8f5b715bb63ad25dd1be845d5afa47b Mon Sep 17 00:00:00 2001 From: Helder Alves Date: Mon, 7 Feb 2022 10:19:58 -0300 Subject: [PATCH 3/4] fix: split extensions by comma respecting open/close brackets --- field_parser.go | 46 +++++++++++++++++++++++++++++++++++++++++--- field_parser_test.go | 3 ++- operation.go | 4 +--- parser_test.go | 2 +- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/field_parser.go b/field_parser.go index 974556814..e4b876f3f 100644 --- a/field_parser.go +++ b/field_parser.go @@ -146,6 +146,46 @@ type structField struct { unique bool } +// splitNotWrapped slices s into all substrings separated by sep if sep is not +// wrapped by brackets and returns a slice of the substrings between those separators. +func splitNotWrapped(s string, sep rune) []string { + openCloseMap := map[rune]rune{ + '(': ')', + '[': ']', + '{': '}', + } + + result := make([]string, 0) + current := "" + var openCount = 0 + var openChar rune + for _, char := range s { + if openChar == 0 && openCloseMap[char] != 0 { + openChar = char + openCount++ + current += string(char) + } else if char == openChar { + openCount++ + current = current + string(char) + } else if openCount > 0 && char == openCloseMap[openChar] { + openCount-- + current += string(char) + } else if openCount == 0 && char == sep { + result = append(result, current) + openChar = 0 + current = "" + } else { + current += string(char) + } + } + + if current != "" { + result = append(result, current) + } + + return result +} + func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error { types := ps.p.GetSchemaTypePath(schema, 2) if len(types) == 0 { @@ -207,9 +247,9 @@ func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error { extensionsTag := ps.tag.Get(extensionsTag) if extensionsTag != "" { structField.extensions = map[string]interface{}{} - extensionsTag := strings.ReplaceAll(extensionsTag, `,,`, `{{escape-comma}}`) - for _, val := range strings.Split(extensionsTag, ",") { - val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") + //extensionsTag := strings.ReplaceAll(extensionsTag, `,,`, `{{escape-comma}}`) + for _, val := range splitNotWrapped(extensionsTag, ',') { + //val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") parts := strings.SplitN(val, "=", 2) if len(parts) == 2 { structField.extensions[parts[0]] = parts[1] diff --git a/field_parser_test.go b/field_parser_test.go index c48e27517..14b63a687 100644 --- a/field_parser_test.go +++ b/field_parser_test.go @@ -80,7 +80,7 @@ func TestDefaultFieldParser(t *testing.T) { err := newTagBaseFieldParser( &Parser{}, &ast.Field{Tag: &ast.BasicLit{ - Value: `json:"test" extensions:"x-nullable,x-abc=def,!x-omitempty,x-example=[0,, 9]"`, + Value: `json:"test" extensions:"x-nullable,x-abc=def,!x-omitempty,x-example=[0, 9],x-example2={çãíœ, (bar=(abc, def)), [0,9]}"`, }}, ).ComplementSchema(&schema) assert.NoError(t, err) @@ -88,6 +88,7 @@ func TestDefaultFieldParser(t *testing.T) { assert.Equal(t, "def", schema.Extensions["x-abc"]) assert.Equal(t, false, schema.Extensions["x-omitempty"]) assert.Equal(t, "[0, 9]", schema.Extensions["x-example"]) + assert.Equal(t, "{çãíœ, (bar=(abc, def)), [0,9]}", schema.Extensions["x-example2"]) }) t.Run("Enums tag", func(t *testing.T) { diff --git a/operation.go b/operation.go index 7197dcb4c..07f6db6b8 100644 --- a/operation.go +++ b/operation.go @@ -495,10 +495,8 @@ func setEnumParam(param *spec.Parameter, attr, objectType, schemaType string) er } func setExtensionParam(param *spec.Parameter, attr string) error { - attr = strings.ReplaceAll(attr, `,,`, `{{escape-comma}}`) param.Extensions = map[string]interface{}{} - for _, val := range strings.Split(attr, ",") { - val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") + for _, val := range splitNotWrapped(attr, ',') { parts := strings.SplitN(val, "=", 2) if len(parts) == 2 { param.Extensions.Add(parts[0], parts[1]) diff --git a/parser_test.go b/parser_test.go index afa9846c9..3b58c3705 100644 --- a/parser_test.go +++ b/parser_test.go @@ -2957,7 +2957,7 @@ func TestParseParamCommentExtension(t *testing.T) { src := ` package main -// @Param request query string true "query params" extensions(x-example=[0,, 9],x-foo=bar) +// @Param request query string true "query params" extensions(x-example=[0, 9],x-foo=bar) // @Success 200 // @Router /test [get] func Fun() { From c9cb00293f7de0e45b19652cf0c5b1b3173df18a Mon Sep 17 00:00:00 2001 From: Helder Alves Date: Mon, 7 Feb 2022 10:22:44 -0300 Subject: [PATCH 4/4] fix: split extensions by comma respecting open/close brackets --- field_parser.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/field_parser.go b/field_parser.go index e4b876f3f..e878ed161 100644 --- a/field_parser.go +++ b/field_parser.go @@ -247,9 +247,7 @@ func (ps *tagBaseFieldParser) ComplementSchema(schema *spec.Schema) error { extensionsTag := ps.tag.Get(extensionsTag) if extensionsTag != "" { structField.extensions = map[string]interface{}{} - //extensionsTag := strings.ReplaceAll(extensionsTag, `,,`, `{{escape-comma}}`) for _, val := range splitNotWrapped(extensionsTag, ',') { - //val = strings.ReplaceAll(val, `{{escape-comma}}`, ",") parts := strings.SplitN(val, "=", 2) if len(parts) == 2 { structField.extensions[parts[0]] = parts[1]