Skip to content

Commit 54dbdc8

Browse files
author
Diego de Estrada
committed
Resolves section references. Fixes swagger-api#147
1 parent b67f507 commit 54dbdc8

File tree

11 files changed

+212
-15
lines changed

11 files changed

+212
-15
lines changed

modules/swagger-parser/src/main/java/io/swagger/parser/Swagger20Parser.java

+5-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class Swagger20Parser implements SwaggerParserExtension {
3030
@Override
3131
public SwaggerDeserializationResult readWithInfo(JsonNode node) {
3232
SwaggerDeserializer ser = new SwaggerDeserializer();
33-
return ser.deserialize(node);
33+
return ser.deserialize(node, null);
3434
}
3535

3636
@Override
@@ -94,7 +94,7 @@ public Swagger read(String location, List<AuthorizationValue> auths) throws IOEx
9494
}
9595
}
9696

97-
return convertToSwagger(data);
97+
return convertToSwagger(data, location);
9898
} catch (Exception e) {
9999
if (System.getProperty("debugParser") != null) {
100100
e.printStackTrace();
@@ -103,7 +103,7 @@ public Swagger read(String location, List<AuthorizationValue> auths) throws IOEx
103103
}
104104
}
105105

106-
private Swagger convertToSwagger(String data) throws IOException {
106+
private Swagger convertToSwagger(String data, String location) throws IOException {
107107
if (data != null) {
108108
JsonNode rootNode;
109109
if (data.trim().startsWith("{")) {
@@ -125,7 +125,7 @@ private Swagger convertToSwagger(String data) throws IOException {
125125
if (swaggerNode == null) {
126126
return null;
127127
} else {
128-
SwaggerDeserializationResult result = new SwaggerDeserializer().deserialize(rootNode);
128+
SwaggerDeserializationResult result = new SwaggerDeserializer().deserialize(rootNode, location);
129129

130130
Swagger convertValue = result.getSwagger();
131131
if (System.getProperty("debugParser") != null) {
@@ -141,7 +141,7 @@ private Swagger convertToSwagger(String data) throws IOException {
141141

142142
public Swagger parse(String data) throws IOException {
143143
Validate.notEmpty(data, "data must not be null!");
144-
return convertToSwagger(data);
144+
return convertToSwagger(data, null);
145145
}
146146

147147
@Override

modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerResolver.java

+13
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import io.swagger.models.Operation;
44
import io.swagger.models.Path;
5+
import io.swagger.models.Response;
56
import io.swagger.models.Swagger;
67
import io.swagger.models.auth.AuthorizationValue;
78
import io.swagger.parser.processors.DefinitionsProcessor;
89
import io.swagger.parser.processors.OperationProcessor;
910
import io.swagger.parser.processors.PathsProcessor;
11+
import io.swagger.parser.processors.PropertyProcessor;
1012

1113
import java.util.List;
1214

@@ -19,13 +21,15 @@ public class SwaggerResolver {
1921
private final PathsProcessor pathProcessor;
2022
private final DefinitionsProcessor definitionsProcessor;
2123
private final OperationProcessor operationsProcessor;
24+
private final PropertyProcessor propertyProcessor;
2225

2326
public SwaggerResolver(Swagger swagger, List<AuthorizationValue> auths, String parentFileLocation) {
2427
this.swagger = swagger;
2528
this.cache = new ResolverCache(swagger, auths, parentFileLocation);
2629
definitionsProcessor = new DefinitionsProcessor(cache, swagger);
2730
pathProcessor = new PathsProcessor(cache, swagger);
2831
operationsProcessor = new OperationProcessor(cache, swagger);
32+
propertyProcessor = new PropertyProcessor(cache, swagger);
2933
}
3034

3135
public SwaggerResolver(Swagger swagger, List<AuthorizationValue> auths) {
@@ -37,6 +41,15 @@ public Swagger resolve() {
3741
return null;
3842
}
3943

44+
if(swagger.getResponses() != null) {
45+
for(String responseCode : swagger.getResponses().keySet()) {
46+
Response response = swagger.getResponses().get(responseCode);
47+
if(response.getSchema() != null) {
48+
propertyProcessor.processProperty(response.getSchema());
49+
}
50+
}
51+
}
52+
4053
pathProcessor.processPaths();
4154
definitionsProcessor.processDefinitions();
4255

modules/swagger-parser/src/main/java/io/swagger/parser/util/SwaggerDeserializer.java

+62-10
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@
22

33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.node.*;
5+
import io.swagger.exception.RefException;
56
import io.swagger.models.*;
7+
import io.swagger.models.Path;
68
import io.swagger.models.auth.*;
79
import io.swagger.models.parameters.*;
810
import io.swagger.models.properties.Property;
911
import io.swagger.models.properties.PropertyBuilder;
12+
import io.swagger.models.refs.GenericRef;
13+
import io.swagger.models.refs.RefFormat;
14+
import io.swagger.models.refs.RefType;
1015
import io.swagger.util.Json;
1116

1217
import java.util.*;
1318

1419
import static io.swagger.models.properties.PropertyBuilder.PropertyId.*;
20+
import static io.swagger.parser.util.RefUtils.isAnExternalRefFormat;
1521

1622
public class SwaggerDeserializer {
1723
static Set<String> ROOT_KEYS = new HashSet<String>(Arrays.asList("swagger", "info", "host", "basePath", "schemes", "consumes", "produces", "paths", "definitions", "parameters", "responses", "securityDefinitions", "security", "tags", "externalDocs"));
@@ -29,17 +35,17 @@ public class SwaggerDeserializer {
2935
static Set<String> BODY_PARAMETER_KEYS = new HashSet<String>(Arrays.asList("name", "in", "description", "required", "schema"));
3036
static Set<String> SECURITY_SCHEME_KEYS = new HashSet<String>(Arrays.asList("type", "name", "in", "description", "flow", "authorizationUrl", "tokenUrl" , "scopes"));
3137

32-
public SwaggerDeserializationResult deserialize(JsonNode rootNode) {
38+
public SwaggerDeserializationResult deserialize(JsonNode rootNode, String parentFileLocation) {
3339
SwaggerDeserializationResult result = new SwaggerDeserializationResult();
3440
ParseResult rootParse = new ParseResult();
3541

36-
Swagger swagger = parseRoot(rootNode, rootParse);
42+
Swagger swagger = parseRoot(rootNode, rootParse, parentFileLocation);
3743
result.setSwagger(swagger);
3844
result.setMessages(rootParse.getMessages());
3945
return result;
4046
}
4147

42-
public Swagger parseRoot(JsonNode node, ParseResult result) {
48+
public Swagger parseRoot(JsonNode node, ParseResult result, String parentFileLocation) {
4349
String location = "";
4450
Swagger swagger = new Swagger();
4551
if (node.getNodeType().equals(JsonNodeType.OBJECT)) {
@@ -50,7 +56,7 @@ public Swagger parseRoot(JsonNode node, ParseResult result) {
5056
String value = getString("swagger", on, true, location, result);
5157
swagger.setSwagger(value);
5258

53-
ObjectNode obj = getObject("info", on, true, "", result);
59+
ObjectNode obj = getRootObject("info", on, true, "", result, parentFileLocation);
5460
if(obj != null) {
5561
Info info = info(obj, "info", result);
5662
swagger.info(info);
@@ -102,15 +108,15 @@ public Swagger parseRoot(JsonNode node, ParseResult result) {
102108
}
103109
}
104110

105-
obj = getObject("paths", on, true, location, result);
111+
obj = getRootObject("paths", on, true, location, result, parentFileLocation);
106112
Map<String, Path> paths = paths(obj, "paths", result);
107113
swagger.paths(paths);
108114

109-
obj = getObject("definitions", on, false, location, result);
115+
obj = getRootObject("definitions", on, false, location, result, parentFileLocation);
110116
Map<String, Model> definitions = definitions(obj, "definitions", result);
111117
swagger.setDefinitions(definitions);
112118

113-
obj = getObject("parameters", on, false, location, result);
119+
obj = getRootObject("parameters", on, false, location, result, parentFileLocation);
114120
// TODO: parse
115121

116122
if(obj != null) {
@@ -126,11 +132,11 @@ public Swagger parseRoot(JsonNode node, ParseResult result) {
126132
swagger.setParameters(parameters);
127133
}
128134

129-
obj = getObject("responses", on, false, location, result);
135+
obj = getRootObject("responses", on, false, location, result, parentFileLocation);
130136
Map<String, Response> responses = responses(obj, "responses", result);
131137
swagger.responses(responses);
132138

133-
obj = getObject("securityDefinitions", on, false, location, result);
139+
obj = getRootObject("securityDefinitions", on, false, location, result, parentFileLocation);
134140
Map<String, SecuritySchemeDefinition> securityDefinitions = securityDefinitions(obj, location, result);
135141
swagger.setSecurityDefinitions(securityDefinitions);
136142

@@ -142,7 +148,7 @@ public Swagger parseRoot(JsonNode node, ParseResult result) {
142148
List<Tag> tags = tags(array, location, result);
143149
swagger.tags(tags);
144150

145-
obj = getObject("externalDocs", on, false, location, result);
151+
obj = getRootObject("externalDocs", on, false, location, result, parentFileLocation);
146152
ExternalDocs docs = externalDocs(obj, location, result);
147153
swagger.externalDocs(docs);
148154

@@ -1378,6 +1384,52 @@ else if(!value.getNodeType().equals(JsonNodeType.OBJECT)) {
13781384
return on;
13791385
}
13801386

1387+
private void changeInternalRefsToRelative(ObjectNode node, String file) {
1388+
if (node != null && node.has("$ref")) {
1389+
String $ref = node.get("$ref").asText();
1390+
GenericRef ref = new GenericRef(RefType.DEFINITION, $ref);
1391+
if (ref.getFormat() == RefFormat.INTERNAL) {
1392+
node.put("$ref", file + $ref);
1393+
}
1394+
}
1395+
Iterator<JsonNode> it = node.iterator();
1396+
while (it.hasNext()) {
1397+
JsonNode child = it.next();
1398+
if (child.getNodeType().equals(JsonNodeType.OBJECT)) {
1399+
changeInternalRefsToRelative((ObjectNode) child, file);
1400+
}
1401+
}
1402+
}
1403+
1404+
public ObjectNode getRootObject(String key, ObjectNode node, boolean required, String location, ParseResult result, String parentFileLocation) {
1405+
ObjectNode on = getObject(key, node, required, location, result);
1406+
while (on != null && on.has("$ref")) {
1407+
String ref = on.get("$ref").asText();
1408+
String[] refParts = ref.split("#/");
1409+
String file = refParts[0];
1410+
String definitionPath = refParts.length == 2 ? refParts[1] : null;
1411+
java.nio.file.Path parentDirectory = PathUtils.getParentDirectoryOfFile(parentFileLocation);
1412+
String contents = RefUtils.readExternalRef(file, RefFormat.RELATIVE, null, parentDirectory);
1413+
1414+
if (definitionPath == null) {
1415+
on = DeserializationUtils.deserialize(contents, file, ObjectNode.class);
1416+
} else {
1417+
JsonNode tree = DeserializationUtils.deserializeIntoTree(contents, file);
1418+
1419+
String[] jsonPathElements = definitionPath.split("/");
1420+
for (String jsonPathElement : jsonPathElements) {
1421+
tree = tree.get(jsonPathElement);
1422+
if (tree == null) {
1423+
throw new RefException(definitionPath, file);
1424+
}
1425+
}
1426+
on = DeserializationUtils.deserialize(tree, file, ObjectNode.class);
1427+
changeInternalRefsToRelative(on, file);
1428+
}
1429+
}
1430+
return on;
1431+
}
1432+
13811433
public Double getDouble(String key, ObjectNode node, boolean required, String location, ParseResult result) {
13821434
Double value = null;
13831435
JsonNode v = node.get(key);

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

+56
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,26 @@ public void testLoadRecursiveExternalDef() throws Exception {
235235
assertEquals(((RefProperty) ((ArrayProperty) definitions.get("x").getProperties().get("children")).getItems()).get$ref(), "#/definitions/x");
236236
}
237237

238+
@Test
239+
public void testLoadResponsesReferenceToExternalDef() {
240+
SwaggerParser parser = new SwaggerParser();
241+
final Swagger swagger = parser.read("src/test/resources/vendor-extensions-references/a.yaml");
242+
Map<String, Response> responses = swagger.getResponses();
243+
244+
assertNotNull(responses.get("200"));
245+
assertEquals(responses.get("200").getDescription(), "Good response");
246+
assertEquals(responses.get("200").getSchema().getType(), "ref");
247+
assertEquals(((RefProperty)responses.get("200").getSchema()).get$ref(), "#/definitions/e");
248+
249+
assertNotNull(responses.get("500"));
250+
assertEquals(responses.get("500").getDescription(), "Bad response");
251+
assertEquals(responses.get("500").getSchema().getType(), "ref");
252+
assertEquals(((RefProperty)responses.get("500").getSchema()).get$ref(), "#/definitions/e");
253+
254+
Map<String, Model> definitions = swagger.getDefinitions();
255+
assertNotNull(definitions.get("e"));
256+
}
257+
238258
@Test
239259
public void testLoadNestedItemsReferences() {
240260
SwaggerParser parser = new SwaggerParser();
@@ -245,6 +265,42 @@ public void testLoadNestedItemsReferences() {
245265
assertTrue(definitions.containsKey("w"));
246266
}
247267

268+
@Test
269+
public void testLoadSectionsReference() {
270+
SwaggerParser parser = new SwaggerParser();
271+
Swagger swagger = parser.read("src/test/resources/section-references/b.yaml");
272+
Map<String, Model> definitions = swagger.getDefinitions();
273+
assertTrue(definitions.containsKey("x"));
274+
Map<String, Response> responses = swagger.getResponses();
275+
assertTrue(responses.containsKey("400"));
276+
Map<String, Path> paths = swagger.getPaths();
277+
assertTrue(paths.containsKey("/foo"));
278+
}
279+
280+
@Test
281+
public void testLoadSectionsFileReference() {
282+
SwaggerParser parser = new SwaggerParser();
283+
Swagger swagger = parser.read("src/test/resources/section-references/b2.yaml");
284+
Map<String, Model> definitions = swagger.getDefinitions();
285+
assertTrue(definitions.containsKey("x"));
286+
Map<String, Response> responses = swagger.getResponses();
287+
assertTrue(responses.containsKey("400"));
288+
Map<String, Path> paths = swagger.getPaths();
289+
assertTrue(paths.containsKey("/foo"));
290+
}
291+
292+
@Test
293+
public void testLoadNestedSectionsReference() {
294+
SwaggerParser parser = new SwaggerParser();
295+
Swagger swagger = parser.read("src/test/resources/section-references/c.yaml");
296+
Map<String, Model> definitions = swagger.getDefinitions();
297+
assertTrue(definitions.containsKey("x"));
298+
Map<String, Response> responses = swagger.getResponses();
299+
assertTrue(responses.containsKey("400"));
300+
Map<String, Path> paths = swagger.getPaths();
301+
assertTrue(paths.containsKey("/foo"));
302+
}
303+
248304
@Test
249305
public void testIssue75() {
250306
SwaggerParser parser = new SwaggerParser();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
swagger: "2.0"
3+
info:
4+
title: test
5+
version: '0.0.0'
6+
responses:
7+
400:
8+
description: "Invalid input"
9+
type: object
10+
properties:
11+
message:
12+
type: string
13+
paths:
14+
/foo:
15+
get:
16+
responses:
17+
200:
18+
description: "OK"
19+
definitions:
20+
x:
21+
type: object
22+
properties:
23+
name:
24+
type: string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
swagger: "2.0"
3+
info:
4+
title: test
5+
version: '0.0.0'
6+
responses:
7+
$ref: "./a.yaml#/responses"
8+
paths:
9+
$ref: "./a.yaml#/paths"
10+
definitions:
11+
$ref: "./a.yaml#/definitions"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
swagger: "2.0"
3+
info:
4+
title: test
5+
version: '0.0.0'
6+
responses:
7+
$ref: "./responses.yaml"
8+
paths:
9+
$ref: "./paths.yaml"
10+
definitions:
11+
$ref: "./definitions.yaml"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
swagger: "2.0"
3+
info:
4+
title: test
5+
version: '0.0.0'
6+
responses:
7+
$ref: "./b.yaml#/responses"
8+
paths:
9+
$ref: "./b.yaml#/paths"
10+
definitions:
11+
$ref: "./b.yaml#/definitions"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
x:
3+
type: object
4+
properties:
5+
name:
6+
type: string
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
/foo:
3+
get:
4+
responses:
5+
200:
6+
description: "OK"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
400:
3+
description: "Invalid input"
4+
type: object
5+
properties:
6+
message:
7+
type: string

0 commit comments

Comments
 (0)