diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java index 0e5f57d7eb..256e276230 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/OpenAPIV3Parser.java @@ -99,9 +99,9 @@ public OpenAPI read(String location, List auths, ParseOption return null; } - public SwaggerParseResult readWithInfo(JsonNode node) { + public SwaggerParseResult readWithInfo(String path, JsonNode node) { OpenAPIDeserializer ser = new OpenAPIDeserializer(); - return ser.deserialize(node); + return ser.deserialize(node, path); } private ObjectMapper getRightMapper(String data) { @@ -139,7 +139,7 @@ public SwaggerParseResult readWithInfo(String location, List ObjectMapper mapper = getRightMapper(data); JsonNode rootNode = mapper.readTree(data); LOGGER.debug("Parsed rootNode: {}", rootNode); - return readWithInfo(rootNode); + return readWithInfo(location, rootNode); } catch (SSLHandshakeException e) { SwaggerParseResult output = new SwaggerParseResult(); diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java index 3bdeb8f115..94f8fd1c16 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java @@ -49,6 +49,9 @@ import org.apache.commons.lang3.StringUtils; import java.math.BigDecimal; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; @@ -97,10 +100,14 @@ public class OpenAPIDeserializer { public SwaggerParseResult deserialize(JsonNode rootNode) { + return deserialize(rootNode, null); + } + + public SwaggerParseResult deserialize(JsonNode rootNode, String path) { SwaggerParseResult result = new SwaggerParseResult(); try { ParseResult rootParse = new ParseResult(); - OpenAPI api = parseRoot(rootNode, rootParse); + OpenAPI api = parseRoot(rootNode, rootParse, path); result.setOpenAPI(api); result.setMessages(rootParse.getMessages()); } catch (Exception e) { @@ -110,7 +117,7 @@ public SwaggerParseResult deserialize(JsonNode rootNode) { return result; } - public OpenAPI parseRoot(JsonNode node, ParseResult result) { + public OpenAPI parseRoot(JsonNode node, ParseResult result, String path) { String location = ""; OpenAPI openAPI = new OpenAPI(); if (node.getNodeType().equals(JsonNodeType.OBJECT)) { @@ -145,7 +152,7 @@ public OpenAPI parseRoot(JsonNode node, ParseResult result) { ArrayNode array = getArray("servers", rootNode, false, location, result); if (array != null && array.size() > 0) { - openAPI.setServers(getServersList(array, String.format("%s.%s", location, "servers"), result)); + openAPI.setServers(getServersList(array, String.format("%s.%s", location, "servers"), result, path)); }else { Server defaultServer = new Server(); defaultServer.setUrl("/"); @@ -331,7 +338,7 @@ public Tag getTag(ObjectNode obj, String location, ParseResult result) { - public List getServersList(ArrayNode obj, String location, ParseResult result) { + public List getServersList(ArrayNode obj, String location, ParseResult result, String path) { List servers = new ArrayList<>(); if (obj == null) { @@ -340,7 +347,7 @@ public List getServersList(ArrayNode obj, String location, ParseResult r } for (JsonNode item : obj) { if (item.getNodeType().equals(JsonNodeType.OBJECT)) { - Server server = getServer((ObjectNode) item, location, result); + Server server = getServer((ObjectNode) item, location, result, path); if (server != null) { servers.add(server); }else{ @@ -352,8 +359,16 @@ public List getServersList(ArrayNode obj, String location, ParseResult r } return servers; } + + public List getServersList(ArrayNode obj, String location, ParseResult result) { + return getServersList(obj, location, result, null); + } + + public Server getServer(ObjectNode obj, String location, ParseResult result) { + return getServer(obj, location, result, null); + } - public Server getServer(ObjectNode obj, String location, ParseResult result) { + public Server getServer(ObjectNode obj, String location, ParseResult result, String path) { if (obj == null) { return null; } @@ -362,6 +377,17 @@ public Server getServer(ObjectNode obj, String location, ParseResult result) { String value = getString("url", obj, true, location, result); if(StringUtils.isNotBlank(value)) { + if(!isValidURL(value) && path != null){ + try { + final URI absURI = new URI(path); + if("http".equals(absURI.getScheme()) || "https".equals(absURI.getScheme())){ + value = absURI.resolve(new URI(value)).toString(); + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + } server.setUrl(value); } @@ -393,6 +419,16 @@ public Server getServer(ObjectNode obj, String location, ParseResult result) { return server; } + + boolean isValidURL(String urlString){ + try { + URL url = new URL(urlString); + url.toURI(); + return true; + } catch (Exception exception) { + return false; + } + } public ServerVariables getServerVariables(ObjectNode obj, String location, ParseResult result){ ServerVariables serverVariables = new ServerVariables(); diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/NetworkReferenceTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/NetworkReferenceTest.java index 6cc854306f..c35cd35ebc 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/NetworkReferenceTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/NetworkReferenceTest.java @@ -38,6 +38,7 @@ public class NetworkReferenceTest { private static String issue_335_json, issue_335_bar_json; private static String issue_407_json; private static String issue_411_server, issue_411_components; + private static String issue_742_json; static { try { @@ -63,6 +64,8 @@ public class NetworkReferenceTest { issue_411_server = readFile("src/test/resources/nested-network-references/issue-411-server.yaml"); issue_411_components = readFile("src/test/resources/nested-network-references/issue-411-remote2.yaml"); + + issue_742_json = readFile("src/test/resources/issue-742.json"); } catch(Exception e) { e.printStackTrace(); @@ -256,6 +259,33 @@ public void testIssue411() throws Exception { assertTrue(swagger.getComponents().getSchemas().get("Error") instanceof Schema); } + + @Test + public void testIssue742() throws Exception { + final List< AuthorizationValue > auths = new ArrayList<>(); + + new Expectations() {{ + remoteUrl.urlToString("http://www.example.io/one/two/swagger.json", auths); + result = issue_742_json; + }}; + + OpenAPIV3Parser parser = new OpenAPIV3Parser(); + ParseOptions options = new ParseOptions(); + options.setResolve(true); + SwaggerParseResult result = parser.readLocation("http://www.example.io/one/two/swagger.json", auths, options); + + Assert.assertNotNull(result.getOpenAPI()); + Assert.assertNotNull(result.getOpenAPI().getServers()); + Assert.assertEquals(result.getOpenAPI().getServers().size(), 4); + Assert.assertEquals(result.getOpenAPI().getServers().get(0).getDescription(), "An absolute path"); + Assert.assertEquals(result.getOpenAPI().getServers().get(0).getUrl(), "https://api.absolute.org/v2"); + Assert.assertEquals(result.getOpenAPI().getServers().get(1).getDescription(), "Server relative to root path"); + Assert.assertEquals(result.getOpenAPI().getServers().get(1).getUrl(), "http://www.example.io/api/v2"); + Assert.assertEquals(result.getOpenAPI().getServers().get(2).getDescription(), "Server relative path 1"); + Assert.assertEquals(result.getOpenAPI().getServers().get(2).getUrl(), "http://www.example.io/one/two/path/v2"); + Assert.assertEquals(result.getOpenAPI().getServers().get(3).getDescription(), "Server relative path 2"); + Assert.assertEquals(result.getOpenAPI().getServers().get(3).getUrl(), "http://www.example.io/one/v2"); + } static String readFile(String name) throws Exception { return new String(Files.readAllBytes(new File(name).toPath()), Charset.forName("UTF-8")); diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java index 13dd389eac..cad5c1e6e6 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java @@ -1408,7 +1408,7 @@ public void readingSpecNodeShouldNotOverQuotingStringExample() throws Exception String yaml = Files.readFile(new File("src/test/resources/over-quoted-example.yaml")); JsonNode rootNode = Yaml.mapper().readValue(yaml, JsonNode.class); OpenAPIV3Parser parser = new OpenAPIV3Parser(); - OpenAPI openAPI = (parser.readWithInfo(rootNode)).getOpenAPI(); + OpenAPI openAPI = (parser.readWithInfo(null, rootNode)).getOpenAPI(); Map definitions = openAPI.getComponents().getSchemas(); assertEquals("NoQuotePlease", definitions.get("CustomerType").getExample()); diff --git a/modules/swagger-parser-v3/src/test/resources/issue-742.json b/modules/swagger-parser-v3/src/test/resources/issue-742.json new file mode 100644 index 0000000000..2532e464b1 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-742.json @@ -0,0 +1,44 @@ +{ + "openapi": "3.0.1", + "servers": [ + { + "description": "An absolute path", + "url": "https://api.absolute.org/v2" + }, + { + "description": "Server relative to root path", + "url": "/api/v2" + }, + { + "description": "Server relative path 1", + "url": "path/v2" + }, + { + "description": "Server relative path 2", + "url": "../v2" + } + ], + "info": { + "description": "This is a sample server", + "version": "1.0.0", + "title": "Issue 742 API", + "license": { + "name": "Apache-2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + }, + "paths": { + "/ping": { + "get": { + "summary": "Some ping operation", + "description": "Some ping operation", + "operationId": "doPing", + "responses": { + "201": { + "description": "OK" + } + } + } + } + } + } \ No newline at end of file