Skip to content

Commit 82e0008

Browse files
authored
Merge pull request #726 from swagger-api/OAS3-relative-resolving
fixing relative references
2 parents be22576 + 96c54a7 commit 82e0008

File tree

17 files changed

+451
-46
lines changed

17 files changed

+451
-46
lines changed

modules/swagger-parser-v2-converter/src/main/java/io/swagger/v3/parser/converter/SwaggerConverter.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ public SwaggerParseResult convert(SwaggerDeserializationResult parse) {
143143
openAPI.setExternalDocs(convert(swagger.getExternalDocs()));
144144
}
145145

146-
openAPI.setInfo(convert(swagger.getInfo()));
146+
if (swagger.getInfo() != null) {
147+
openAPI.setInfo(convert(swagger.getInfo()));
148+
}
147149

148150
openAPI.setServers(convert(swagger.getSchemes(), swagger.getHost(), swagger.getBasePath()));
149151

modules/swagger-parser-v2-converter/src/test/java/io/swagger/parser/test/V2ConverterTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,15 @@ public class V2ConverterTest {
7979
private static final String ISSUE_673_YAML = "issue-673.yaml";
8080
private static final String ISSUE_676_JSON = "issue-676.json";
8181
private static final String ISSUE_708_YAML = "issue-708.yaml";
82+
private static final String ISSUE_755_YAML = "issue-755.yaml";
8283
private static final String ISSUE_740_YAML = "issue-740.yaml";
8384
private static final String ISSUE_756_JSON = "issue-756.json";
8485
private static final String ISSUE_758_JSON = "issue-758.json";
8586
private static final String ISSUE_762_JSON = "issue-762.json";
8687
private static final String ISSUE_765_YAML = "issue-765.yaml";
8788
private static final String ISSUE_768_JSON = "issue-786.json";
8889

90+
8991
private static final String API_BATCH_PATH = "/api/batch/";
9092
private static final String PETS_PATH = "/pets";
9193
private static final String PET_FIND_BY_STATUS_PATH = "/pet/findByStatus";
@@ -704,12 +706,20 @@ public void testSwaggerParseResultHasMessage() throws Exception {
704706
assertNotNull(result.getMessages());
705707
}
706708

709+
707710
@Test(description = "OpenAPI v2 converter - Migrate minLength, maxLength and pattern of String property")
708711
public void testIssue786() throws Exception {
709712
final OpenAPI oas = getConvertedOpenAPIFromJsonFile(ISSUE_768_JSON);
710713
assertNotNull(oas);
711714
}
712715

716+
717+
@Test(description = "OpenAPI v2 converter - Conversion of a spec without a info section")
718+
public void testIssue755() throws Exception {
719+
final OpenAPI oas = getConvertedOpenAPIFromJsonFile(ISSUE_755_YAML);
720+
assertNotNull(oas);
721+
}
722+
713723
private OpenAPI getConvertedOpenAPIFromJsonFile(String file) throws IOException, URISyntaxException {
714724
SwaggerConverter converter = new SwaggerConverter();
715725
String swaggerAsString = new String(Files.readAllBytes(Paths.get(getClass().getClassLoader().getResource(file).toURI())));
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
swagger: '2.0'
2+
host: petstore.swagger.io
3+
basePath: /v2
4+
schemes:
5+
- http
6+
paths:
7+
/ping:
8+
post:
9+
summary: test
10+
description: 'test it'
11+
operationId: pingOp
12+
responses:
13+
'200':
14+
description: OK

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

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,17 @@ public OpenAPI parseRoot(JsonNode node, ParseResult result, String path) {
203203
return openAPI;
204204
}
205205

206+
public String mungedRef(String refString) {
207+
// Ref: IETF RFC 3966, Section 5.2.2
208+
if (!refString.contains(":") && // No scheme
209+
!refString.startsWith("#") && // Path is not empty
210+
!refString.startsWith("/") && // Path is not absolute
211+
refString.indexOf(".") > 0) { // Path does not start with dot but contains "." (file extension)
212+
return "./" + refString;
213+
}
214+
return null;
215+
}
216+
206217
public Map<String,Object> getExtensions(ObjectNode node){
207218

208219
Map<String,Object> extensions = new LinkedHashMap<>();
@@ -531,8 +542,13 @@ public PathItem getPathItem(ObjectNode obj, String location, ParseResult result)
531542
JsonNode ref = obj.get("$ref");
532543

533544
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
534-
pathItem.set$ref(ref.asText());
535-
return pathItem.$ref((ref.asText()));
545+
String mungedRef = mungedRef(ref.textValue());
546+
if (mungedRef != null) {
547+
pathItem.set$ref(mungedRef);
548+
}else{
549+
pathItem.set$ref(ref.textValue());
550+
}
551+
return pathItem;
536552
} else if (ref.getNodeType().equals(JsonNodeType.OBJECT)) {
537553
ObjectNode node = (ObjectNode) ref;
538554

@@ -1005,8 +1021,14 @@ public Link getLink(ObjectNode linkNode, String location, ParseResult result) {
10051021
JsonNode ref = linkNode.get("$ref");
10061022
if (ref != null) {
10071023
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
1008-
link.set$ref(ref.asText());
1009-
return link.$ref(ref.asText());
1024+
String mungedRef = mungedRef(ref.textValue());
1025+
if (mungedRef != null) {
1026+
link.set$ref(mungedRef);
1027+
}else{
1028+
link.set$ref(ref.textValue());
1029+
}
1030+
1031+
return link;
10101032
} else {
10111033
result.invalidType(location, "$ref", "string", linkNode);
10121034
return null;
@@ -1101,7 +1123,13 @@ public Callback getCallback(ObjectNode node,String location, ParseResult result)
11011123
if (ref != null) {
11021124
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
11031125
PathItem pathItem = new PathItem();
1104-
return callback.addPathItem(name,pathItem.$ref(ref.asText()));
1126+
String mungedRef = mungedRef(ref.textValue());
1127+
if (mungedRef != null) {
1128+
pathItem.set$ref(mungedRef);
1129+
}else{
1130+
pathItem.set$ref(ref.textValue());
1131+
}
1132+
return callback.addPathItem(name,pathItem);
11051133
} else {
11061134
result.invalidType(location, "$ref", "string", node);
11071135
return null;
@@ -1295,8 +1323,13 @@ public Parameter getParameter(ObjectNode obj, String location, ParseResult resul
12951323
if (ref != null) {
12961324
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
12971325
parameter = new Parameter();
1298-
parameter.set$ref(ref.asText());
1299-
return parameter.$ref(ref.asText());
1326+
String mungedRef = mungedRef(ref.textValue());
1327+
if (mungedRef != null) {
1328+
parameter.set$ref(mungedRef);
1329+
}else{
1330+
parameter.set$ref(ref.textValue());
1331+
}
1332+
return parameter;
13001333
} else {
13011334
result.invalidType(location, "$ref", "string", obj);
13021335
return null;
@@ -1451,8 +1484,13 @@ public Header getHeader(ObjectNode headerNode, String location, ParseResult resu
14511484
JsonNode ref = headerNode.get("$ref");
14521485
if (ref != null) {
14531486
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
1454-
header.set$ref(ref.asText());
1455-
return header.$ref(ref.asText());
1487+
String mungedRef = mungedRef(ref.textValue());
1488+
if (mungedRef != null) {
1489+
header.set$ref(mungedRef);
1490+
}else{
1491+
header.set$ref(ref.textValue());
1492+
}
1493+
return header;
14561494
} else {
14571495
result.invalidType(location, "$ref", "string", headerNode);
14581496
return null;
@@ -1586,8 +1624,13 @@ public SecurityScheme getSecurityScheme(ObjectNode node, String location, ParseR
15861624
JsonNode ref = node.get("$ref");
15871625
if (ref != null) {
15881626
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
1589-
securityScheme.set$ref(ref.asText());
1590-
return securityScheme.$ref(ref.asText());
1627+
String mungedRef = mungedRef(ref.textValue());
1628+
if (mungedRef != null) {
1629+
securityScheme.set$ref(mungedRef);
1630+
}else{
1631+
securityScheme.set$ref(ref.textValue());
1632+
}
1633+
return securityScheme;
15911634
} else {
15921635
result.invalidType(location, "$ref", "string", node);
15931636
return null;
@@ -1892,8 +1935,13 @@ public Schema getSchema(ObjectNode node, String location, ParseResult result){
18921935
JsonNode ref = node.get("$ref");
18931936
if (ref != null) {
18941937
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
1895-
schema.set$ref(ref.asText());
1896-
return schema.$ref(ref.asText());
1938+
String mungedRef = mungedRef(ref.textValue());
1939+
if (mungedRef != null) {
1940+
schema.set$ref(mungedRef);
1941+
}else{
1942+
schema.set$ref(ref.asText());
1943+
}
1944+
return schema;
18971945
} else {
18981946
result.invalidType(location, "$ref", "string", node);
18991947
return null;
@@ -2192,8 +2240,13 @@ public Example getExample(ObjectNode node, String location, ParseResult result)
21922240
JsonNode ref = node.get("$ref");
21932241
if (ref != null) {
21942242
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
2195-
example.set$ref(ref.asText());
2196-
return example.$ref((ref.asText()));
2243+
String mungedRef = mungedRef(ref.textValue());
2244+
if (mungedRef != null) {
2245+
example.set$ref(mungedRef);
2246+
}else{
2247+
example.set$ref(ref.textValue());
2248+
}
2249+
return example;
21972250
} else {
21982251
result.invalidType(location, "$ref", "string", node);
21992252
return null;
@@ -2303,8 +2356,13 @@ public ApiResponse getResponse(ObjectNode node, String location, ParseResult res
23032356
JsonNode ref = node.get("$ref");
23042357
if (ref != null) {
23052358
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
2306-
apiResponse.set$ref(ref.asText());
2307-
return apiResponse.$ref((ref.asText()));
2359+
String mungedRef = mungedRef(ref.textValue());
2360+
if (mungedRef != null) {
2361+
apiResponse.set$ref(mungedRef);
2362+
}else{
2363+
apiResponse.set$ref(ref.textValue());
2364+
}
2365+
return apiResponse;
23082366
} else {
23092367
result.invalidType(location, "$ref", "string", node);
23102368
return null;
@@ -2528,8 +2586,13 @@ protected RequestBody getRequestBody(ObjectNode node, String location, ParseResu
25282586
JsonNode ref = node.get("$ref");
25292587
if (ref != null) {
25302588
if (ref.getNodeType().equals(JsonNodeType.STRING)) {
2531-
body.set$ref(ref.asText());
2532-
return body.$ref(ref.asText());
2589+
String mungedRef = mungedRef(ref.textValue());
2590+
if (mungedRef != null) {
2591+
body.set$ref(mungedRef);
2592+
}else{
2593+
body.set$ref(ref.textValue());
2594+
}
2595+
return body;
25332596
} else {
25342597
result.invalidType(location, "$ref", "string", node);
25352598
return null;
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,36 @@
11
package io.swagger.v3.parser.util;
22

33
import java.net.URI;
4+
import java.net.URL;
5+
import java.nio.file.Files;
46
import java.nio.file.Path;
57
import java.nio.file.Paths;
68

79
public class PathUtils {
810

911

10-
public static Path getParentDirectoryOfFile(String fileStr) {
11-
final String fileScheme = "file:";
12-
Path file;
13-
fileStr = fileStr.replaceAll("\\\\","/");
14-
if (fileStr.toLowerCase().startsWith(fileScheme)) {
15-
file = Paths.get(URI.create(fileStr)).toAbsolutePath();
16-
} else {
17-
file = Paths.get(fileStr).toAbsolutePath();
12+
public static Path getParentDirectoryOfFile(String location) {
13+
Path file = null;
14+
try {
15+
location = location.replaceAll("\\\\","/");
16+
final String fileScheme = "file:";
17+
if (location.toLowerCase().startsWith(fileScheme)) {
18+
file = Paths.get(URI.create(location)).toAbsolutePath();
19+
} else {
20+
file = Paths.get(location).toAbsolutePath();
21+
}
22+
if (!Files.exists(file)) {
23+
URL url = PathUtils.class.getClassLoader().getResource(location);
24+
file = Paths.get((URI.create(url.toExternalForm())));
25+
return file.getParent();
26+
}
27+
28+
29+
30+
} catch (Exception e) {
31+
e.getMessage();
1832
}
33+
1934
return file.toAbsolutePath().getParent();
2035
}
2136
}

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ public static boolean isAnExternalRefFormat(RefFormat refFormat) {
4949

5050
public static RefFormat computeRefFormat(String ref) {
5151
RefFormat result = RefFormat.INTERNAL;
52-
if(ref.startsWith("http")) {
52+
ref = mungedRef(ref);
53+
if(ref.startsWith("http")||ref.startsWith("https")) {
5354
result = RefFormat.URL;
5455
} else if(ref.startsWith("#/")) {
5556
result = RefFormat.INTERNAL;
@@ -60,6 +61,18 @@ public static RefFormat computeRefFormat(String ref) {
6061
return result;
6162
}
6263

64+
public static String mungedRef(String refString) {
65+
// Ref: IETF RFC 3966, Section 5.2.2
66+
if (!refString.contains(":") && // No scheme
67+
!refString.startsWith("#") && // Path is not empty
68+
!refString.startsWith("/") && // Path is not absolute
69+
!refString.contains("$") &&
70+
refString.indexOf(".") > 0) { // Path does not start with dot but contains "." (file extension)
71+
return "./" + refString;
72+
}
73+
return refString;
74+
}
75+
6376

6477
public static String readExternalUrlRef(String file, RefFormat refFormat, List<AuthorizationValue> auths,
6578
String rootPath) {
@@ -136,27 +149,38 @@ public static String readExternalRef(String file, RefFormat refFormat, List<Auth
136149
throw new RuntimeException("Ref is not external");
137150
}
138151

139-
String result;
152+
String result = null;
140153

141154
try {
142155
if (refFormat == RefFormat.URL) {
143-
144156
result = RemoteUrl.urlToString(file, auths);
145-
146157
} else {
147158
//its assumed to be a relative file ref
148159
final Path pathToUse = parentDirectory.resolve(file).normalize();
149160

150161
if(Files.exists(pathToUse)) {
151162
result = IOUtils.toString(new FileInputStream(pathToUse.toFile()), "UTF-8");
152163
} else {
164+
String url = file;
165+
if(url.contains("..")) {
166+
url = parentDirectory + url.substring(url.indexOf(".") + 2);
167+
}else{
168+
url = parentDirectory + url.substring(url.indexOf(".") + 1);
169+
}
170+
final Path pathToUse2 = parentDirectory.resolve(url).normalize();
171+
172+
if(Files.exists(pathToUse2)) {
173+
result = IOUtils.toString(new FileInputStream(pathToUse2.toFile()), "UTF-8");
174+
}
175+
}
176+
if (result == null){
153177
result = ClasspathHelper.loadFileFromClasspath(file);
154178
}
155179

180+
156181
}
157182
} catch (Exception e) {
158-
e.printStackTrace();
159-
throw new RuntimeException("Unable to load " + refFormat + " ref: " + file, e);
183+
throw new RuntimeException("Unable to load " + refFormat + " ref: " + file + " path: "+parentDirectory, e);
160184
}
161185

162186
return result;

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,25 @@ public void issue682() throws Exception {
7272
OpenAPIV3Parser parser = new OpenAPIV3Parser();
7373
ParseOptions options = new ParseOptions();
7474
options.setResolve(true);
75-
options.setResolveCombinators(false);
76-
options.setResolveFully(true);
7775

78-
String location = getClass().getResource("/odin.yaml").toString();
79-
Assert.assertNotNull(location);
80-
final SwaggerParseResult result = parser.readLocation(location, null, options);
76+
final SwaggerParseResult result = parser.readLocation("src/test/resources/sample/SwaggerPetstore.yaml", null, options);
8177
Assert.assertNotNull(result.getOpenAPI());
8278
Assert.assertTrue(result.getMessages().isEmpty());
83-
Assert.assertTrue(result.getOpenAPI().getPaths().get("/JTasker/startRun").getPost().getRequestBody().getContent().get("application/json").getSchema().getProperties().size() == 2);
79+
Assert.assertNotNull(result.getOpenAPI().getPaths().get("/pets").getGet());
80+
}
81+
82+
@Test
83+
public void issueRelativeRefs2() throws Exception {
84+
OpenAPIV3Parser parser = new OpenAPIV3Parser();
85+
ParseOptions options = new ParseOptions();
86+
options.setResolve(true);
87+
88+
final SwaggerParseResult result = parser.readLocation("src/test/resources/relative-upper-directory/swagger.yaml", null, options);
89+
Assert.assertNotNull(result.getOpenAPI());
90+
OpenAPI openAPI = result.getOpenAPI();
91+
assertNotNull(openAPI.getPaths().get("/api/Address").getGet());
92+
assertTrue(openAPI.getComponents().getSchemas().size() == 1);
93+
assertNotNull(openAPI.getComponents().getSchemas().get("AddressEx"));
8494
}
8595

8696
@Test

0 commit comments

Comments
 (0)