Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit 27cbe43

Browse files
authored
Merge pull request #20 from OpenAPITools/11-fix-regex-handling
Fixes regex pattern handling
2 parents aec7e6b + 9ef28e1 commit 27cbe43

File tree

4 files changed

+136
-47
lines changed

4 files changed

+136
-47
lines changed

modules/openapi-json-schema-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java

+74-45
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,9 @@ public CodegenProperty fromProperty(String name, Schema p, boolean required, boo
10411041
cp.isNullable = false;
10421042
cp.setHasMultipleTypes(true);
10431043
}
1044-
postProcessPattern(cp.pattern, cp.vendorExtensions);
1044+
if (p.getPattern() != null) {
1045+
postProcessPattern(p.getPattern(), cp.vendorExtensions);
1046+
}
10451047
// if we have a property that has a difficult name, either:
10461048
// 1. name is reserved, like class int float
10471049
// 2. name is invalid in python like '3rd' or 'Content-Type'
@@ -1485,7 +1487,11 @@ protected Object processTestExampleData(Object value) {
14851487
@Override
14861488
public CodegenModel fromModel(String name, Schema sc) {
14871489
CodegenModel cm = super.fromModel(name, sc);
1488-
1490+
if (sc.getPattern() != null) {
1491+
postProcessPattern(sc.getPattern(), cm.vendorExtensions);
1492+
String pattern = (String) cm.vendorExtensions.get("x-regex");
1493+
cm.setPattern(pattern);
1494+
}
14891495
if (cm.isNullable) {
14901496
cm.setIsNull(true);
14911497
cm.isNullable = false;
@@ -1891,34 +1897,21 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
18911897
example = "2";
18921898
} else if (StringUtils.isNotBlank(schema.getPattern())) {
18931899
String pattern = schema.getPattern();
1900+
List<Object> results = getPatternAndModifiers(pattern);
1901+
String extractedPattern = (String) results.get(0);
1902+
List<String> regexFlags = (List<String>) results.get(1);
18941903
/*
18951904
RxGen does not support our ECMA dialect https://github.com/curious-odd-man/RgxGen/issues/56
18961905
So strip off the leading / and trailing / and turn on ignore case if we have it
18971906
*/
1898-
Pattern valueExtractor = Pattern.compile("^/?(.+?)/?(.?)$");
1899-
Matcher m = valueExtractor.matcher(pattern);
19001907
RgxGen rgxGen = null;
1901-
if (m.find()) {
1902-
int groupCount = m.groupCount();
1903-
if (groupCount == 1) {
1904-
// only pattern found
1905-
String isolatedPattern = m.group(1);
1906-
rgxGen = new RgxGen(isolatedPattern);
1907-
} else if (groupCount == 2) {
1908-
// patterns and flag found
1909-
String isolatedPattern = m.group(1);
1910-
String flags = m.group(2);
1911-
if (flags.contains("i")) {
1912-
rgxGen = new RgxGen(isolatedPattern);
1913-
RgxGenProperties properties = new RgxGenProperties();
1914-
RgxGenOption.CASE_INSENSITIVE.setInProperties(properties, true);
1915-
rgxGen.setProperties(properties);
1916-
} else {
1917-
rgxGen = new RgxGen(isolatedPattern);
1918-
}
1919-
}
1908+
if (regexFlags.size() > 0 && regexFlags.contains("i")) {
1909+
rgxGen = new RgxGen(extractedPattern);
1910+
RgxGenProperties properties = new RgxGenProperties();
1911+
RgxGenOption.CASE_INSENSITIVE.setInProperties(properties, true);
1912+
rgxGen.setProperties(properties);
19201913
} else {
1921-
rgxGen = new RgxGen(pattern);
1914+
rgxGen = new RgxGen(extractedPattern);
19221915
}
19231916

19241917
// this seed makes it so if we have [a-z] we pick a
@@ -2358,6 +2351,16 @@ protected void updatePropertyForString(CodegenProperty property, Schema p) {
23582351
property.pattern = toRegularExpression(p.getPattern());
23592352
}
23602353

2354+
@Override
2355+
public String toRegularExpression(String pattern) {
2356+
if (pattern == null) {
2357+
return null;
2358+
}
2359+
List<Object> results = getPatternAndModifiers(pattern);
2360+
String extractedPattern = (String) results.get(0);
2361+
return extractedPattern;
2362+
}
2363+
23612364
protected void updatePropertyForNumber(CodegenProperty property, Schema p) {
23622365
property.setIsNumber(true);
23632366
// float and double differentiation is determined with format info
@@ -2482,35 +2485,61 @@ public ModelsMap postProcessModels(ModelsMap objs) {
24822485
return postProcessModelsEnum(objs);
24832486
}
24842487

2488+
/**
2489+
* @param pattern the regex pattern
2490+
* @return List<String pattern, List<String modifer>>
2491+
*/
2492+
private List<Object> getPatternAndModifiers(String pattern) {
2493+
/*
2494+
Notes:
2495+
RxGen does not support our ECMA dialect https://github.com/curious-odd-man/RgxGen/issues/56
2496+
So strip off the leading / and trailing / and turn on ignore case if we have it
2497+
2498+
json schema test cases omit the leading and trailing /s, so make sure that the regex allows that
2499+
*/
2500+
Pattern valueExtractor = Pattern.compile("^/?(.+?)/?([simu]{0,4})$");
2501+
Matcher m = valueExtractor.matcher(pattern);
2502+
if (m.find()) {
2503+
int groupCount = m.groupCount();
2504+
if (groupCount == 1) {
2505+
// only pattern found
2506+
String isolatedPattern = m.group(1);
2507+
return Arrays.asList(isolatedPattern, null);
2508+
} else if (groupCount == 2) {
2509+
List<String> modifiers = new ArrayList<String>();
2510+
// patterns and flag found
2511+
String isolatedPattern = m.group(1);
2512+
String flags = m.group(2);
2513+
if (flags.contains("s")) {
2514+
modifiers.add("DOTALL");
2515+
}
2516+
if (flags.contains("i")) {
2517+
modifiers.add("IGNORECASE");
2518+
}
2519+
if (flags.contains("m")) {
2520+
modifiers.add("MULTILINE");
2521+
}
2522+
return Arrays.asList(isolatedPattern, modifiers);
2523+
}
2524+
}
2525+
return Arrays.asList(pattern, new ArrayList<String>());
2526+
}
2527+
24852528
/*
24862529
* The OpenAPI pattern spec follows the Perl convention and style of modifiers. Python
24872530
* does not support this in as natural a way so it needs to convert it. See
24882531
* https://docs.python.org/2/howto/regex.html#compilation-flags for details.
24892532
*/
24902533
public void postProcessPattern(String pattern, Map<String, Object> vendorExtensions) {
24912534
if (pattern != null) {
2492-
int regexLength = pattern.length();
2493-
String regex = pattern;
2494-
int i = pattern.lastIndexOf('/');
2495-
if (regexLength >= 2 && pattern.charAt(0) == '/' && i != -1) {
2496-
// json schema tests do not include the leading and trailing slashes
2497-
// so I do not think that they are required
2498-
regex = pattern.substring(1, i);
2499-
}
2500-
regex = regex.replace("'", "\\'");
2501-
List<String> modifiers = new ArrayList<String>();
2502-
2503-
if (i != -1) {
2504-
for (char c : pattern.substring(i).toCharArray()) {
2505-
if (regexModifiers.containsKey(c)) {
2506-
String modifier = regexModifiers.get(c);
2507-
modifiers.add(modifier);
2508-
}
2509-
}
2510-
}
2535+
List<Object> results = getPatternAndModifiers(pattern);
2536+
String extractedPattern = (String) results.get(0);
2537+
List<String> modifiers = (List<String>) results.get(1);
25112538

2512-
vendorExtensions.put("x-regex", regex);
2513-
vendorExtensions.put("x-modifiers", modifiers);
2539+
vendorExtensions.put("x-regex", extractedPattern);
2540+
if (modifiers.size() > 0) {
2541+
vendorExtensions.put("x-modifiers", modifiers);
2542+
}
25142543
}
25152544
}
25162545

modules/openapi-json-schema-generator/src/test/java/org/openapitools/codegen/python/PythonClientTest.java

+33
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import java.nio.charset.StandardCharsets;
3333
import java.nio.file.Files;
3434
import java.nio.file.Path;
35+
import java.util.Arrays;
3536
import java.util.Collections;
3637
import java.util.List;
3738
import java.util.Map;
@@ -164,4 +165,36 @@ public void testApisNotGenerated() throws Exception {
164165
Assert.assertFalse(Files.isDirectory(pathThatShouldNotExist));
165166
output.deleteOnExit();
166167
}
168+
169+
@Test
170+
public void testRegexWithoutTrailingSlashWorks() {
171+
OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/11_regex.yaml");
172+
PythonClientCodegen codegen = new PythonClientCodegen();
173+
codegen.setOpenAPI(openAPI);
174+
175+
String modelName = "UUID";
176+
Schema schema = openAPI.getComponents().getSchemas().get(modelName);
177+
178+
CodegenModel cm = codegen.fromModel(modelName, schema);
179+
String expectedRegexPattern = "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}";
180+
Assert.assertEquals(cm.getPattern(), expectedRegexPattern);
181+
Assert.assertEquals(cm.vendorExtensions.get("x-regex"), expectedRegexPattern);
182+
Assert.assertFalse(cm.vendorExtensions.containsKey("x-modifiers"));
183+
}
184+
185+
@Test
186+
public void testRegexWithMultipleFlagsWorks() {
187+
OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/11_regex.yaml");
188+
PythonClientCodegen codegen = new PythonClientCodegen();
189+
codegen.setOpenAPI(openAPI);
190+
191+
String modelName = "StringWithRegexWithThreeFlags";
192+
Schema schema = openAPI.getComponents().getSchemas().get(modelName);
193+
194+
CodegenModel cm = codegen.fromModel(modelName, schema);
195+
String expectedRegexPattern = "a.";
196+
Assert.assertEquals(cm.getPattern(), expectedRegexPattern);
197+
Assert.assertEquals(cm.vendorExtensions.get("x-regex"), expectedRegexPattern);
198+
Assert.assertEquals(cm.vendorExtensions.get("x-modifiers"), Arrays.asList("DOTALL", "IGNORECASE", "MULTILINE"));
199+
}
167200
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
openapi: 3.0.3
2+
info:
3+
title: Test
4+
version: 1.0.0-SNAPSHOT
5+
paths:
6+
/test:
7+
get:
8+
tags:
9+
- Test Resource
10+
parameters:
11+
- name: uuid
12+
in: query
13+
schema:
14+
$ref: '#/components/schemas/UUID'
15+
responses:
16+
"200":
17+
description: OK
18+
components:
19+
schemas:
20+
UUID:
21+
format: uuid
22+
pattern: "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}"
23+
type: string
24+
StringWithRegexWithThreeFlags:
25+
format: uuid
26+
pattern: "/a./sim"
27+
type: string

samples/openapi3/client/petstore/python/docs/apis/tags/FakeApi.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -949,8 +949,8 @@ with petstore_api.ApiClient(configuration) as api_client:
949949
number=32.1,
950950
_float=3.14,
951951
double=67.8,
952-
string="A",
953-
pattern_without_delimiter="Aj",
952+
string="a",
953+
pattern_without_delimiter="AUR,rZ#UM/?R,Fp^l6$ARjbhJk C>",
954954
byte='YQ==',
955955
binary=open('/path/to/file', 'rb'),
956956
date="1970-01-01",

0 commit comments

Comments
 (0)