Skip to content

Commit 7702fcf

Browse files
authored
Merge pull request #943 from swagger-api/recurssion-issue
Recurssion issue
2 parents eab9a28 + 2db4713 commit 7702fcf

File tree

4 files changed

+188
-38
lines changed

4 files changed

+188
-38
lines changed

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

Lines changed: 78 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package io.swagger.v3.parser.util;
22

3+
import io.swagger.v3.oas.models.Components;
34
import io.swagger.v3.oas.models.OpenAPI;
45
import io.swagger.v3.oas.models.Operation;
56
import io.swagger.v3.oas.models.PathItem;
7+
import io.swagger.v3.oas.models.Paths;
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;
@@ -19,7 +21,6 @@
1921
import io.swagger.v3.parser.models.RefFormat;
2022
import org.slf4j.Logger;
2123
import org.slf4j.LoggerFactory;
22-
2324
import java.util.ArrayList;
2425
import java.util.HashMap;
2526
import java.util.HashSet;
@@ -38,6 +39,8 @@ public class ResolverFully {
3839

3940
private boolean aggregateCombinators;
4041

42+
43+
4144
public ResolverFully() {
4245
this(true);
4346
}
@@ -53,52 +56,54 @@ public ResolverFully(boolean aggregateCombinators) {
5356
private Map<String, RequestBody> requestBodies;
5457
private Map<String, Header> headers;
5558
private Map<String, Link> links;
59+
private Map<String, Schema> resolvedProperties = new HashMap<>();
5660

5761
public void resolveFully(OpenAPI openAPI) {
58-
if (openAPI.getComponents() != null && openAPI.getComponents().getRequestBodies() != null) {
59-
requestBodies = openAPI.getComponents().getRequestBodies();
62+
Components components = openAPI.getComponents();
63+
if (components != null && components.getRequestBodies() != null) {
64+
requestBodies = components.getRequestBodies();
6065
if (requestBodies == null) {
6166
requestBodies = new HashMap<>();
6267
}
6368
}
6469

65-
if (openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) {
66-
schemas = openAPI.getComponents().getSchemas();
70+
if (components != null && components.getSchemas() != null) {
71+
schemas = components.getSchemas();
6772
if (schemas == null) {
6873
schemas = new HashMap<>();
6974
}
7075
}
7176

72-
if (openAPI.getComponents() != null && openAPI.getComponents().getExamples() != null) {
73-
examples = openAPI.getComponents().getExamples();
77+
if (components != null && components.getExamples() != null) {
78+
examples = components.getExamples();
7479
if (examples == null) {
7580
examples = new HashMap<>();
7681
}
7782
}
7883

79-
if (openAPI.getComponents() != null && openAPI.getComponents().getHeaders() != null) {
80-
headers = openAPI.getComponents().getHeaders();
84+
if (components != null && components.getHeaders() != null) {
85+
headers = components.getHeaders();
8186
if (headers == null) {
8287
headers = new HashMap<>();
8388
}
8489
}
8590

86-
if (openAPI.getComponents() != null && openAPI.getComponents().getParameters() != null) {
87-
parameters = openAPI.getComponents().getParameters();
91+
if (components != null && components.getParameters() != null) {
92+
parameters = components.getParameters();
8893
if (parameters == null) {
8994
parameters = new HashMap<>();
9095
}
9196
}
92-
if (openAPI.getComponents() != null && openAPI.getComponents().getLinks() != null) {
93-
links = openAPI.getComponents().getLinks();
97+
if (components != null && components.getLinks() != null) {
98+
links = components.getLinks();
9499
if (links == null) {
95100
links = new HashMap<>();
96101
}
97102
}
98-
99-
if(openAPI.getPaths() != null) {
100-
for (String pathname : openAPI.getPaths().keySet()) {
101-
PathItem pathItem = openAPI.getPaths().get(pathname);
103+
Paths paths = openAPI.getPaths();
104+
if(paths != null) {
105+
for (String pathname : paths.keySet()) {
106+
PathItem pathItem = paths.get(pathname);
102107
resolvePath(pathItem);
103108
}
104109
}
@@ -262,21 +267,24 @@ public Schema resolveSchema(Schema schema) {
262267
String ref= schema.get$ref();
263268
ref = ref.substring(ref.lastIndexOf("/") + 1);
264269
Schema resolved = schemas.get(ref);
265-
if(resolved == null) {
266-
LOGGER.error("unresolved model " + ref);
267-
return schema;
268-
}
269-
if(this.resolvedModels.containsKey(ref)) {
270-
LOGGER.debug("avoiding infinite loop");
271-
return this.resolvedModels.get(ref);
272-
}
273-
this.resolvedModels.put(ref, schema);
274270

275-
Schema model = resolveSchema(resolved);
271+
if (resolved != null) {
276272

277-
// if we make it without a resolution loop, we can update the reference
278-
this.resolvedModels.put(ref, model);
279-
return model;
273+
if (this.resolvedModels.containsKey(ref)) {
274+
LOGGER.debug("avoiding infinite loop");
275+
return resolvedModels.get(ref);
276+
}
277+
resolvedModels.put(ref, schema);
278+
Schema model = resolveSchema(resolved);
279+
280+
// if we make it without a resolution loop, we can update the reference
281+
resolvedModels.put(ref, model);
282+
283+
return model;
284+
285+
}else {
286+
return schema;
287+
}
280288
}
281289

282290
if(schema instanceof ArraySchema) {
@@ -297,8 +305,14 @@ public Schema resolveSchema(Schema schema) {
297305
Schema innerProperty = obj.getProperties().get(propertyName);
298306
// reference check
299307
if(schema != innerProperty) {
300-
Schema resolved = resolveSchema(innerProperty);
301-
updated.put(propertyName, resolved);
308+
if(resolvedProperties.get(propertyName) == null && resolvedProperties.get(propertyName) != innerProperty) {
309+
LOGGER.debug("avoiding infinite loop");
310+
Schema resolved = resolveSchema(innerProperty);
311+
updated.put(propertyName, resolved);
312+
resolvedProperties.put(propertyName, resolved);
313+
}else {
314+
updated.put(propertyName, resolvedProperties.get(propertyName));
315+
}
302316
}
303317
}
304318
obj.setProperties(updated);
@@ -331,7 +345,14 @@ public Schema resolveSchema(Schema schema) {
331345
if (resolved.getProperties() != null) {
332346
for (String key : properties.keySet()) {
333347
Schema prop = (Schema) resolved.getProperties().get(key);
334-
model.addProperties(key, resolveSchema(prop));
348+
if(resolvedProperties.get(key) == null && resolvedProperties.get(key) != prop) {
349+
LOGGER.debug("avoiding infinite loop");
350+
Schema resolvedProp = resolveSchema(prop);
351+
model.addProperties(key,resolvedProp );
352+
resolvedProperties.put(key, resolvedProp);
353+
}else {
354+
model.addProperties(key,resolvedProperties.get(key));
355+
}
335356
}
336357
if (resolved.getRequired() != null) {
337358
for (int i = 0; i < resolved.getRequired().size(); i++) {
@@ -372,7 +393,14 @@ public Schema resolveSchema(Schema schema) {
372393
if (resolved.getProperties() != null) {
373394
for (String key : properties.keySet()) {
374395
Schema prop = (Schema) resolved.getProperties().get(key);
375-
model.addProperties(key, resolveSchema(prop));
396+
if(resolvedProperties.get(key) == null && resolvedProperties.get(key) != prop) {
397+
LOGGER.debug("avoiding infinite loop");
398+
Schema resolvedProp = resolveSchema(prop);
399+
model.addProperties(key,resolvedProp );
400+
resolvedProperties.put(key, resolvedProp);
401+
}else {
402+
model.addProperties(key,resolvedProperties.get(key));
403+
}
376404
}
377405
if (resolved.getRequired() != null) {
378406
for (int i = 0; i < resolved.getRequired().size(); i++) {
@@ -414,7 +442,14 @@ public Schema resolveSchema(Schema schema) {
414442
if (resolved.getProperties() != null) {
415443
for (String key : properties.keySet()) {
416444
Schema prop = (Schema) resolved.getProperties().get(key);
417-
model.addProperties(key, resolveSchema(prop));
445+
if(resolvedProperties.get(key) == null && resolvedProperties.get(key) != prop) {
446+
LOGGER.debug("avoiding infinite loop");
447+
Schema resolvedProp = resolveSchema(prop);
448+
model.addProperties(key,resolvedProp );
449+
resolvedProperties.put(key, resolvedProp);
450+
}else {
451+
model.addProperties(key,resolvedProperties.get(key));
452+
}
418453
}
419454
if (resolved.getRequired() != null) {
420455
for (int i = 0; i < resolved.getRequired().size(); i++) {
@@ -463,8 +498,14 @@ public Schema resolveSchema(Schema schema) {
463498
Map<String, Schema> properties = model.getProperties();
464499
for (String propertyName : properties.keySet()) {
465500
Schema property = (Schema) model.getProperties().get(propertyName);
466-
Schema resolved = resolveSchema(property);
467-
updated.put(propertyName, resolved);
501+
if(resolvedProperties.get(propertyName) == null && resolvedProperties.get(propertyName) != property) {
502+
LOGGER.debug("avoiding infinite loop");
503+
Schema resolved = resolveSchema(property);
504+
updated.put(propertyName, resolved);
505+
resolvedProperties.put(propertyName, resolved);
506+
}else {
507+
updated.put(propertyName, resolvedProperties.get(propertyName));
508+
}
468509
}
469510

470511
for (String key : updated.keySet()) {

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

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.github.tomakehurst.wiremock.client.WireMock;
99
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
1010
import io.swagger.v3.core.util.Json;
11+
import io.swagger.v3.core.util.Yaml;
1112
import io.swagger.v3.oas.models.Components;
1213
import io.swagger.v3.oas.models.OpenAPI;
1314
import io.swagger.v3.oas.models.Operation;
@@ -69,6 +70,7 @@
6970
import static org.testng.Assert.assertNotNull;
7071
import static org.testng.Assert.assertNull;
7172
import static org.testng.Assert.assertTrue;
73+
import static org.testng.FileAssert.fail;
7274

7375

7476
public class OpenAPIResolverTest {
@@ -432,7 +434,7 @@ public void testSelfReferenceResolution(@Injectable final List<AuthorizationValu
432434
OpenAPI openAPI = new OpenAPIV3Parser().readContents(yaml,auths,options).getOpenAPI();
433435
ResolverFully resolverUtil = new ResolverFully();
434436
resolverUtil.resolveFully(openAPI);
435-
//System.out.println(openAPI.getPaths().get("/selfRefB").getGet().getRequestBody().getContent().get("application/json"));
437+
436438

437439
RequestBody body = openAPI.getPaths().get("/selfRefB").getGet().getRequestBody();
438440
Schema schema = body.getContent().get("application/json").getSchema();
@@ -595,6 +597,7 @@ public void resolveAllOfWithoutAggregatingParameters(@Injectable final List<Auth
595597
assertTrue(allOf.getAllOf().get(0).getProperties().containsKey("street"));
596598
assertTrue(allOf.getAllOf().get(1).getProperties().containsKey("gps"));
597599

600+
598601
// Testing path item
599602
ComposedSchema schema = (ComposedSchema) openAPI.getPaths().get("/withInvalidComposedModel").getPost().getRequestBody().getContent().get("application/json").getSchema();
600603

@@ -1134,6 +1137,35 @@ public void testUpdateInternalReferencesOfExternalFiles() {
11341137
assertEquals(((Schema) coreSchema.getProperties().get("inner")).get$ref(), "#/components/schemas/innerCore");
11351138
}
11361139

1140+
@Test
1141+
public void recursiveResolving() {
1142+
ParseOptions parseOptions = new ParseOptions();
1143+
parseOptions.setResolveFully(true);
1144+
OpenAPI openAPI = new OpenAPIV3Parser().read("recursive.yaml", null, parseOptions);
1145+
Assert.assertNotNull(openAPI.getPaths().get("/myPath").getGet().getResponses().get("200").getContent().get("application/json").getSchema().getProperties().get("myProp"));
1146+
try {
1147+
Json.mapper().writeValueAsString(openAPI);
1148+
}
1149+
catch (Exception e) {
1150+
fail("Recursive loop found");
1151+
}
1152+
1153+
}
1154+
1155+
@Test
1156+
public void recursiveResolving2() {
1157+
ParseOptions parseOptions = new ParseOptions();
1158+
parseOptions.setResolve(true);
1159+
parseOptions.setResolveFully(true);
1160+
OpenAPI openAPI = new OpenAPIV3Parser().read("recursive2.yaml", null, parseOptions);
1161+
try {
1162+
Json.mapper().writeValueAsString(openAPI);
1163+
}
1164+
catch (Exception e) {
1165+
fail("Recursive loop found");
1166+
}
1167+
}
1168+
11371169
public String replacePort(String url){
11381170
String pathFile = url.replace("${dynamicPort}", String.valueOf(this.serverPort));
11391171
return pathFile;
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
openapi: 3.0.0
2+
info:
3+
version: "0.0.2"
4+
paths:
5+
/myPath:
6+
get:
7+
responses:
8+
"200":
9+
description: Success
10+
content:
11+
application/json:
12+
schema:
13+
$ref: "#/components/schemas/Outer"
14+
application/xml:
15+
schema:
16+
$ref: "#/components/schemas/Test"
17+
components:
18+
schemas:
19+
Outer:
20+
allOf:
21+
- $ref: "#/components/schemas/Inner"
22+
Inner:
23+
properties:
24+
myProp:
25+
type: array
26+
items:
27+
$ref: "#/components/schemas/Inner"
28+
Test:
29+
properties:
30+
myProp:
31+
type: array
32+
items:
33+
$ref: "#/components/schemas/Inner"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
openapi: 3.0.0
2+
info:
3+
version: 'minimal'
4+
title: 'recursion2'
5+
description: 'problem in ResolverFully'
6+
7+
paths:
8+
/foo:
9+
post:
10+
responses:
11+
'200':
12+
description: Ok
13+
requestBody:
14+
$ref: '#/components/requestBodies/MyRequestBody'
15+
16+
/bar:
17+
put:
18+
responses:
19+
'200':
20+
description: Ok
21+
requestBody:
22+
$ref: '#/components/requestBodies/MyRequestBody'
23+
24+
components:
25+
schemas:
26+
Schema1:
27+
type: object
28+
properties:
29+
prop:
30+
$ref: '#/components/schemas/Schema2'
31+
Schema2:
32+
type: object
33+
properties:
34+
prop1:
35+
$ref: '#/components/schemas/Schema1'
36+
prop2:
37+
$ref: '#/components/schemas/Schema1'
38+
39+
requestBodies:
40+
MyRequestBody:
41+
content:
42+
application/json:
43+
schema:
44+
$ref: '#/components/schemas/Schema2'

0 commit comments

Comments
 (0)