Skip to content

Commit 7eabe3b

Browse files
committed
implement support for custom URL resolvers
1 parent 63af905 commit 7eabe3b

File tree

9 files changed

+144
-62
lines changed

9 files changed

+144
-62
lines changed

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/reference/DereferencersFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public List<OpenAPIDereferencer> getDereferencers() {
4747
if (ext == null) {
4848
LOGGER.error("failed to load extension {}", ext);
4949
} else {
50-
instance.addDereferencer(ext);
50+
getInstance().addDereferencer(ext);
5151
LOGGER.debug("adding OpenAPIDereferencer: {}", ext);
5252
}
5353
}

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/reference/ReferenceUtils.java

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,13 @@
11
package io.swagger.v3.parser.reference;
22

33
import com.fasterxml.jackson.databind.JsonNode;
4-
import io.swagger.v3.core.util.Json31;
5-
import io.swagger.v3.core.util.Yaml31;
6-
import io.swagger.v3.parser.core.models.AuthorizationValue;
7-
import io.swagger.v3.parser.util.ClasspathHelper;
8-
import io.swagger.v3.parser.util.RemoteUrl;
9-
import org.apache.commons.io.IOUtils;
104
import org.apache.commons.lang3.StringUtils;
115

12-
import java.io.FileInputStream;
13-
import java.io.InputStream;
146
import java.io.UnsupportedEncodingException;
157
import java.net.URI;
168
import java.net.URLDecoder;
17-
import java.util.List;
189
import java.util.regex.Pattern;
1910

