diff --git a/modules/swagger-compat-spec-parser/pom.xml b/modules/swagger-compat-spec-parser/pom.xml index 65ad6b05aa..1b9a038788 100644 --- a/modules/swagger-compat-spec-parser/pom.xml +++ b/modules/swagger-compat-spec-parser/pom.xml @@ -4,13 +4,13 @@ io.swagger swagger-parser-project - 1.0.8 + 1.0.9 ../.. 4.0.0 io.swagger swagger-compat-spec-parser - 1.0.8 + 1.0.9 jar swagger-compat-spec-parser diff --git a/modules/swagger-compat-spec-parser/src/main/java/io/swagger/parser/SwaggerCompatConverter.java b/modules/swagger-compat-spec-parser/src/main/java/io/swagger/parser/SwaggerCompatConverter.java index f8d7efb10e..09a044409d 100644 --- a/modules/swagger-compat-spec-parser/src/main/java/io/swagger/parser/SwaggerCompatConverter.java +++ b/modules/swagger-compat-spec-parser/src/main/java/io/swagger/parser/SwaggerCompatConverter.java @@ -1,7 +1,5 @@ package io.swagger.parser; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.ObjectNode; import io.swagger.models.ArrayModel; import io.swagger.models.AuthorizationScope; import io.swagger.models.Contact; @@ -39,6 +37,7 @@ import io.swagger.models.properties.Property; import io.swagger.models.properties.PropertyBuilder; import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.StringProperty; import io.swagger.models.resourcelisting.ApiInfo; import io.swagger.models.resourcelisting.ApiKeyAuthorization; import io.swagger.models.resourcelisting.ApiListingReference; @@ -60,6 +59,9 @@ import java.util.List; import java.util.Map; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + // legacy models public class SwaggerCompatConverter implements SwaggerParserExtension { @@ -234,6 +236,12 @@ public Parameter convertParameter(io.swagger.models.apideclaration.Parameter par p = arrayProperty; } else { p = propertyFromTypedObject(param); + if (p == null) { + System.out.println(String.format( + "WARNING! No property detected for parameter '%s' (%s)! Falling back to string!", + param.getName(), param.getParamType())); + p = new StringProperty(); + } } if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; @@ -336,15 +344,11 @@ public Property propertyFromTypedObject(ExtendedTypedObject obj) { } } - if (output == null) { - System.out.println("WARNING! No property detected! Falling back to string!"); - output = PropertyBuilder.build("string", null, null); - } - return output; } - public Operation convertOperation(String tag, io.swagger.models.apideclaration.Operation operation) { + public Operation convertOperation(String tag, io.swagger.models.apideclaration.Operation operation, + ApiDeclaration apiDeclaration) { Method method; if (operation.getMethod() == null) { @@ -366,16 +370,25 @@ public Operation convertOperation(String tag, io.swagger.models.apideclaration.O output.parameter(convertParameter(parameter)); } - if (operation.getConsumes() != null) { + if (operation.getConsumes() != null && !operation.getConsumes().isEmpty()) { for (String consumes : operation.getConsumes()) { output.consumes(consumes); } + } else if (apiDeclaration.getConsumes() != null) { + for (String consumes : apiDeclaration.getConsumes()) { + output.consumes(consumes); + } } - if (operation.getProduces() != null) { + if (operation.getProduces() != null && !operation.getProduces().isEmpty()) { for (String produces : operation.getProduces()) { output.produces(produces); } + } else if (apiDeclaration.getProduces() != null) { + for (String produces : apiDeclaration.getProduces()) { + output.produces(produces); + } } + for (ResponseMessage message : operation.getResponseMessages()) { Response response = new Response().description(message.getMessage()); @@ -502,7 +515,7 @@ public Swagger convert(ResourceListing resourceListing, List api paths.put(apiPath, path); } for (io.swagger.models.apideclaration.Operation op : ops) { - Operation operation = convertOperation(tag, op); + Operation operation = convertOperation(tag, op, apiDeclaration); if (op.getMethod() != null) { path.set(op.getMethod().toString().toLowerCase(), operation); @@ -541,6 +554,7 @@ public Swagger convert(ResourceListing resourceListing, List api } } + Swagger swagger = new Swagger() .host(host) .scheme(Scheme.forValue(scheme)) diff --git a/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/LegacyConverterTest.java b/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/LegacyConverterTest.java index 1d52464aae..9c3275ad4d 100644 --- a/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/LegacyConverterTest.java +++ b/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/LegacyConverterTest.java @@ -4,15 +4,24 @@ import io.swagger.models.Info; import io.swagger.models.License; import io.swagger.models.Operation; +import io.swagger.models.Path; +import io.swagger.models.Response; import io.swagger.models.Swagger; import io.swagger.models.auth.ApiKeyAuthDefinition; import io.swagger.models.auth.In; import io.swagger.models.auth.OAuth2Definition; import io.swagger.models.auth.SecuritySchemeDefinition; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.parameters.PathParameter; import io.swagger.models.parameters.QueryParameter; import io.swagger.parser.SwaggerCompatConverter; + import org.testng.annotations.Test; +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import java.io.IOException; import java.util.List; import java.util.Map; @@ -100,4 +109,26 @@ public void failConversionTest() throws Exception { assertNull(swagger); } + + @Test + public void testFixedProperties() throws IOException { + final Swagger swagger = converter.read("src/test/resources/specs/v1_2/singleFile.json"); + final Path path = swagger.getPath("/pet/{petId}"); + assertEquals(path.getPost().getResponses().size(), 1); + for (Response item : path.getPost().getResponses().values()) { + assertNull(item.getSchema()); + } + assertNull(path.getDelete().getResponses()); + final PathParameter id = (PathParameter) Iterables.find(path.getPatch().getParameters(), + new Predicate() { + + @Override + public boolean apply(Parameter input) { + return "petId".equals(input.getName()); + } + }); + + assertEquals(id.getType(), "string"); + assertNull(id.getFormat()); + } } diff --git a/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/OperationConverterTest.java b/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/OperationConverterTest.java index c27ad434be..4567f7823c 100644 --- a/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/OperationConverterTest.java +++ b/modules/swagger-compat-spec-parser/src/test/java/io/swagger/converter/OperationConverterTest.java @@ -4,18 +4,16 @@ import io.swagger.models.Operation; import io.swagger.models.ParamType; import io.swagger.models.Response; +import io.swagger.models.apideclaration.ApiDeclaration; import io.swagger.models.apideclaration.ResponseMessage; import io.swagger.models.properties.Property; import io.swagger.models.properties.RefProperty; import io.swagger.parser.SwaggerCompatConverter; import org.testng.annotations.Test; -import java.util.ArrayList; -import java.util.List; +import java.util.*; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertTrue; +import static org.testng.Assert.*; public class OperationConverterTest { SwaggerCompatConverter converter = new SwaggerCompatConverter(); @@ -56,7 +54,7 @@ public void convertOperation1() throws Exception { parameters.add(param); operation.setParameters(parameters); - Operation converted = converter.convertOperation("tag", operation); + Operation converted = converter.convertOperation("tag", operation, new ApiDeclaration()); assertTrue(converted.getTags().size() == 1); assertEquals(converted.getTags().get(0), "tag"); @@ -78,4 +76,30 @@ public void convertOperation1() throws Exception { RefProperty ref = (RefProperty) property; assertEquals(ref.getSimpleRef(), "Cat"); } + + @Test + public void testConvertOperation_ConsumesAndProducesInheritedFromApiDeclaration() throws Exception { + Set expectedConsumes = new HashSet<>(Arrays.asList("application/json", "application/xml")); + Set expectedProduces = new HashSet<>(Arrays.asList("text/plain")); + + final ApiDeclaration apiDeclaration = new ApiDeclaration(); + apiDeclaration.setConsumes(new ArrayList<>(expectedConsumes)); + apiDeclaration.setProduces(new ArrayList<>(expectedProduces)); + + io.swagger.models.apideclaration.Operation operation = new io.swagger.models.apideclaration.Operation(); + operation.setMethod(Method.GET); + + final SwaggerCompatConverter swaggerCompatConverter = new SwaggerCompatConverter(); + Operation converted = swaggerCompatConverter.convertOperation("tag", operation, apiDeclaration); + + assertSetsAreEqual(expectedConsumes, converted.getConsumes()); + assertSetsAreEqual(expectedProduces, converted.getProduces()); + } + + private void assertSetsAreEqual(Set expectedConsumes, List actualConsumes) { + Set actualConsumesSet = new HashSet<>(); + actualConsumesSet.addAll(actualConsumes); + assertEquals(expectedConsumes.size(), actualConsumes.size()); + assertTrue(actualConsumesSet.containsAll(expectedConsumes)); + } } \ No newline at end of file diff --git a/modules/swagger-compat-spec-parser/src/test/resources/specs/v1_2/singleFile.json b/modules/swagger-compat-spec-parser/src/test/resources/specs/v1_2/singleFile.json index 48414cc98e..81b2942376 100644 --- a/modules/swagger-compat-spec-parser/src/test/resources/specs/v1_2/singleFile.json +++ b/modules/swagger-compat-spec-parser/src/test/resources/specs/v1_2/singleFile.json @@ -119,7 +119,6 @@ "name": "petId", "description": "ID of pet that needs to be fetched", "required": true, - "type": "string", "paramType": "path" }, { diff --git a/modules/swagger-parser/pom.xml b/modules/swagger-parser/pom.xml index 370b3393e3..997f0adbf2 100644 --- a/modules/swagger-parser/pom.xml +++ b/modules/swagger-parser/pom.xml @@ -4,13 +4,13 @@ io.swagger swagger-parser-project - 1.0.8 + 1.0.9 ../.. 4.0.0 io.swagger swagger-parser - 1.0.8 + 1.0.9 jar swagger-parser diff --git a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java index 04e311b200..7f3f336dd9 100644 --- a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java +++ b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerParser.java @@ -55,7 +55,7 @@ public Swagger parse(String swaggerAsString, List auths) { Swagger output; try { output = new Swagger20Parser().parse(swaggerAsString); - if (output != null && auths != null && auths.size() > 0) { + if (output != null) { return new SwaggerResolver().resolve(output, auths); } } catch (IOException e) { @@ -65,6 +65,10 @@ public Swagger parse(String swaggerAsString, List auths) { } public Swagger read(JsonNode node) { + return read(node, false); + } + + public Swagger read(JsonNode node, boolean resolve) { if (node == null) { return null; } @@ -75,7 +79,12 @@ public Swagger read(JsonNode node) { try { output = new Swagger20Parser().read(node); if (output != null) { - return output; + if(resolve) { + return new SwaggerResolver().resolve(output, new ArrayList()); + } + else { + return output; + } } } catch (IOException e) { // continue; diff --git a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerResolver.java b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerResolver.java index 4ba3b9df77..e476a33e63 100644 --- a/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerResolver.java +++ b/modules/swagger-parser/src/main/java/io/swagger/parser/SwaggerResolver.java @@ -1,6 +1,8 @@ package io.swagger.parser; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + import io.swagger.models.ArrayModel; import io.swagger.models.Model; import io.swagger.models.ModelImpl; @@ -19,6 +21,7 @@ import io.swagger.models.properties.RefProperty; import io.swagger.parser.util.RemoteUrl; import io.swagger.util.Json; +import io.swagger.util.Yaml; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -85,7 +88,13 @@ public void applyResolutions(List auths) { JsonNode location = null; String locationName = null; if (contents != null) { - location = Json.mapper().readTree(contents); + ObjectMapper mapper; + if (contents.trim().startsWith("{")) { + mapper = Json.mapper(); + } else { + mapper = Yaml.mapper(); + } + location = mapper.readTree(contents); String[] objectPath = definitionPath.split("/"); for (String objectPathPart : objectPath) { LOGGER.debug("getting part " + objectPathPart); @@ -198,6 +207,22 @@ public void detectOperationRefs() { resolutionMap.put(key, m); } } + else if (schema instanceof ArrayProperty) { + Property item = ((ArrayProperty)schema).getItems(); + if (item instanceof RefProperty) { + RefProperty ref = (RefProperty) item; + String key = ref.get$ref(); + + if (key != null && key.startsWith("http")) { + List m = resolutionMap.get(key); + if (m == null) { + m = new ArrayList(); + } + m.add(new ResolutionContext(ref, schema, "ref")); + resolutionMap.put(key, m); + } + } + } } } } diff --git a/modules/swagger-parser/src/test/scala/RemoteUrlTest.scala b/modules/swagger-parser/src/test/scala/RemoteUrlTest.scala index 7636bc22a6..156b2c2220 100644 --- a/modules/swagger-parser/src/test/scala/RemoteUrlTest.scala +++ b/modules/swagger-parser/src/test/scala/RemoteUrlTest.scala @@ -9,13 +9,13 @@ import scala.collection.JavaConverters._ @RunWith(classOf[JUnitRunner]) class RemoteUrlTest extends FlatSpec with Matchers { it should "read a remote URL" in { - val output = RemoteUrl.urlToString("http://petstore.swagger.io/v2/pet/3", null) + val output = RemoteUrl.urlToString("http://petstore.swagger.io/v2/pet/1", null) output should not be (null) } it should "set a header" in { val av = new AuthorizationValue("accept", "application/xml", "header") - val output = RemoteUrl.urlToString("http://petstore.swagger.io/v2/pet/3", List(av).asJava) + val output = RemoteUrl.urlToString("http://petstore.swagger.io/v2/pet/1", List(av).asJava) output.trim.charAt(0) should be('<') } diff --git a/modules/swagger-parser/src/test/scala/SwaggerReaderTest.scala b/modules/swagger-parser/src/test/scala/SwaggerReaderTest.scala index b1ac69aec3..4189893a62 100644 --- a/modules/swagger-parser/src/test/scala/SwaggerReaderTest.scala +++ b/modules/swagger-parser/src/test/scala/SwaggerReaderTest.scala @@ -1,9 +1,14 @@ +import io.swagger.models.Swagger import io.swagger.parser.SwaggerParser import io.swagger.util.Json +import org.apache.commons.io.FileUtils import org.junit.runner.RunWith import org.scalatest.{FlatSpec, Matchers} import org.scalatest.junit.JUnitRunner +import java.io.File +import java.nio.charset.StandardCharsets + @RunWith(classOf[JUnitRunner]) class SwaggerReaderTest extends FlatSpec with Matchers { val m = Json.mapper() @@ -41,4 +46,16 @@ class SwaggerReaderTest extends FlatSpec with Matchers { }""" ) } + + it should "read the issue 59 resource" in { + val parser = new SwaggerParser() + val sampleFilePath = "./src/test/resources/uber.json" + + val swaggerFromFile = parser.parse(FileUtils.readFileToString(new File(sampleFilePath), StandardCharsets.UTF_8)) + val swaggerFromString = parser.read(sampleFilePath) + + swaggerFromFile.isInstanceOf[Swagger] should be(true) + swaggerFromString.isInstanceOf[Swagger] should be(true) + swaggerFromFile should equal(swaggerFromString) + } } diff --git a/modules/swagger-parser/src/test/scala/SwaggerResolverTest.scala b/modules/swagger-parser/src/test/scala/SwaggerResolverTest.scala index 9a677a2b42..00bea7ccd0 100644 --- a/modules/swagger-parser/src/test/scala/SwaggerResolverTest.scala +++ b/modules/swagger-parser/src/test/scala/SwaggerResolverTest.scala @@ -22,6 +22,20 @@ class SwaggerResolverTest extends FlatSpec with Matchers { swagger.getDefinitions().get("Tag") should not be (null) } + it should "resolve a simple remote model property definition in yaml" in { + val swagger = new Swagger() + swagger.addDefinition( + "Sample", new ModelImpl() + .property("remoteRef", new RefProperty("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag"))) + + val resolved = new SwaggerResolver().resolve(swagger, null) + val prop = resolved.getDefinitions().get("Sample").getProperties().get("remoteRef") + prop.isInstanceOf[RefProperty] should be(true) + val ref = prop.asInstanceOf[RefProperty] + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } + it should "resolve an array remote model property definition" in { val swagger = new Swagger() swagger.addDefinition( @@ -37,6 +51,21 @@ class SwaggerResolverTest extends FlatSpec with Matchers { swagger.getDefinitions().get("Tag") should not be (null) } + it should "resolve an array remote model property definition in yaml" in { + val swagger = new Swagger() + swagger.addDefinition( + "Sample", new ModelImpl() + .property("remoteRef", new ArrayProperty(new RefProperty("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag")))) + + val resolved = new SwaggerResolver().resolve(swagger, null) + val prop = resolved.getDefinitions().get("Sample").getProperties().get("remoteRef") + prop.isInstanceOf[ArrayProperty] should be(true) + val ap = prop.asInstanceOf[ArrayProperty] + val ref = ap.getItems().asInstanceOf[RefProperty] + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } + it should "resolve an map remote model property definition" in { val swagger = new Swagger() swagger.addDefinition( @@ -51,6 +80,20 @@ class SwaggerResolverTest extends FlatSpec with Matchers { swagger.getDefinitions().get("Tag") should not be (null) } + it should "resolve an map remote model property definition in yaml" in { + val swagger = new Swagger() + swagger.addDefinition( + "Sample", new ModelImpl() + .property("remoteRef", new MapProperty(new RefProperty("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag")))) + val resolved = new SwaggerResolver().resolve(swagger, null) + val prop = resolved.getDefinitions().get("Sample").getProperties().get("remoteRef") + prop.isInstanceOf[MapProperty] should be(true) + val ap = prop.asInstanceOf[MapProperty] + val ref = ap.getAdditionalProperties().asInstanceOf[RefProperty] + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } + it should "resolve operation bodyparam remote refs" in { val swagger = new Swagger() swagger.path("/fun", new Path() @@ -65,6 +108,19 @@ class SwaggerResolverTest extends FlatSpec with Matchers { swagger.getDefinitions().get("Tag") should not be (null) } + it should "resolve operation bodyparam remote refs in yaml" in { + val swagger = new Swagger() + swagger.path("/fun", new Path() + .get(new Operation() + .parameter(new BodyParameter() + .schema(new RefModel("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag"))))) + + val resolved = new SwaggerResolver().resolve(swagger, null) + val param = swagger.getPaths().get("/fun").getGet().getParameters().get(0).asInstanceOf[BodyParameter] + val ref = param.getSchema().asInstanceOf[RefModel] + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } it should "resolve operation parameter remote refs" in { val swagger = new Swagger() @@ -119,5 +175,36 @@ class SwaggerResolverTest extends FlatSpec with Matchers { ref.get$ref() should equal("#/definitions/Tag") swagger.getDefinitions().get("Tag") should not be (null) } + + it should "resolve response remote refs in yaml" in { + val swagger = new Swagger() + swagger.path("/fun", new Path() + .get(new Operation() + .response(200, new Response() + .schema(new RefProperty("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag"))))) + val resolved = new SwaggerResolver().resolve(swagger, null) + val response = swagger.getPaths().get("/fun").getGet().getResponses().get("200") + val ref = response.getSchema.asInstanceOf[RefProperty] + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } + + it should "resolve array response remote refs in yaml" in { + val swagger = new Swagger() + swagger.path("/fun", new Path() + .get(new Operation() + .response(200, new Response() + .schema( + new ArrayProperty( + new RefProperty("http://petstore.swagger.io/v2/swagger.yaml#/definitions/Tag")))))) + val resolved = new SwaggerResolver().resolve(swagger, null) + val response = swagger.getPaths().get("/fun").getGet().getResponses().get("200") + val array = response.getSchema.asInstanceOf[ArrayProperty] + array.getItems() should not be (null) + + val ref = array.getItems().asInstanceOf[RefProperty]; + ref.get$ref() should equal("#/definitions/Tag") + swagger.getDefinitions().get("Tag") should not be (null) + } } diff --git a/pom.xml b/pom.xml index 60f9c77876..b280c1b984 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 4.0.0 io.swagger swagger-parser-project - 1.0.8 + 1.0.9 pom swagger-parser-project @@ -209,7 +209,7 @@ 2.4 2.10.4 1.6.3 - 1.5.0 + 1.5.1 4.8.1 2.1.3