Skip to content

Commit

Permalink
ref #984 - fix circular refs stack overflow
Browse files Browse the repository at this point in the history
  • Loading branch information
gracekarina authored and frantuma committed Feb 26, 2019
1 parent b3dceb6 commit b93e1d3
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 19 deletions.
10 changes: 8 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>${swagger-core-version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-core</artifactId>
<version>${swagger-core-version}</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-parser</artifactId>
Expand Down Expand Up @@ -429,9 +434,10 @@
</dependency>
</dependencies>
<properties>
<!--<swagger-core-version>1.5.22-SNAPSHOT</swagger-core-version>-->
<swagger-core-version>1.5.21</swagger-core-version>
<swagger-parser-version>1.0.38</swagger-parser-version>
<jackson.version>2.8.7</jackson.version>
<swagger-parser-version>1.0.42</swagger-parser-version>
<jackson.version>2.9.8</jackson.version>
<joda-time-version>2.9.1</joda-time-version>
<joda-version>1.8.1</joda-version>
<jetty-version>9.2.9.v20150224</jetty-version>
Expand Down
75 changes: 60 additions & 15 deletions src/main/java/io/swagger/inflector/utils/ResolverUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ public class ResolverUtil {
private Map<String, Model> schemas;
private Map<String, Model> resolvedModels = new HashMap<>();
private Map<String, Property> resolvedProperties = new HashMap<>();
private Map<String, Model> processedModels = new HashMap<>();
private Map<String, Property> processedProperties = new HashMap<>();

/* set resolveCircularRefsAsObjectRefs to true to allow (in some cases) resolving circular refs in spec
as circular object references in models/properties, see issue #984 */
private boolean resolveCircularRefsAsObjectRefs = System.getProperty("resolveCircularRefsAsObjectRefs") == null ? false : Boolean.valueOf(System.getProperty("resolveCircularRefsAsObjectRefs"));

public Map<String, Model> getResolvedModels() {
return resolvedModels;
Expand Down Expand Up @@ -100,22 +106,33 @@ public void resolvePath(Path path) {
public Model resolveModel(Model schema) {
if (schema instanceof RefModel) {
String ref = ((RefModel) schema).getSimpleRef();
Model resolved = schemas.get(ref);
if (resolved == null) {
Model definitionsSchema = schemas.get(ref);
if (definitionsSchema == null) {
LOGGER.error("unresolved model " + ref);
return schema;
}
if (this.resolvedModels.containsKey(ref)) {
LOGGER.debug("avoiding infinite loop");
return this.resolvedModels.get(ref);
}
this.resolvedModels.put(ref, schema);
if (!resolveCircularRefsAsObjectRefs) {
if (this.processedModels.containsKey(ref)) {
return this.processedModels.get(ref);
}

Model model = resolveModel(resolved);
if (this.processedProperties.containsKey(ref)) {
PropertyModelConverter converter = new PropertyModelConverter();
return converter.propertyToModel(this.processedProperties.get(ref));
}

this.processedModels.put(ref, schema);
} else {
this.resolvedModels.put(ref, schema);
}
Model resolved = resolveModel(definitionsSchema);
// if we make it without a resolution loop, we can update the reference
this.resolvedModels.put(ref, model);
return model;
this.resolvedModels.put(ref, resolved);
return resolved;
}
if (schema instanceof ArrayModel) {
ArrayModel arrayModel = (ArrayModel) schema;
Expand Down Expand Up @@ -151,6 +168,7 @@ public Model resolveModel(Model schema) {
}
return model;
}
return schema;
}
if (schema instanceof ComposedModel) {
ComposedModel composedSchema = (ComposedModel) schema;
Expand All @@ -166,7 +184,11 @@ public Model resolveModel(Model schema) {
if (property.getRequired()) {
requiredProperties.add(key);
}
model.addProperty(key, resolveProperty(property));
if (!resolveCircularRefsAsObjectRefs) {
model.addProperty(key, property);
} else {
model.addProperty(key, resolveProperty(property));
}
}

}
Expand All @@ -190,8 +212,8 @@ public Model resolveModel(Model schema) {
private Property resolveProperty(Property property) {
if (property instanceof RefProperty) {
String ref = ((RefProperty) property).getSimpleRef();
Model resolved = schemas.get(ref);
if (resolved == null) {
Model definitionsSchema = schemas.get(ref);
if (definitionsSchema == null) {
LOGGER.error("unresolved model " + ref);
return property;
}
Expand All @@ -215,15 +237,38 @@ private Property resolveProperty(Property property) {

}

this.resolvedProperties.put(ref, property);
Model model = resolveModel(resolved);
if (!resolveCircularRefsAsObjectRefs) {
if (this.processedModels.containsKey(ref) || this.processedProperties.containsKey(ref)) {
LOGGER.debug("avoiding infinite loop");
Model modelResolved = this.processedModels.get(ref);
Property propertyResolved = this.processedProperties.get(ref);
if (modelResolved != null) {
PropertyModelConverter converter = new PropertyModelConverter();
Property convertedProperty = converter.modelToProperty(modelResolved);
if (convertedProperty instanceof UntypedProperty && modelResolved instanceof ModelImpl) {
Property property1 = createObjectProperty(modelResolved);
this.processedProperties.put(ref, property1);
return property1;
} else {
return convertedProperty;
}
} else if (propertyResolved != null) {
return propertyResolved;
}

}
this.processedProperties.put(ref, property);
} else {
this.resolvedProperties.put(ref, property);
}
Model resolved = resolveModel(definitionsSchema);

// if we make it without a resolution loop, we can update the reference
this.resolvedModels.put(ref, model);
this.resolvedModels.put(ref, resolved);
PropertyModelConverter converter = new PropertyModelConverter();
Property prop = converter.modelToProperty(model);
if (prop instanceof UntypedProperty && model instanceof ModelImpl) {
Property property1 = createObjectProperty(model);
Property prop = converter.modelToProperty(resolved);
if (prop instanceof UntypedProperty && resolved instanceof ModelImpl) {
Property property1 = createObjectProperty(resolved);
this.resolvedProperties.put(ref, property1);
return property1;
} else {
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/io/swagger/test/examples/ExampleBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -658,8 +658,10 @@ public void testCircularRefSchema() throws Exception {
@Test
public void testCircularRefSchemaInResponse() throws Exception {
Swagger swagger = new SwaggerParser().read("./src/test/swagger/circuler-refs-SPLAT-56-2.yaml");
System.setProperty("resolveCircularRefsAsObjectRefs", "true");
ResolverUtil resolverUtil = new ResolverUtil();
resolverUtil.resolveFully(swagger);
String yaml = Yaml.pretty().writeValueAsString(swagger);
Response response = swagger.getPaths().get("/candidates").getOperationMap().get(HttpMethod.GET).getResponses().get("200");
Example example = ExampleBuilder.fromModel("", response.getResponseSchema(), swagger.getDefinitions(), new HashMap<String, Example>());
assertNotNull(example);
Expand Down Expand Up @@ -693,6 +695,14 @@ public void testCircularRefSchemaInResponse() throws Exception {
" \"selfObj\" : { }\n" +
" }\n" +
"}");

System.setProperty("resolveCircularRefsAsObjectRefs", "false");

swagger = new SwaggerParser().read("./src/test/swagger/circuler-refs-SPLAT-56-2.yaml");
resolverUtil = new ResolverUtil();
resolverUtil.resolveFully(swagger);
String yaml2 = Yaml.pretty().writeValueAsString(swagger);
assertEquals(yaml, yaml2);
}

@Test
Expand Down
17 changes: 15 additions & 2 deletions src/test/java/io/swagger/test/utils/ResolverUtilTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.ObjectProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.UntypedProperty;
import io.swagger.parser.SwaggerParser;
import io.swagger.sample.models.Dog;
import io.swagger.util.Json;
Expand All @@ -25,6 +23,21 @@

public class ResolverUtilTest {


@Test
public void testIssue294() throws Exception {
Swagger swagger = new SwaggerParser().read("./src/test/swagger/issue-294/issue-294.yaml");
new ResolverUtil().resolveFully(swagger);
Yaml.prettyPrint(swagger);
try {
Json.mapper().writeValueAsString(swagger);

}
catch (Exception e) {
fail("Recursive loop found");
}
}

@Test
public void testRefs2() {
Swagger swagger = new SwaggerParser().read("./src/test/swagger/anotherSpec.yaml");
Expand Down
54 changes: 54 additions & 0 deletions src/test/swagger/issue-294/issue-294.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"swagger": "2.0",
"info": {
"description": "Data",
"version": "1.0",
"title": "Data"
},
"basePath": "/dcs/api",
"paths": {
"/concept": {
"get": {
"summary": "Retrieve settings",
"description": "settings",
"operationId": "getSettings",
"produces": [
"application/json"
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Concept"
}
}
}
}
}
}
},
"definitions": {

"Concept": {
"type": "object",
"properties": {
"thing": {
"type": "array",
"uniqueItems": true,
"items": {
"$ref": "#/definitions/Thing"
}
}
}
},
"Thing": {
"allOf": [
{
"$ref": "#/definitions/Concept"
}
]
}
}
}

0 comments on commit b93e1d3

Please sign in to comment.