Skip to content

Commit 3756045

Browse files
committed
fix(converter): prevent x-nullable loss for shared $ref responses
The convert(vendorExtensions) method mutated the original map when filtering out x-nullable, x-example, and x-examples extensions. When multiple endpoints referenced the same response via $ref, the first conversion removed x-nullable from the shared source object, causing subsequent conversions to miss it and produce nullable=null. Create a new map instead of mutating the original to preserve vendor extensions for all endpoints sharing the same response definition.
1 parent c7f8b94 commit 3756045

3 files changed

Lines changed: 72 additions & 4 deletions

File tree

modules/swagger-parser-v2-converter/src/main/java/io/swagger/v3/parser/converter/SwaggerConverter.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,10 +655,14 @@ public Operation convert(io.swagger.models.Operation v2Operation) {
655655

656656
private Map<String, Object> convert(Map<String, Object> vendorExtensions) {
657657
if (vendorExtensions != null && vendorExtensions.size() > 0) {
658-
vendorExtensions.entrySet().removeIf(extension -> (
659-
extension.getKey().equals("x-example")) ||
660-
extension.getKey().equals("x-examples") ||
661-
extension.getKey().equals("x-nullable"));
658+
Map<String, Object> result = new LinkedHashMap<>();
659+
for (Map.Entry<String, Object> entry : vendorExtensions.entrySet()) {
660+
String key = entry.getKey();
661+
if (!key.equals("x-example") && !key.equals("x-examples") && !key.equals("x-nullable")) {
662+
result.put(key, entry.getValue());
663+
}
664+
}
665+
return result;
662666
}
663667

664668
return vendorExtensions;

modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,32 @@ public void testIssue1552() throws Exception {
686686
assertNotNull(schema.getProperties().get("foo"));
687687
}
688688

689+
@Test(description = "Issue 2269: preserve x-nullable in shared responses for swagger 2.0 specs")
690+
public void testSwagger2SharedResponseNullable() {
691+
ParseOptions options = new ParseOptions();
692+
options.setResolve(true);
693+
options.setResolveFully(true);
694+
695+
SwaggerParseResult result = new OpenAPIParser().readLocation("issue2269.yaml", null, options);
696+
697+
assertNotNull(result);
698+
assertNotNull(result.getOpenAPI());
699+
OpenAPI openAPI = result.getOpenAPI();
700+
701+
Schema endpoint1Field = (Schema) openAPI.getPaths().get("/endpoint1").getGet()
702+
.getResponses().get("200").getContent().values().iterator().next()
703+
.getSchema().getProperties().get("optional_field");
704+
assertNotNull(endpoint1Field, "Endpoint 1 should have optional_field");
705+
assertEquals(endpoint1Field.getNullable(), Boolean.TRUE, "Endpoint 1 optional_field should be nullable");
706+
707+
Schema endpoint2Field = (Schema) openAPI.getPaths().get("/endpoint2").getGet()
708+
.getResponses().get("200").getContent().values().iterator().next()
709+
.getSchema().getProperties().get("optional_field");
710+
assertNotNull(endpoint2Field, "Endpoint 2 should have optional_field");
711+
assertEquals(endpoint2Field.getNullable(), Boolean.TRUE, "Endpoint 2 optional_field should be nullable");
712+
713+
}
714+
689715
@org.testng.annotations.Test(description = "convert response schema")
690716
public void testIssue1552AdditionalProps() throws Exception {
691717
ParseOptions options = new ParseOptions();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
swagger: '2.0'
2+
info:
3+
version: 1.0.0
4+
title: Swagger 2.0 Shared Response Test
5+
description: Test case for verifying x-nullable is preserved when multiple endpoints share the same response definition
6+
7+
responses:
8+
SharedResponse:
9+
description: A shared response with nullable field
10+
schema:
11+
type: object
12+
properties:
13+
data:
14+
type: string
15+
description: Required field
16+
optional_field:
17+
type: integer
18+
description: Optional nullable field
19+
x-nullable: true
20+
required:
21+
- data
22+
23+
paths:
24+
/endpoint1:
25+
get:
26+
operationId: endpoint1
27+
summary: First endpoint using shared response
28+
responses:
29+
'200':
30+
$ref: '#/responses/SharedResponse'
31+
32+
/endpoint2:
33+
get:
34+
operationId: endpoint2
35+
summary: Second endpoint using shared response
36+
responses:
37+
'200':
38+
$ref: '#/responses/SharedResponse'

0 commit comments

Comments
 (0)