Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,13 @@


import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;

public class SwaggerConverter implements SwaggerParserExtension {

private static final Set<String> STRIPPED_EXTENSION_KEYS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("x-example", "x-examples", "x-nullable")));

private List<String> globalConsumes = new ArrayList<>();
private List<String> globalProduces = new ArrayList<>();
private Components components = new Components();
Expand Down Expand Up @@ -654,14 +652,17 @@ public Operation convert(io.swagger.models.Operation v2Operation) {
}

private Map<String, Object> convert(Map<String, Object> vendorExtensions) {
if (vendorExtensions != null && vendorExtensions.size() > 0) {
vendorExtensions.entrySet().removeIf(extension -> (
extension.getKey().equals("x-example")) ||
extension.getKey().equals("x-examples") ||
extension.getKey().equals("x-nullable"));
if (vendorExtensions == null || vendorExtensions.isEmpty()) {
return vendorExtensions;
}

return vendorExtensions;
Map<String, Object> result = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : vendorExtensions.entrySet()) {
if (!STRIPPED_EXTENSION_KEYS.contains(entry.getKey())) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}

private Schema convertFormDataToSchema(io.swagger.models.parameters.Parameter formParam) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public class V2ConverterTest {

private static final String ISSUE_1767_YAML = "issue-1767.yaml";
private static final String ISSUE_1796_YAML = "issue-1796.yaml";
private static final String ISSUE_2269_YAML = "issue-2269.yaml";

private static final String API_BATCH_PATH = "/api/batch/";
private static final String PETS_PATH = "/pets";
Expand Down Expand Up @@ -928,6 +929,42 @@ public void testConvertFormDataAsObjectSchema() throws Exception {

}

@Test
public void testIssue2269SharedResponseNullable() {
SwaggerConverter converter = new SwaggerConverter();
ParseOptions parseOptions = new ParseOptions();
parseOptions.setResolve(true);
parseOptions.setResolveFully(true);
SwaggerParseResult result = converter.readLocation(
"src/test/resources/" + ISSUE_2269_YAML, null, parseOptions);
assertNotNull(result);
OpenAPI oas = result.getOpenAPI();
assertNotNull(oas);

for (String endpoint : Arrays.asList("/endpoint1", "/endpoint2")) {
Map<String, Schema> properties = oas.getPaths().get(endpoint).getGet()
.getResponses().get("200").getContent().values().iterator().next()
.getSchema().getProperties();
assertNotNull(properties, endpoint + " schema properties must not be null");

Schema nullableField = properties.get("nullable_field");
assertNotNull(nullableField, endpoint + " must have nullable_field");
assertEquals(nullableField.getNullable(), Boolean.TRUE,
endpoint + " nullable_field must have nullable: true");
assertNotNull(nullableField.getExtensions(), endpoint + " nullable_field must retain extensions");
assertEquals(nullableField.getExtensions().get("x-internal-note"), "abc",
endpoint + " nullable_field must retain x-internal-note extension");

Schema nonNullableField = properties.get("non_nullable_field");
assertNotNull(nonNullableField, endpoint + " must have non_nullable_field");
assertEquals(nonNullableField.getNullable(), Boolean.FALSE,
endpoint + " non_nullable_field must have nullable: false");
assertNotNull(nonNullableField.getExtensions(), endpoint + " non_nullable_field must retain extensions");
assertEquals(nonNullableField.getExtensions().get("x-internal-note"), "def",
endpoint + " non_nullable_field must retain x-internal-note extension");
}
}

/**
* A clone (almost) of {@link OpenAPIV3Parser#readContents(String, List, ParseOptions)}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
swagger: '2.0'
info:
version: 1.0.0
title: Issue 2269 - x-nullable preservation for shared $ref responses

responses:
SharedResponse:
description: A shared response with multiple fields using x-nullable
schema:
type: object
properties:
data:
type: string
description: Required field
nullable_field:
type: integer
x-nullable: true
x-internal-note: abc
non_nullable_field:
type: integer
x-nullable: false
x-internal-note: def
required:
- data

paths:
/endpoint1:
get:
operationId: endpoint1
summary: First endpoint using shared response
responses:
'200':
$ref: '#/responses/SharedResponse'

/endpoint2:
get:
operationId: endpoint2
summary: Second endpoint using shared response
responses:
'200':
$ref: '#/responses/SharedResponse'
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,32 @@ public void testIssue1552() throws Exception {
assertNotNull(schema.getProperties().get("foo"));
}

@Test(description = "Issue 2269: preserve x-nullable in shared responses for swagger 2.0 specs")
public void testSwagger2SharedResponseNullable() {
ParseOptions options = new ParseOptions();
options.setResolve(true);
options.setResolveFully(true);

SwaggerParseResult result = new OpenAPIParser().readLocation("issue2269.yaml", null, options);

assertNotNull(result);
assertNotNull(result.getOpenAPI());
OpenAPI openAPI = result.getOpenAPI();

Schema endpoint1Field = (Schema) openAPI.getPaths().get("/endpoint1").getGet()
.getResponses().get("200").getContent().values().iterator().next()
.getSchema().getProperties().get("optional_field");
assertNotNull(endpoint1Field, "Endpoint 1 should have optional_field");
assertEquals(endpoint1Field.getNullable(), Boolean.TRUE, "Endpoint 1 optional_field should be nullable");

Schema endpoint2Field = (Schema) openAPI.getPaths().get("/endpoint2").getGet()
.getResponses().get("200").getContent().values().iterator().next()
.getSchema().getProperties().get("optional_field");
assertNotNull(endpoint2Field, "Endpoint 2 should have optional_field");
assertEquals(endpoint2Field.getNullable(), Boolean.TRUE, "Endpoint 2 optional_field should be nullable");

}

@org.testng.annotations.Test(description = "convert response schema")
public void testIssue1552AdditionalProps() throws Exception {
ParseOptions options = new ParseOptions();
Expand Down
38 changes: 38 additions & 0 deletions modules/swagger-parser/src/test/resources/issue2269.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
swagger: '2.0'
info:
version: 1.0.0
title: Swagger 2.0 Shared Response Test
description: Test case for verifying x-nullable is preserved when multiple endpoints share the same response definition

responses:
SharedResponse:
description: A shared response with nullable field
schema:
type: object
properties:
data:
type: string
description: Required field
optional_field:
type: integer
description: Optional nullable field
x-nullable: true
required:
- data

paths:
/endpoint1:
get:
operationId: endpoint1
summary: First endpoint using shared response
responses:
'200':
$ref: '#/responses/SharedResponse'

/endpoint2:
get:
operationId: endpoint2
summary: Second endpoint using shared response
responses:
'200':
$ref: '#/responses/SharedResponse'
Loading