Skip to content

Commit 9c19b19

Browse files
authored
fix: use explicit prefix for array items with dotted property names (#2330)
1 parent 4dfb6fa commit 9c19b19

4 files changed

Lines changed: 117 additions & 13 deletions

File tree

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/InlineModelResolver.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,7 @@ private void fixStringModel(Schema m) {
393393
}
394394
}
395395
}
396-
396+
397397
private static String pathBody(String pathname)
398398
{
399399
String[] parts = pathname.split("/");
@@ -409,7 +409,7 @@ private static String pathBody(String pathname)
409409
body.append("body");
410410
return body.toString();
411411
}
412-
412+
413413
private static String normalize(String pathPart)
414414
{
415415
return pathPart.replace(".", "_");
@@ -486,7 +486,7 @@ public void flattenProperties(Map<String, Schema> properties, String path) {
486486
Schema model = createModelFromProperty(property, modelName);
487487
String existing = matchGenerated(model);
488488
if (existing != null) {
489-
propsToUpdate.put(key, new Schema().$ref(existing));
489+
propsToUpdate.put(key, new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+existing));
490490
} else {
491491
propsToUpdate.put(key, new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+modelName));
492492
modelsToAdd.put(modelName, model);
@@ -503,9 +503,9 @@ public void flattenProperties(Map<String, Schema> properties, String path) {
503503
Schema innerModel = createModelFromProperty(inner, modelName);
504504
String existing = matchGenerated(innerModel);
505505
if (existing != null) {
506-
ap.setItems(new Schema().$ref(existing));
506+
ap.setItems(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix() + existing));
507507
} else {
508-
ap.setItems(new Schema().$ref(modelName));
508+
ap.setItems(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix() + modelName));
509509
addGenerated(modelName, innerModel);
510510
openAPI.getComponents().addSchemas(modelName, innerModel);
511511
}
@@ -515,9 +515,9 @@ public void flattenProperties(Map<String, Schema> properties, String path) {
515515
Schema innerModel = createModelFromProperty(inner, modelName);
516516
String existing = matchGenerated(innerModel);
517517
if (existing != null) {
518-
ap.setItems(new Schema().$ref(existing));
518+
ap.setItems(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+existing));
519519
} else {
520-
ap.setItems(new Schema().$ref(modelName));
520+
ap.setItems(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+modelName));
521521
addGenerated(modelName, innerModel);
522522
openAPI.getComponents().addSchemas(modelName, innerModel);
523523
}
@@ -532,9 +532,9 @@ public void flattenProperties(Map<String, Schema> properties, String path) {
532532
Schema innerModel = createModelFromProperty(inner, modelName);
533533
String existing = matchGenerated(innerModel);
534534
if (existing != null) {
535-
property.setAdditionalProperties(new Schema().$ref(existing));
535+
property.setAdditionalProperties(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+existing));
536536
} else {
537-
property.setAdditionalProperties(new Schema().$ref(modelName));
537+
property.setAdditionalProperties(new Schema().$ref(RefType.SCHEMAS.getInternalPrefix()+modelName));
538538
addGenerated(modelName, innerModel);
539539
openAPI.getComponents().addSchemas(modelName, innerModel);
540540
}

modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,58 @@
4040
public class OpenAPIV3ParserTest {
4141
List<AuthorizationValue> auths = new ArrayList<>();
4242

43+
@Test
44+
public void testFlattenOptionOnDottedPropertyNames() {
45+
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
46+
ParseOptions options = new ParseOptions();
47+
options.setFlatten(true);
48+
SwaggerParseResult parseResult = openApiParser.readLocation("dotted-property-names.yaml", null, options);
49+
OpenAPI openAPI = parseResult.getOpenAPI();
50+
51+
assertNotNull(openAPI);
52+
53+
List<String> messages = parseResult.getMessages();
54+
if (messages != null) {
55+
for (String message : messages) {
56+
assertFalse(message.contains("Could not resolve reference"),
57+
"Unexpected resolver error: " + message);
58+
}
59+
}
60+
61+
Map<String, Schema> schemas = openAPI.getComponents().getSchemas();
62+
assertNotNull(schemas);
63+
assertNotNull(schemas.get("pathtoapi_object.field"), "object.field inline object must be extracted");
64+
assertNotNull(schemas.get("pathtoapi_nodot"), "nodot items object must be extracted");
65+
assertNotNull(schemas.get("pathtoapi_objectarray.field"), "objectarray.field items object must be extracted");
66+
67+
Schema bodySchema = schemas.get("to_api_body");
68+
assertNotNull(bodySchema, "request body schema must be extracted");
69+
70+
// string.field — plain string property, must remain inline (no $ref, no extraction)
71+
Schema stringField = (Schema) bodySchema.getProperties().get("string.field");
72+
assertNotNull(stringField, "string.field must be present in the body schema");
73+
assertNull(stringField.get$ref(), "string.field must not be replaced with a $ref");
74+
assertEquals(stringField.getType(), "string", "string.field must remain a string type");
75+
76+
// object.field — inline object replaced by a $ref
77+
String objectFieldRef = ((Schema) bodySchema.getProperties().get("object.field")).get$ref();
78+
assertNotNull(objectFieldRef, "object.field must have a $ref");
79+
assertTrue(objectFieldRef.startsWith("#/components/schemas/"),
80+
"object.field $ref must be a valid JSON Pointer, was: " + objectFieldRef);
81+
82+
// nodot — array of objects without a dot: items $ref must be valid
83+
String nodotRef = ((ArraySchema) bodySchema.getProperties().get("nodot")).getItems().get$ref();
84+
assertNotNull(nodotRef, "nodot items must have a $ref");
85+
assertTrue(nodotRef.startsWith("#/components/schemas/"),
86+
"nodot items $ref must be a valid JSON Pointer, was: " + nodotRef);
87+
88+
// objectarray.field — array of objects WITH a dot: items $ref must also be valid
89+
String dottedRef = ((ArraySchema) bodySchema.getProperties().get("objectarray.field")).getItems().get$ref();
90+
assertNotNull(dottedRef, "objectarray.field items must have a $ref");
91+
assertTrue(dottedRef.startsWith("#/components/schemas/"),
92+
"objectarray.field items $ref must be a valid JSON Pointer, was: " + dottedRef);
93+
}
94+
4395
@Test(description = "Issue 2223: reading 3.1 spec from Windows file path location produces URISyntaxException" )
4496
public void testWindowsFilePathRead() {
4597
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();

modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/InlineModelResolverTest.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ public void resolveInlineRequestBody() throws Exception {
653653

654654
assertNotNull(bodySchema.getProperties().get("address"));
655655
}
656-
656+
657657
@Test
658658
public void resolveInlineRequestBody_maxTwoPathParts() throws Exception {
659659
OpenAPI openAPI = new OpenAPI();
@@ -692,7 +692,7 @@ public void resolveInlineRequestBody_maxTwoPathParts() throws Exception {
692692

693693
assertNotNull(bodySchema.getProperties().get("address"));
694694
}
695-
695+
696696
@Test
697697
public void resolveInlineRequestBody_stripsDotsFromPath() throws Exception {
698698
OpenAPI openAPI = new OpenAPI();
@@ -724,8 +724,8 @@ public void resolveInlineRequestBody_stripsDotsFromPath() throws Exception {
724724

725725
Operation getOperation = openAPI.getPaths().get("/api/Cloud.Greet.Hello").getGet();
726726
RequestBody body = getOperation.getRequestBody();
727-
assertEquals("use dot as common word separator: as it occurs frequently on OData services",
728-
"#/components/schemas/ApiCloudGreetHelloBody",
727+
assertEquals("use dot as common word separator: as it occurs frequently on OData services",
728+
"#/components/schemas/ApiCloudGreetHelloBody",
729729
body.getContent().get("*/*").getSchema().get$ref());
730730

731731
Schema bodySchema = openAPI.getComponents().getSchemas().get("ApiCloudGreetHelloBody");
@@ -1509,4 +1509,5 @@ public void testSchemaPropertiesBeingPassedToFlattenedModel() {
15091509
assertEquals(userAddress.getWriteOnly(), Boolean.FALSE);
15101510

15111511
}
1512+
15121513
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
openapi: 3.0.3
2+
info:
3+
version: "4"
4+
title: dotted-property-names
5+
description: |
6+
This API has an object in the schema which has a dot in one of the property names (user.id).
7+
8+
contact:
9+
email: "test.test@smartbear.com"
10+
paths:
11+
/path/to/api:
12+
post:
13+
requestBody:
14+
content:
15+
application/json:
16+
schema:
17+
type: object
18+
properties:
19+
string.field:
20+
type: string
21+
description: String field - works with a dot in the name
22+
object.field:
23+
type: object
24+
description: Object field - works with a dot in the name
25+
properties:
26+
p1:
27+
type: string
28+
array.field:
29+
type: array
30+
description: Array of strings field - works with a dot in the name
31+
items:
32+
type: string
33+
nodot:
34+
type: array
35+
description: An array of objects - works WITHOUT a dot in the name
36+
items:
37+
type: object
38+
properties:
39+
q1:
40+
type: string
41+
objectarray.field:
42+
type: array
43+
description: An array of objects - works with a dot in the name
44+
items:
45+
type: object
46+
properties:
47+
r1:
48+
type: string
49+
responses:
50+
'200':
51+
description: 'success'

0 commit comments

Comments
 (0)