20-
import static java.nio.charset.StandardCharsets.UTF_8;
21-
2211
public class ReferenceUtils {
2312

2413
public static String toBaseURI(String uri) throws Exception
@@ -68,52 +57,6 @@ public static boolean isAnchor(String ref) {
6857
return false;
6958
}
7059

71-
public static String readURI(String absoluteUri, List<AuthorizationValue> auths) throws Exception {
72-
URI resolved = new URI(absoluteUri);
73-
if (StringUtils.isNotBlank(resolved.getScheme())) {
74-
if (resolved.getScheme().startsWith("http")) {
75-
return readHttp(absoluteUri, auths);
76-
} else if (resolved.getScheme().startsWith("file")) {
77-
return readFile(absoluteUri);
78-
} else if (resolved.getScheme().startsWith("classpath")) {
79-
return readClasspath(absoluteUri);
80-
}
81-
}
82-
// If no matches exists, try file
83-
String content = null;
84-
try {
85-
content = readFile(absoluteUri);
86-
} catch (Exception e) {
87-
//
88-
}
89-
if (StringUtils.isBlank(content)) {
90-
content = readClasspath(absoluteUri);
91-
}
92-
return content;
93-
}
94-
95-
public static JsonNode deserializeIntoTree(String content) throws Exception {
96-
boolean isJson = content.trim().startsWith("{");
97-
return isJson ? Json31.mapper().readTree(content) : Yaml31.mapper().readTree(content);
98-
}
99-
100-
public static JsonNode parse(String absoluteUri, List<AuthorizationValue> auths) throws Exception {
101-
return deserializeIntoTree(readURI(absoluteUri, auths));
102-
}
103-
104-
public static String readFile(String uri) throws Exception {
105-
try (InputStream inputStream = new FileInputStream(uri)) {
106-
return IOUtils.toString(inputStream, UTF_8);
107-
}
108-
}
109-
110-
public static String readClasspath(String uri) throws Exception {
111-
return ClasspathHelper.loadFileFromClasspath(uri);
112-
}
113-
public static String readHttp(String uri, List<AuthorizationValue> auths) throws Exception {
114-
return RemoteUrl.urlToString(uri, auths);
115-
}
116-
11760
public static String unescapePointer(String jsonPathElement) {
11861
// URL decode the fragment
11962
try {

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/reference/ReferenceVisitor.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.node.ArrayNode;
5+
import io.swagger.v3.core.util.Json31;
6+
import io.swagger.v3.core.util.Yaml31;
57
import io.swagger.v3.oas.models.PathItem;
68
import io.swagger.v3.oas.models.examples.Example;
79
import io.swagger.v3.oas.models.headers.Header;
@@ -11,6 +13,7 @@
1113
import io.swagger.v3.oas.models.parameters.RequestBody;
1214
import io.swagger.v3.oas.models.responses.ApiResponse;
1315
import io.swagger.v3.oas.models.security.SecurityScheme;
16+
import io.swagger.v3.parser.core.models.AuthorizationValue;
1417
import org.apache.commons.lang3.StringUtils;
1518
import org.slf4j.LoggerFactory;
1619

@@ -50,7 +53,7 @@ public Reference toReference(String uri) throws Exception{
5053
if (referenceSet.containsKey(baseUri)) {
5154
return referenceSet.get(baseUri);
5255
}
53-
JsonNode node = ReferenceUtils.parse(baseUri, this.reference.getAuths());
56+
JsonNode node = parse(baseUri, this.reference.getAuths());
5457

5558
Reference ref = new Reference()
5659
.auths(this.reference.getAuths())
@@ -205,11 +208,11 @@ public Schema resolveSchemaRef(Schema visiting, String ref, List<String> inherit
205208
else {
206209
JsonNode node = null;
207210
try {
208-
node = ReferenceUtils.parse(baseURI, this.reference.getAuths());
211+
node = parse(baseURI, this.reference.getAuths());
209212
} catch (Exception e) {
210213
// we can not parse, try ref
211214
baseURI = toBaseURI(ref);
212-
node = ReferenceUtils.parse(baseURI, this.reference.getAuths());
215+
node = parse(baseURI, this.reference.getAuths());
213216
}
214217
reference = toSchemaReference(baseURI, node);
215218
}
@@ -266,4 +269,13 @@ public JsonNode findAnchor(JsonNode root, String anchor) {
266269

267270
return null;
268271
}
272+
273+
public JsonNode deserializeIntoTree(String content) throws Exception {
274+
boolean isJson = content.trim().startsWith("{");
275+
return isJson ? Json31.mapper().readTree(content) : Yaml31.mapper().readTree(content);
276+
}
277+
278+
public JsonNode parse(String absoluteUri, List<AuthorizationValue> auths) throws Exception {
279+
return deserializeIntoTree(readURI(absoluteUri, auths));
280+
}
269281
}

modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/reference/Visitor.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,19 @@
1616
import io.swagger.v3.oas.models.responses.ApiResponse;
1717
import io.swagger.v3.oas.models.responses.ApiResponses;
1818
import io.swagger.v3.oas.models.security.SecurityScheme;
19-
19+
import io.swagger.v3.parser.core.models.AuthorizationValue;
20+
import io.swagger.v3.parser.util.ClasspathHelper;
21+
import io.swagger.v3.parser.util.RemoteUrl;
22+
import org.apache.commons.io.IOUtils;
23+
import org.apache.commons.lang3.StringUtils;
24+
25+
import java.io.FileInputStream;
26+
import java.io.InputStream;
27+
import java.net.URI;
2028
import java.util.List;
2129

30+
import static java.nio.charset.StandardCharsets.UTF_8;
31+
2232
public interface Visitor {
2333
OpenAPI visitOpenApi(OpenAPI openAPI);
2434

@@ -52,4 +62,41 @@ public interface Visitor {
5262

5363
Example visitExample(Example example);
5464

65+
default String readFile(String uri) throws Exception {
66+
try (InputStream inputStream = new FileInputStream(uri)) {
67+
return IOUtils.toString(inputStream, UTF_8);
68+
}
69+
}
70+
71+
default String readClasspath(String uri) throws Exception {
72+
return ClasspathHelper.loadFileFromClasspath(uri);
73+
}
74+
default String readHttp(String uri, List<AuthorizationValue> auths) throws Exception {
75+
return RemoteUrl.urlToString(uri, auths);
76+
}
77+
78+
default String readURI(String absoluteUri, List<AuthorizationValue> auths) throws Exception {
79+
URI resolved = new URI(absoluteUri);
80+
if (StringUtils.isNotBlank(resolved.getScheme())) {
81+
if (resolved.getScheme().startsWith("http")) {
82+
return readHttp(absoluteUri, auths);
83+
} else if (resolved.getScheme().startsWith("file")) {
84+
return readFile(absoluteUri);
85+
} else if (resolved.getScheme().startsWith("classpath")) {
86+
return readClasspath(absoluteUri);
87+
}
88+
}
89+
// If no matches exists, try file
90+
String content = null;
91+
try {
92+
content = readFile(absoluteUri);
93+
} catch (Exception e) {
94+
//
95+
}
96+
if (StringUtils.isBlank(content)) {
97+
content = readClasspath(absoluteUri);
98+
}
99+
return content;
100+
}
101+
55102
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package io.swagger.v3.parser.test;
2+
3+
import io.swagger.v3.parser.core.models.AuthorizationValue;
4+
import io.swagger.v3.parser.reference.DereferencerContext;
5+
import io.swagger.v3.parser.reference.OpenAPI31Traverser;
6+
import io.swagger.v3.parser.reference.OpenAPIDereferencer31;
7+
import io.swagger.v3.parser.reference.Reference;
8+
import io.swagger.v3.parser.reference.ReferenceVisitor;
9+
import io.swagger.v3.parser.reference.Traverser;
10+
import io.swagger.v3.parser.reference.Visitor;
11+
12+
import java.util.HashMap;
13+
import java.util.HashSet;
14+
import java.util.List;
15+
16+
public class CustomOpenAPIDereferencer extends OpenAPIDereferencer31 {
17+
@Override
18+
public Visitor buildReferenceVisitor(DereferencerContext context, Reference reference, Traverser traverser) {
19+
return new CustomVisitor(reference, (OpenAPI31Traverser)traverser, new HashSet<>(), new HashMap<>());
20+
}
21+
22+
static public class CustomVisitor extends ReferenceVisitor {
23+
24+
public CustomVisitor(Reference reference, OpenAPI31Traverser openAPITraverser, HashSet<Object> visited,
25+
HashMap<Object, Object> visitedMap) {
26+
super(reference, openAPITraverser, visited, visitedMap);
27+
}
28+
29+
@Override
30+
public String readHttp(String uri, List<AuthorizationValue> auths) throws Exception {
31+
32+
if (uri.startsWith("http://example.com/custom")) {
33+
return "openapi: 3.1.0\n" +
34+
"info:\n" +
35+
" title: Domain\n" +
36+
" version: '1.0'\n" +
37+
"components:\n" +
38+
" pathItems:\n" +
39+
" ExternalRef:\n" +
40+
" get:\n" +
41+
" description: ExternalRef domain\n" +
42+
" operationId: ExternalRef PathItem\n" +
43+
" responses:\n" +
44+
" '200':\n" +
45+
" description: OK";
46+
} else {
47+
return super.readHttp(uri, auths);
48+
}
49+
}
50+
}
51+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,13 @@ public void testFull() throws Exception {
1919
SwaggerParseResult swaggerParseResult = new OpenAPIV3Parser().readLocation(uri, null, p);
2020
org.testng.Assert.assertEquals(Yaml31.pretty(swaggerParseResult.getOpenAPI()), FileUtils.readFileToString(new File("src/test/resources/3.1.0/dereference/fullFS/dereferenced.yaml")));
2121
}
22+
23+
@Test
24+
public void testCustomUrlResolver() throws Exception {
25+
ParseOptions p = new ParseOptions();
26+
p.setResolve(true);
27+
String uri = "3.1.0/dereference/custom/root.json";
28+
SwaggerParseResult swaggerParseResult = new OpenAPIV3Parser().readLocation(uri, null, p);
29+
org.testng.Assert.assertEquals(Yaml31.pretty(swaggerParseResult.getOpenAPI()), FileUtils.readFileToString(new File("src/test/resources/3.1.0/dereference/custom/dereferenced.yaml")));
30+
}
2231
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
openapi: 3.1.0
2+
servers:
3+
- url: /
4+
paths:
5+
/externalref:
6+
get:
7+
description: ExternalRef domain
8+
operationId: ExternalRef PathItem
9+
responses:
10+
"200":
11+
description: OK
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"openapi": "3.1.0",
3+
"paths": {
4+
"/externalref": {
5+
"$ref": "http://example.com/custom/nested/domain.yaml#/components/pathItems/ExternalRef"
6+
}
7+
}
8+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.swagger.v3.parser.test.CustomOpenAPIDereferencer

0 commit comments

Comments
 (0)