Skip to content

Commit b4c52dd

Browse files
authored
Merge pull request #1343 from swagger-api/issue-1292
Fixing remote paths, response schema resolution - Issue 1292
2 parents e172a00 + 99bdc74 commit b4c52dd

File tree

12 files changed

+227
-20
lines changed

12 files changed

+227
-20
lines changed

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import io.swagger.v3.oas.models.Components;
55
import io.swagger.v3.oas.models.OpenAPI;
6+
import io.swagger.v3.oas.models.Operation;
7+
import io.swagger.v3.oas.models.PathItem;
68
import io.swagger.v3.oas.models.callbacks.Callback;
79
import io.swagger.v3.oas.models.examples.Example;
810
import io.swagger.v3.oas.models.headers.Header;
@@ -27,6 +29,7 @@
2729
import org.slf4j.LoggerFactory;
2830

2931
import java.net.URI;
32+
import java.nio.file.Path;
3033
import java.util.Collection;
3134
import java.util.LinkedHashMap;
3235
import java.util.Map;
@@ -83,13 +86,15 @@ public String processRefToExternalSchema(String $ref, RefFormat refFormat) {
8386
// use the new model
8487
existingModel = null;
8588
}else{
86-
//We add a number at the end of the definition name
87-
int i = 2;
88-
for (String name : schemas.keySet()) {
89-
if (name.equals(possiblyConflictingDefinitionName)) {
90-
tryName = possiblyConflictingDefinitionName + "_" + i;
91-
existingModel = schemas.get(tryName);
92-
i++;
89+
if (!schema.equals(existingModel)){
90+
//We add a number at the end of the definition name
91+
int i = 2;
92+
for (String name : schemas.keySet()) {
93+
if (name.equals(possiblyConflictingDefinitionName)) {
94+
tryName = possiblyConflictingDefinitionName + "_" + i;
95+
existingModel = schemas.get(tryName);
96+
i++;
97+
}
9398
}
9499
}
95100
}
@@ -116,6 +121,7 @@ public String processRefToExternalSchema(String $ref, RefFormat refFormat) {
116121
}
117122
}
118123

124+
119125
if(schema instanceof ComposedSchema){
120126
ComposedSchema composedSchema = (ComposedSchema) schema;
121127
if (composedSchema.getAllOf() != null){
@@ -226,6 +232,84 @@ private void processProperties(Map<String, Schema> properties, String file) {
226232
}
227233
}
228234

235+
236+
public PathItem processRefToExternalPathItem(String $ref, RefFormat refFormat) {
237+
238+
final PathItem pathItem = cache.loadRef($ref, refFormat, PathItem.class);
239+
240+
String newRef;
241+
242+
Map<String, PathItem> paths = openAPI.getPaths();
243+
244+
if (paths == null) {
245+
paths = new LinkedHashMap<>();
246+
}
247+
248+
final String possiblyConflictingDefinitionName = computeDefinitionName($ref);
249+
250+
PathItem existingPathItem = paths.get(possiblyConflictingDefinitionName);
251+
252+
if (existingPathItem != null) {
253+
LOGGER.debug("A model for " + existingPathItem + " already exists");
254+
if(existingPathItem.get$ref() != null) {
255+
// use the new model
256+
existingPathItem = null;
257+
}
258+
}
259+
newRef = possiblyConflictingDefinitionName;
260+
cache.putRenamedRef($ref, newRef);
261+
262+
if(pathItem != null) {
263+
if(pathItem.readOperationsMap() != null) {
264+
final Map<PathItem.HttpMethod, Operation> operationMap = pathItem.readOperationsMap();
265+
for (PathItem.HttpMethod httpMethod : operationMap.keySet()) {
266+
Operation operation = operationMap.get(httpMethod);
267+
if (operation.getResponses() != null) {
268+
final Map<String, ApiResponse> responses = operation.getResponses();
269+
if (responses != null) {
270+
for (String responseCode : responses.keySet()) {
271+
ApiResponse response = responses.get(responseCode);
272+
if (response != null) {
273+
Schema schema = null;
274+
if (response.getContent() != null) {
275+
Map<String, MediaType> content = response.getContent();
276+
for (String mediaName : content.keySet()) {
277+
MediaType mediaType = content.get(mediaName);
278+
if (mediaType.getSchema() != null) {
279+
schema = mediaType.getSchema();
280+
if (schema != null) {
281+
processRefSchemaObject(mediaType.getSchema(), $ref);
282+
}
283+
}
284+
}
285+
}
286+
}
287+
}
288+
}
289+
}
290+
if (operation.getRequestBody() != null) {
291+
RequestBody body = operation.getRequestBody();
292+
Schema schema = null;
293+
if (body.getContent() != null) {
294+
Map<String, MediaType> content = body.getContent();
295+
for (String mediaName : content.keySet()) {
296+
MediaType mediaType = content.get(mediaName);
297+
if (mediaType.getSchema() != null) {
298+
schema = mediaType.getSchema();
299+
if (schema != null) {
300+
processRefSchemaObject(mediaType.getSchema(), $ref);
301+
}
302+
}
303+
}
304+
}
305+
}
306+
}
307+
}
308+
}
309+
310+
return pathItem;
311+
}
312+
229313
private void processDiscriminator(Discriminator d, String file) {
230314
if (d != null && d.getMapping() != null) {
231315
processDiscriminatorMapping(d.getMapping(), file);
@@ -239,6 +323,7 @@ private void processDiscriminatorMapping(Map<String, String> mapping, String fil
239323
processSchema(subtype, file);
240324
mapping.put(key, subtype.get$ref());
241325
}
326+
242327
}
243328

244329
public String processRefToExternalResponse(String $ref, RefFormat refFormat) {
@@ -716,6 +801,7 @@ public String processRefToExternalCallback(String $ref, RefFormat refFormat) {
716801
return newRef;
717802
}
718803

804+
719805
private void processRefContent(Map<String, MediaType> content, String $ref) {
720806
for(MediaType mediaType : content.values()) {
721807
if(mediaType.getSchema() != null) {
@@ -802,6 +888,8 @@ private void processRefLinks(Map<String, Link> links, String $ref) {
802888
}
803889
}
804890

891+
892+
805893
private void processRefSchema(Schema subRef, String externalFile) {
806894
RefFormat format = computeRefFormat(subRef.get$ref());
807895

@@ -897,4 +985,5 @@ else if("".equals(uri.getPath()) && !fragment.startsWith("/")) {
897985
}
898986

899987

988+
900989
}

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424

2525
import static io.swagger.v3.parser.util.RefUtils.computeRefFormat;
26+
import static io.swagger.v3.parser.util.RefUtils.isAnExternalRefFormat;
2627

2728
public class PathsProcessor {
2829

@@ -31,6 +32,7 @@ public class PathsProcessor {
3132
private final OpenAPIResolver.Settings settings;
3233
private final ParameterProcessor parameterProcessor;
3334
private final OperationProcessor operationProcessor;
35+
private final ExternalRefProcessor externalRefProcessor;
3436

3537
public PathsProcessor(ResolverCache cache, OpenAPI openAPI) {
3638
this(cache, openAPI, new OpenAPIResolver.Settings());
@@ -41,6 +43,7 @@ public PathsProcessor(ResolverCache cache, OpenAPI openAPI, OpenAPIResolver.Sett
4143
this.settings = settings;
4244
parameterProcessor = new ParameterProcessor(cache, openAPI);
4345
operationProcessor = new OperationProcessor(cache, openAPI);
46+
this.externalRefProcessor = new ExternalRefProcessor(cache, openAPI);
4447
}
4548

4649
public void processPaths() {
@@ -56,10 +59,8 @@ public void processPaths() {
5659
addParametersToEachOperation(pathItem);
5760

5861
if (pathItem.get$ref() != null) {
59-
RefFormat refFormat = computeRefFormat(pathItem.get$ref());
60-
PathItem resolvedPath = cache.loadRef(pathItem.get$ref(), refFormat, PathItem.class);
6162

62-
// TODO: update references to the parent location
63+
PathItem resolvedPath = processReferencePath(pathItem);
6364

6465
String pathRef = pathItem.get$ref().split("#")[0];
6566

@@ -290,4 +291,16 @@ protected boolean isLocalRef(String ref) {
290291
protected String computeLocalRef(String ref, String prefix) {
291292
return prefix + ref;
292293
}
294+
295+
public PathItem processReferencePath(PathItem pathItem){
296+
RefFormat refFormat = computeRefFormat(pathItem.get$ref());
297+
String $ref = pathItem.get$ref();
298+
if (isAnExternalRefFormat(refFormat)){
299+
pathItem = externalRefProcessor.processRefToExternalPathItem($ref, refFormat);
300+
}else{
301+
pathItem = cache.loadRef(pathItem.get$ref(), refFormat, PathItem.class);
302+
}
303+
304+
return pathItem;
305+
}
293306
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33

44

5+
import io.swagger.v3.core.util.Yaml;
56
import io.swagger.v3.oas.models.OpenAPI;
67
import io.swagger.v3.oas.models.Operation;
78
import io.swagger.v3.oas.models.PathItem;
@@ -78,6 +79,7 @@ public void testIssue312() {
7879

7980
Operation get = path.getGet();
8081
assertEquals(get.getOperationId(), "getEvents");
82+
Yaml.prettyPrint(swagger);
8183
assertTrue(swagger.getComponents().getSchemas().size() == 2);
8284
assertTrue(swagger.getComponents().getSchemas().get("Paging").getProperties().size() == 1);
8385
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ public void componentsResolver() throws Exception {
232232

233233
//remote url schema
234234
Schema user = (Schema) pet.getProperties().get("user");
235-
assertEquals(user.get$ref(),"#/components/schemas/User_2");
235+
assertEquals(user.get$ref(),"#/components/schemas/User");
236236

237237

238238
//ArraySchema items
@@ -247,7 +247,7 @@ public void componentsResolver() throws Exception {
247247
//Schema additionalProperties
248248
assertTrue(schemas.get("OrderRef").getAdditionalProperties() instanceof Schema);
249249
Schema additionalProperties = (Schema) schemas.get("OrderRef").getAdditionalProperties();
250-
assertEquals(additionalProperties.get$ref(), "#/components/schemas/User_2");
250+
assertEquals(additionalProperties.get$ref(), "#/components/schemas/User");
251251

252252
//AllOfSchema
253253
ComposedSchema extended = (ComposedSchema) schemas.get("ExtendedErrorModel");
@@ -307,7 +307,7 @@ public void componentsResolver() throws Exception {
307307
//internal Schema header
308308
Map<String, Header> headers = openAPI.getComponents().getHeaders();
309309
//header remote schema ref
310-
assertEquals(headers.get("X-Rate-Limit-Remaining").getSchema().get$ref(),"#/components/schemas/User_2");
310+
assertEquals(headers.get("X-Rate-Limit-Remaining").getSchema().get$ref(),"#/components/schemas/User");
311311

312312
//header examples
313313
assertEquals(headers.get("X-Rate-Limit-Reset").getExamples().get("headerExample").get$ref(), "#/components/examples/dog" );

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,23 @@ public void testIssue1316() {
9191
}
9292

9393

94+
@Test
95+
public void testIssue_1292() {
96+
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
97+
ParseOptions options = new ParseOptions();
98+
options.setResolve(true);
99+
100+
SwaggerParseResult parseResult = openApiParser.readLocation("issue-1292/petstore.yml", null, options);
101+
102+
OpenAPI openAPI = parseResult.getOpenAPI();
103+
104+
assertNotNull(openAPI.getPaths().get("/pets").getGet().getResponses().get("200").getContent().get("application/json").getSchema().get$ref(), "#/components/schemas/Pets");
105+
assertNotNull(openAPI.getPaths().get("/pets").getGet().getResponses().getDefault().getContent().get("application/json").getSchema().get$ref(), "#/components/schemas/Error");
106+
assertNotNull(openAPI.getComponents().getSchemas().get("Pet"));
107+
assertNotNull(openAPI.getComponents().getSchemas().get("Pets"));
108+
assertNotNull(openAPI.getComponents().getSchemas().get("Error"));
109+
}
110+
94111
@Test
95112
public void testFlattenComposedSchema() {
96113
OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
@@ -1089,9 +1106,9 @@ public void testLoadExternalNestedDefinitions() throws Exception {
10891106
assertTrue(definitions.containsKey("x"));
10901107
assertTrue(definitions.containsKey("y"));
10911108
assertTrue(definitions.containsKey("z"));
1092-
assertEquals( definitions.get("i").get$ref(),"#/components/schemas/k_2");
1109+
assertEquals( definitions.get("i").get$ref(),"#/components/schemas/k");
10931110
assertEquals( definitions.get("k").getTitle(), "k-definition");
1094-
assertEquals( definitions.get("k_2").getTitle(), "k-definition");
1111+
10951112
}
10961113

10971114
@Test
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
components:
2+
schemas:
3+
Pet:
4+
type: object
5+
required:
6+
- id
7+
- name
8+
properties:
9+
id:
10+
type: integer
11+
format: int64
12+
name:
13+
type: string
14+
tag:
15+
type: string
16+
Pets:
17+
type: array
18+
items:
19+
$ref: "#/components/schemas/Pet"
20+
Error:
21+
type: object
22+
required:
23+
- code
24+
- message
25+
properties:
26+
code:
27+
type: integer
28+
format: int32
29+
message:
30+
type: string
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
get:
2+
summary: List all pets
3+
operationId: listPets
4+
tags:
5+
- pets
6+
parameters:
7+
- name: limit
8+
in: query
9+
description: How many items to return at one time (max 100)
10+
required: false
11+
schema:
12+
type: integer
13+
format: int32
14+
responses:
15+
"200":
16+
description: A paged array of pets
17+
headers:
18+
x-next:
19+
description: A link to the next page of responses
20+
schema:
21+
type: string
22+
content:
23+
application/json:
24+
schema:
25+
$ref: "./def.yml#/components/schemas/Pets"
26+
default:
27+
description: unexpected error
28+
content:
29+
application/json:
30+
schema:
31+
$ref: "./def.yml#/components/schemas/Error"
32+
post:
33+
summary: Create a pet
34+
operationId: createPets
35+
tags:
36+
- pets
37+
responses:
38+
"201":
39+
description: Null response
40+
default:
41+
description: unexpected error
42+
content:
43+
application/json:
44+
schema:
45+
$ref: "./def.yml#/components/schemas/Error"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
openapi: "3.0.0"
2+
info:
3+
version: 1.0.0
4+
title: Swagger Petstore
5+
license:
6+
name: MIT
7+
servers:
8+
- url: http://petstore.swagger.io/v1
9+
paths:
10+
/pets:
11+
$ref: "./pets/pets.yml"

modules/swagger-parser-v3/src/test/resources/nested-file-references/events.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ get:
1515
paging:
1616
$ref: './common/paging.yaml#/Paging'
1717
items:
18-
$ref: '#/components/schemas/StatusResponse'
18+
$ref: './issue-312.yaml#/components/schemas/StatusResponse'

modules/swagger-parser-v3/src/test/resources/nested-file-references/issue-312.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ components:
1313
StatusResponse:
1414
properties:
1515
http_code:
16-
type: integer
16+
type: integer

0 commit comments

Comments
 (0)