Skip to content

Commit 8611f24

Browse files
wing328mlebihan
andauthored
Fix Golang pattern validation with regex fails on commas (OpenAPITools#24349)
* Fix Golang pattern validation with regex fails on commas OpenAPITools#20079 * Test project is now in with its test ready to run (depicting the remaining problems) * Re-generating samples. * update workflow * regenerate --------- Co-authored-by: Marc LE BIHAN <mlebihan29@gmail.com>
1 parent 60975f1 commit 8611f24

49 files changed

Lines changed: 3810 additions & 33 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/samples-go-client.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ on:
99
- samples/client/others/go/allof_multiple_ref_and_discriminator/**
1010
- samples/client/others/go/oneof-anyof-required/**
1111
- samples/client/others/go/oneof-discriminator-lookup/**
12+
- samples/client/others/go/issue_20079_go_regex_wrongly_translated/**
1213
pull_request:
1314
paths:
1415
- 'samples/openapi3/client/petstore/go/go-petstore-aws-signature/**'
@@ -17,6 +18,7 @@ on:
1718
- samples/client/others/go/allof_multiple_ref_and_discriminator/**
1819
- samples/client/others/go/oneof-anyof-required/**
1920
- samples/client/others/go/oneof-discriminator-lookup/**
21+
- samples/client/others/go/issue_20079_go_regex_wrongly_translated/**
2022
jobs:
2123
build:
2224
name: Build Go
@@ -31,6 +33,7 @@ jobs:
3133
- samples/client/others/go/allof_multiple_ref_and_discriminator/
3234
- samples/client/others/go/oneof-anyof-required/
3335
- samples/client/others/go/oneof-discriminator-lookup/
36+
- samples/client/others/go/issue_20079_go_regex_wrongly_translated/
3437
steps:
3538
- uses: actions/checkout@v7
3639
- uses: actions/setup-go@v7

bin/configs/go-regex-test.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
generatorName: go
2+
outputDir: samples/client/others/go/issue_20079_go_regex_wrongly_translated/
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/issue_20079_go_regex_wrongly_translated.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/go
5+
#additionalProperties:
6+
# enumClassPrefix: "true"
7+
# packageName: petstore
8+
# disallowAdditionalPropertiesIfNotPresent: false
9+
# generateInterfaces: true
10+
# useDefaultValuesForRequiredVars: "true"
11+
# enumUnknownDefaultCase: true

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,11 @@ public String toEnumVarName(String value, String datatype) {
992992
public boolean specVersionGreaterThanOrEqualTo310(OpenAPI openAPI) {
993993
String originalSpecVersion;
994994
String xOriginalSwaggerVersion = "x-original-swagger-version";
995+
996+
if (openAPI == null) {
997+
return false;
998+
}
999+
9951000
if (openAPI.getExtensions() != null && !openAPI.getExtensions().isEmpty() && openAPI.getExtensions().containsValue(xOriginalSwaggerVersion)) {
9961001
originalSpecVersion = (String) openAPI.getExtensions().get(xOriginalSwaggerVersion);
9971002
} else {

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractGoCodegen.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
import java.io.File;
3131
import java.util.*;
32+
import java.util.regex.Matcher;
3233

3334
import static org.openapitools.codegen.utils.CamelizeOption.LOWERCASE_FIRST_LETTER;
3435
import static org.openapitools.codegen.utils.StringUtils.camelize;
@@ -38,6 +39,7 @@ public abstract class AbstractGoCodegen extends DefaultCodegen implements Codege
3839

3940
private final Logger LOGGER = LoggerFactory.getLogger(AbstractGoCodegen.class);
4041
private static final String NUMERIC_ENUM_PREFIX = "_";
42+
private static final String X_GO_CUSTOM_TAG = "x-go-custom-tag";
4143

4244
@Setter
4345
protected boolean withGoCodegenComment = false;
@@ -804,9 +806,20 @@ public ModelsMap postProcessModels(ModelsMap objs) {
804806
}
805807

806808
if (cp.pattern != null) {
807-
cp.vendorExtensions.put("x-go-custom-tag", "validate:\"regexp=" +
808-
cp.pattern.replace("\\", "\\\\").replaceAll("^/|/$", "") +
809-
"\"");
809+
String regexp = String.format(Locale.getDefault(), "regexp=%s", cp.pattern);
810+
811+
// Replace backtick by \\x60, if found
812+
if (regexp.contains("`")) {
813+
regexp = regexp.replace("`", "\\x60");
814+
}
815+
816+
// Escape comma
817+
if (regexp.contains(",")) {
818+
regexp = regexp.replace(",", "\\\\,");
819+
}
820+
821+
String validate = String.format(Locale.getDefault(), "validate:\"%s\"", regexp);
822+
cp.vendorExtensions.put(X_GO_CUSTOM_TAG, validate);
810823
}
811824

812825
// construct data tag in the template: x-go-datatag
@@ -835,8 +848,8 @@ public ModelsMap postProcessModels(ModelsMap objs) {
835848
}
836849

837850
// {{#vendorExtensions.x-go-custom-tag}} {{{.}}}{{/vendorExtensions.x-go-custom-tag}}
838-
if (StringUtils.isNotEmpty(String.valueOf(cp.vendorExtensions.getOrDefault("x-go-custom-tag", "")))) {
839-
goDataTag += " " + cp.vendorExtensions.get("x-go-custom-tag");
851+
if (StringUtils.isNotEmpty(String.valueOf(cp.vendorExtensions.getOrDefault(X_GO_CUSTOM_TAG, "")))) {
852+
goDataTag += " " + cp.vendorExtensions.get(X_GO_CUSTOM_TAG);
840853
}
841854

842855
// if it contains backtick, wrap with " instead
@@ -891,6 +904,11 @@ public ModelsMap postProcessModels(ModelsMap objs) {
891904
return postProcessModelsEnum(objs);
892905
}
893906

907+
@Override
908+
public String addRegularExpressionDelimiter(String pattern) {
909+
return pattern;
910+
}
911+
894912
@Override
895913
public Map<String, Object> postProcessSupportingFileData(Map<String, Object> objs) {
896914
generateYAMLSpecFile(objs);

modules/openapi-generator/src/main/resources/go/model_oneof.mustache

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,16 @@ func (dst *{{classname}}) UnmarshalJSON(data []byte) error {
8080
} else if match == 1 {
8181
return nil // exactly one match
8282
} else { // no match
83-
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
83+
{{#oneOf}}
84+
if err != nil {
85+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}}): %v", err)
86+
} else {
87+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
88+
}
89+
{{/oneOf}}
90+
{{^oneOf}}
91+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
92+
{{/oneOf}}
8493
}
8594
{{/discriminator}}
8695
{{/useOneOfDiscriminatorLookup}}
@@ -115,7 +124,16 @@ func (dst *{{classname}}) UnmarshalJSON(data []byte) error {
115124
} else if match == 1 {
116125
return nil // exactly one match
117126
} else { // no match
118-
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
127+
{{#oneOf}}
128+
if err != nil {
129+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}}): %v", err)
130+
} else {
131+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
132+
}
133+
{{/oneOf}}
134+
{{^oneOf}}
135+
return fmt.Errorf("data failed to match schemas in oneOf({{classname}})")
136+
{{/oneOf}}
119137
}
120138
{{/useOneOfDiscriminatorLookup}}
121139
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
openapi: '3.0.3'
2+
info:
3+
title: API Test
4+
version: '1.0'
5+
paths:
6+
/foo:
7+
get:
8+
responses:
9+
'200':
10+
description: OK
11+
content:
12+
application/json:
13+
schema:
14+
type: object
15+
properties:
16+
code:
17+
oneOf:
18+
- $ref: "#/components/schemas/importCode"
19+
20+
components:
21+
schemas:
22+
importCode:
23+
type: object
24+
properties:
25+
code:
26+
type: string
27+
pattern: "^[0-9]{2,}$"
28+
29+
creditCard:
30+
description: "Visa credit card\n
31+
matches: 4123 6453 2222 1746\n
32+
non-matches: 3124 5675 4400 4567, 4123-6453-2222-1746"
33+
type: string
34+
35+
pattern: "^4[0-9]{3}\\s[0-9]{4}\\s[0-9]{4}\\s[0-9]{4}$"
36+
# Original was: 4[0-9]{3}\s[0-9]{4}\s[0-9]{4}\s[0-9]{4}
37+
38+
date:
39+
description: "Some dates\n
40+
matches: 31/04/1999, 15/12/4567\n
41+
non-matches: 31/4/1999, 31/4/99, 1999/04/19, 42/67/25456"
42+
type: string
43+
pattern: "^([0-2][0-9]|30|31)/(0[1-9]|1[0-2])/[0-9]{4}$"
44+
# Original was: ([0-2][0-9]|30|31)/(0[1-9]|1[0-2])/[0-9]{4} : unchanged
45+
46+
windowsAbsolutePath:
47+
description: "Windows absolute path\n
48+
matches: \\\\server\\share\\file\n
49+
non-matches: \\directory\\directory2, /directory2"
50+
type: string
51+
52+
# This test case doesn't work due to a problem (?) in validator.v2 (?)
53+
# it issues an unexpected unknown tag or Bad Parameter.
54+
55+
# pattern: "^([A-Za-z]:|\\)\\[[:alnum:][:whitespace:]!\"#$%&'()+,-.;=@[]^_`{}~.]*$"
56+
# Original was: ([A-Za-z]:|\\)\\[[:alnum:][:whitespace:]!"#$%&'()+,-.\\;=@\[\]^_`{}~.]*
57+
58+
email1:
59+
description: "Email Address 1\n
60+
matches: abc.123@def456.com, _123@abc.ca\n
61+
non-matches: abc@dummy, ab*cd@efg.hijkl"
62+
type: string
63+
64+
pattern: "^[[:word:]\\-.]+@[[:word:]\\-.]+\\.[[:alpha:]]{2,3}$"
65+
# Original was: [[:word:]\-.]+@[[:word:]\-.]+\.[[:alpha:]]{2,3}
66+
67+
email2:
68+
description: "Email Address 2\n
69+
matches: *@qrstuv@wxyz.12345.com, __1234^%@@abc.def.ghijkl\n
70+
non-matches: abc.123.*&ca, ^%abcdefg123"
71+
type: string
72+
73+
pattern: "^.+@.+\\..+$"
74+
# Original was: .+@.+\..+
75+
76+
htmlHexadecimalColorCode1:
77+
description: "HTML Hexadecimal Color Code 1\n
78+
matches: AB1234, CCCCCC, 12AF3B\n
79+
non-matches: 123G45, 12-44-CC"
80+
type: string
81+
pattern: "^[A-F0-9]{6}$"
82+
# Original was: [A-F0-9]{6} : unchanged
83+
84+
htmlHexadecimalColorCode2:
85+
description: "HTML Hexadecimal Color Code 2\n
86+
matches: AB 11 00, CC 12 D3\n
87+
non-matches: SS AB CD, AA BB CC DD, 1223AB"
88+
type: string
89+
90+
pattern: "^[A-F0-9]{2}\\s[A-F0-9]{2}\\s[A-F0-9]{2}$"
91+
# Original was: [A-F0-9]{2}\s[A-F0-9]{2}\s[A-F0-9]{2}
92+
93+
ipAddress:
94+
description: "IP Address\n
95+
matches: 10.25.101.216\n
96+
non-matches: 0.0.0, 256.89.457.02"
97+
type: string
98+
99+
pattern: "^((2(5[0-5]|[0-4][0-9])|1([0-9][0-9])|([1-9][0-9])|[0-9])\\.){3}(2(5[0-5]|[0-4][0-9])|1([0-9][0-9])|([1-9][0-9])|[0-9])$"
100+
# Original was: ((2(5[0-5]|[0-4][0-9])|1([0-9][0-9])|([1-9][0-9])|[0-9])\.){3}(2(5[0-5]|[0-4][0-9])|1([0-9][0-9])|([1-9][0-9])|[0-9])
101+
102+
javaComments:
103+
description: "Java Comments\n
104+
matches: Matches Java comments that are between /* and */, or one line comments prefaced by //\n
105+
non-matches: a=1"
106+
type: string
107+
108+
# This test case doesn't work due to a problem (?) in validator.v2 (?)
109+
# org.yaml.snakeyaml.scanner declares \* being an invalid escape code at yaml checking step
110+
111+
# pattern: "^/\*.*\*/|//[^\\n]*$"
112+
# Original was: /\*.*\*/|//[^\n]*
113+
114+
money:
115+
description: "\n
116+
matches: $1.00, -$97.65
117+
non-matches: $1, 1.00$, $-75.17"
118+
type: string
119+
120+
pattern: "^(\\+|-)?\\$[0-9]*\\.[0-9]{2}$"
121+
# Original was: (\+|-)?\$[0-9]*\.[0-9]{2}
122+
123+
positiveNegativeDecimalValue:
124+
description: "Positive, negative numbers, and decimal values\n
125+
matches: +41, -412, 2, 7968412, 41, +41.1, -3.141592653
126+
non-matches: ++41, 41.1.19, -+97.14"
127+
type: string
128+
129+
pattern: "^(\\+|-)?[0-9]+(\\.[0-9]+)?$"
130+
# Original was: (\+|-)?[0-9]+(\.[0-9]+)?
131+
132+
password1:
133+
description: "Passwords 1\n
134+
matches: abcd, 1234, A1b2C3d4, 1a2B3\n
135+
non-matches: abc, *ab12, abcdefghijkl"
136+
type: string
137+
pattern: "^[[:alnum:]]{4,10}$"
138+
# Original was: [[:alnum:]]{4,10} : unchanged
139+
140+
password2:
141+
description: "Passwords 2\n
142+
matches: AB_cd, A1_b2c3, a123_\n
143+
non-matches: *&^g, abc, 1bcd"
144+
type: string
145+
146+
pattern: "^[a-zA-Z]\\w{3,7}$"
147+
# Original was: [a-zA-Z]\w{3,7} : unchanged
148+
149+
phoneNumber:
150+
description: "Phone Numbers\n
151+
matches: 519-883-6898, 519 888 6898\n
152+
non-matches: 888 6898, 5198886898, 519 883-6898"
153+
type: string
154+
155+
pattern: "^([2-9][0-9]{2}-[2-9][0-9]{2}-[0-9]{4})|([2-9][0-9]{2}\\s[2-9][0-9]{2}\\s[0-9]{4})$"
156+
# Original was: ([2-9][0-9]{2}-[2-9][0-9]{2}-[0-9]{4})|([2-9][0-9]{2}\s[2-9][0-9]{2}\s[0-9]{4})
157+
158+
sentence1:
159+
description: "Sentences 1\n
160+
matches: Hello, how are you?\n
161+
non-matches: i am fine"
162+
type: string
163+
164+
pattern: "^[A-Z0-9].*(\\.|\\?|!)$"
165+
# Original was: [A-Z0-9].*(\.|\?|!)
166+
167+
sentence2:
168+
description: "Sentences 2\n
169+
matches: Hello, how are you?n
170+
non-matches: i am fine"
171+
type: string
172+
pattern: "^[[:upper:]0-9].*[.?!]$"
173+
# Original was: [[:upper:]0-9].*[.?!] : unchanged
174+
175+
socialSecurityNumber:
176+
description: "Social Security Number\n
177+
matches: 123-45-6789\n
178+
non-matches: 123 45 6789, 123456789, 1234-56-7891"
179+
type: string
180+
pattern: "^[0-9]{3}-[0-9]{2}-[0-9]{4}$"
181+
# Original was: [0-9]{3}-[0-9]{2}-[0-9]{4} : unchanged
182+
183+
url:
184+
description: "URL\n
185+
matches: http://www.sample.com, www.sample.com\n
186+
non-matches: http://sample.com, http://www.sample.comm"
187+
type: string
188+
189+
# \. ==> \\.
190+
pattern: "^(http://)?www\\.[a-zA-Z0-9]+\\.[a-zA-Z]{2,3}$"
191+
# Original was: (http://)?www\.[a-zA-Z0-9]+\.[a-zA-Z]{2,3}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
10+
# Architecture specific extensions/prefixes
11+
*.[568vq]
12+
[568vq].out
13+
14+
*.cgo1.go
15+
*.cgo2.c
16+
_cgo_defun.c
17+
_cgo_gotypes.go
18+
_cgo_export.*
19+
20+
_testmain.go
21+
22+
*.exe
23+
*.test
24+
*.prof
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
# As an example, the C# client generator defines ApiClient.cs.
8+
# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
9+
#ApiClient.cs
10+
11+
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
12+
#foo/*/qux
13+
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
14+
15+
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
16+
#foo/**/qux
17+
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
18+
19+
# You can also negate patterns with an exclamation (!).
20+
# For example, you can ignore all files in a docs folder with the file extension .md:
21+
#docs/*.md
22+
# Then explicitly reverse the ignore rule for a single file:
23+
#!docs/README.md

0 commit comments

Comments
 (0)