diff --git a/modules/swagger-parser-core/src/main/java/io/swagger/parser/models/ParseOptions.java b/modules/swagger-parser-core/src/main/java/io/swagger/parser/models/ParseOptions.java index 0ea648eebb..67c906bb5b 100644 --- a/modules/swagger-parser-core/src/main/java/io/swagger/parser/models/ParseOptions.java +++ b/modules/swagger-parser-core/src/main/java/io/swagger/parser/models/ParseOptions.java @@ -3,6 +3,7 @@ public class ParseOptions { private boolean resolve; private boolean resolveFully; + private boolean flatten; public boolean isResolve() { return resolve; @@ -19,4 +20,8 @@ public boolean isResolveFully() { public void setResolveFully(boolean resolveFully) { this.resolveFully = resolveFully; } + + public boolean isFlatten() { return flatten; } + + public void setFlatten(boolean flatten) { this.flatten = flatten; } } diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/parser/v3/OpenAPIV3Parser.java b/modules/swagger-parser-v3/src/main/java/io/swagger/parser/v3/OpenAPIV3Parser.java index 5a24d07ec6..02209ad8f2 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/parser/v3/OpenAPIV3Parser.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/parser/v3/OpenAPIV3Parser.java @@ -9,6 +9,7 @@ import io.swagger.parser.models.SwaggerParseResult; import io.swagger.parser.v3.util.ClasspathHelper; import io.swagger.parser.v3.util.DeserializationUtils; +import io.swagger.parser.v3.util.InlineModelResolver; import io.swagger.parser.v3.util.OpenAPIDeserializer; import io.swagger.parser.v3.util.RemoteUrl; import io.swagger.parser.v3.util.ResolverFully; @@ -54,6 +55,9 @@ public SwaggerParseResult readLocation(String url, List auth if (options.isResolveFully()) { result.setOpenAPI(resolver.resolve()); new ResolverFully().resolveFully(result.getOpenAPI()); + }else if (options.isFlatten()){ + InlineModelResolver inlineResolver = new InlineModelResolver(); + inlineResolver.flatten(result.getOpenAPI()); } } } @@ -177,6 +181,9 @@ public SwaggerParseResult readContents(String swaggerAsString, List addedModels = new HashMap<>(); + Map generatedSignature = new HashMap<>(); + + public void flatten(OpenAPI openAPI) { + this.openAPI = openAPI; + + if (openAPI.getComponents() != null) { + + if (openAPI.getComponents().getSchemas() == null) { + openAPI.getComponents().setSchemas(new HashMap<>()); + } + } + + // operations + Map paths = openAPI.getPaths(); + if(openAPI.getComponents()== null){ + openAPI.setComponents(new Components()); + } + Map models = openAPI.getComponents().getSchemas(); + + if (paths != null) { + for (String pathname : paths.keySet()) { + PathItem path = paths.get(pathname); + + for (Operation operation : path.readOperations()) { + RequestBody body = operation.getRequestBody(); + + if (body != null) { + if (body.getContent() != null) { + Map content = body.getContent(); + for(String key: content.keySet()) { + if (content.get(key) != null) { + MediaType mediaType = content.get(key); + if(mediaType.getSchema() != null) { + Schema model = mediaType.getSchema(); + if (model.getProperties() != null && model.getProperties().size() > 0) { + flattenProperties(model.getProperties(), pathname); + String modelName = resolveModelName(model.getTitle(), "body"); + mediaType.setSchema(new Schema().$ref(modelName)); + addGenerated(modelName, model); + openAPI.getComponents().addSchemas(modelName, model); + + } else if (model instanceof ArraySchema) { + ArraySchema am = (ArraySchema) model; + Schema inner = am.getItems(); + + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), pathname); + String modelName = resolveModelName(op.getTitle(), "body"); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + am.setItems(new Schema().$ref(existing)); + } else { + am.setItems(new Schema().$ref(modelName)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } + } + } + } + } + } + List parameters = operation.getParameters(); + if (parameters != null){ + for (Parameter parameter : parameters) { + if (parameter.getSchema() != null) { + Schema model = parameter.getSchema(); + if (model.getProperties() != null) { + if (model.getType() == null || "object".equals(model.getType())) { + if (model.getProperties() != null && model.getProperties().size() > 0) { + flattenProperties(model.getProperties(), pathname); + String modelName = resolveModelName(model.getTitle(), parameter.getName()); + parameter.setSchema(new Schema().$ref(modelName)); + addGenerated(modelName, model); + openAPI.getComponents().addSchemas(modelName, model); + } + } + } else if (model instanceof ArraySchema) { + ArraySchema am = (ArraySchema) model; + Schema inner = am.getItems(); + + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), pathname); + String modelName = resolveModelName(op.getTitle(), parameter.getName()); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + am.setItems(new Schema().$ref(existing)); + } else { + am.setItems(new Schema().$ref(modelName)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } + } + } + } + Map responses = operation.getResponses(); + if (responses != null) { + for (String key : responses.keySet()) { + ApiResponse response = responses.get(key); + if (response.getContent() != null) { + Map content = response.getContent(); + for (String name: content.keySet()) { + if (content.get(name) != null) { + MediaType media = content.get(name); + if (media.getSchema() != null) { + Schema property = media.getSchema(); + if (property instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) property; + if (op.getProperties() != null && op.getProperties().size() > 0) { + String modelName = resolveModelName(op.getTitle(), "inline_response_" + key); + Schema model = modelFromProperty(op, modelName); + String existing = matchGenerated(model); + if (existing != null) { + media.setSchema(this.makeRefProperty(existing, property)); + } else { + media.setSchema(this.makeRefProperty(modelName, property)); + addGenerated(modelName, model); + openAPI.getComponents().addSchemas(modelName, model); + } + } + } else if (property instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) property; + Schema inner = ap.getItems(); + + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), pathname); + String modelName = resolveModelName(op.getTitle(), + "inline_response_" + key); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + ap.setItems(this.makeRefProperty(existing, op)); + } else { + ap.setItems(this.makeRefProperty(modelName, op)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } else if (property.getAdditionalProperties() != null) { + + Schema innerProperty = property.getAdditionalProperties(); + if (innerProperty instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) innerProperty; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), pathname); + String modelName = resolveModelName(op.getTitle(), + "inline_response_" + key); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + property.setAdditionalProperties(new Schema().$ref(existing)); + } else { + property.setAdditionalProperties(new Schema().$ref(modelName)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } + } + } + } + } + } + } + } + } + } + + // definitions + if (models != null) { + List modelNames = new ArrayList(models.keySet()); + for (String modelName : modelNames) { + Schema model = models.get(modelName); + if (model.getProperties() != null) { + Map properties = model.getProperties(); + flattenProperties(properties, modelName); + fixStringModel(model); + } else if (model instanceof ArraySchema) { + ArraySchema m = (ArraySchema) model; + Schema inner = m.getItems(); + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + String innerModelName = resolveModelName(op.getTitle(), modelName + "_inner"); + Schema innerModel = modelFromProperty(op, innerModelName); + String existing = matchGenerated(innerModel); + if (existing == null) { + openAPI.getComponents().addSchemas(innerModelName, innerModel); + addGenerated(innerModelName, innerModel); + m.setItems(new Schema().$ref(innerModelName)); + } else { + m.setItems(new Schema().$ref(existing)); + } + } + } + } else if (model instanceof ComposedSchema) { + ComposedSchema composedSchema = (ComposedSchema) model; + List list = null; + if (composedSchema.getAllOf() != null) { + list = composedSchema.getAllOf(); + }else if (composedSchema.getAnyOf() != null) { + list = composedSchema.getAnyOf(); + }else if (composedSchema.getOneOf() != null) { + list = composedSchema.getOneOf(); + } + for(int i= 0; i 0) { + name = key + "_" + count; + } + if (openAPI.getComponents().getSchemas() == null) { + return name; + } else if (!openAPI.getComponents().getSchemas().containsKey(name)) { + return name; + } + count += 1; + } + return key; + } + + public void flattenProperties(Map properties, String path) { + if (properties == null) { + return; + } + Map propsToUpdate = new HashMap<>(); + Map modelsToAdd = new HashMap<>(); + for (String key : properties.keySet()) { + Schema property = properties.get(key); + if (property instanceof ObjectSchema && ((ObjectSchema) property).getProperties() != null + && ((ObjectSchema) property).getProperties().size() > 0) { + + ObjectSchema op = (ObjectSchema) property; + + String modelName = resolveModelName(op.getTitle(), path + "_" + key); + Schema model = modelFromProperty(op, modelName); + + String existing = matchGenerated(model); + + if (existing != null) { + propsToUpdate.put(key, new Schema().$ref(existing)); + } else { + propsToUpdate.put(key, new Schema().$ref(modelName)); + modelsToAdd.put(modelName, model); + addGenerated(modelName, model); + openAPI.getComponents().addSchemas(modelName, model); + } + } else if (property instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) property; + Schema inner = ap.getItems(); + + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), path); + String modelName = resolveModelName(op.getTitle(), path + "_" + key); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + ap.setItems(new Schema().$ref(existing)); + } else { + ap.setItems(new Schema().$ref(modelName)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } else if (property.getAdditionalProperties() != null) { + Schema inner = property.getAdditionalProperties(); + + if (inner instanceof ObjectSchema) { + ObjectSchema op = (ObjectSchema) inner; + if (op.getProperties() != null && op.getProperties().size() > 0) { + flattenProperties(op.getProperties(), path); + String modelName = resolveModelName(op.getTitle(), path + "_" + key); + Schema innerModel = modelFromProperty(op, modelName); + String existing = matchGenerated(innerModel); + if (existing != null) { + property.setAdditionalProperties(new Schema().$ref(existing)); + } else { + property.setAdditionalProperties(new Schema().$ref(modelName)); + addGenerated(modelName, innerModel); + openAPI.getComponents().addSchemas(modelName, innerModel); + } + } + } + } + } + if (propsToUpdate.size() > 0) { + for (String key : propsToUpdate.keySet()) { + properties.put(key, propsToUpdate.get(key)); + } + } + for (String key : modelsToAdd.keySet()) { + openAPI.getComponents().addSchemas(key, modelsToAdd.get(key)); + this.addedModels.put(key, modelsToAdd.get(key)); + } + } + + @SuppressWarnings("static-method") + public Schema modelFromProperty(ArraySchema object, @SuppressWarnings("unused") String path) { + String description = object.getDescription(); + String example = null; + + Object obj = object.getExample(); + if (obj != null) { + example = obj.toString(); + } + + Schema inner = object.getItems(); + if (inner instanceof ObjectSchema) { + ArraySchema model = new ArraySchema(); + model.setDescription(description); + model.setExample(example); + model.setItems(object.getItems()); + return model; + } + + return null; + } + + public Schema modelFromProperty(ObjectSchema object, String path) { + String description = object.getDescription(); + String example = null; + + Object obj = object.getExample(); + if (obj != null) { + example = obj.toString(); + } + String name = object.getName(); + XML xml = object.getXml(); + Map properties = object.getProperties(); + + Schema model = new Schema();//TODO Verify this! + model.setDescription(description); + model.setExample(example); + model.setName(name); + model.setXml(xml); + + if (properties != null) { + flattenProperties(properties, path); + model.setProperties(properties); + } + + return model; + } + + @SuppressWarnings("static-method") + public Schema modelFromProperty(Schema object, @SuppressWarnings("unused") String path) { + String description = object.getDescription(); + String example = null; + + Object obj = object.getExample(); + if (obj != null) { + example = obj.toString(); + } + + ArraySchema model = new ArraySchema(); + model.setDescription(description); + model.setExample(example); + model.setItems(object.getAdditionalProperties()); + + return model; + } + + /** + * Make a RefProperty + * + * @param ref new property name + * @param property Property + * @return + */ + public Schema makeRefProperty(String ref, Schema property) { + Schema newProperty = new Schema().$ref(ref); + + this.copyVendorExtensions(property, newProperty); + return newProperty; + } + + /** + * Copy vendor extensions from Property to another Property + * + * @param source source property + * @param target target property + */ + public void copyVendorExtensions(Schema source, Schema target) { + if(source.getExtensions() != null) { + Map vendorExtensions = source.getExtensions(); + for (String extName : vendorExtensions.keySet()) { + target.addExtension(extName, vendorExtensions.get(extName)); + } + } + } + + public boolean isSkipMatches() { + return skipMatches; + } + + public void setSkipMatches(boolean skipMatches) { + this.skipMatches = skipMatches; + } + +} \ No newline at end of file diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/parser/test/OpenAPIV3ParserTest.java index 7392d38b7a..cce034f3ba 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/parser/test/OpenAPIV3ParserTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/parser/test/OpenAPIV3ParserTest.java @@ -5,6 +5,7 @@ import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; import io.swagger.oas.models.OpenAPI; +import io.swagger.oas.models.media.Schema; import io.swagger.parser.models.AuthorizationValue; import io.swagger.parser.models.ParseOptions; import io.swagger.parser.models.SwaggerParseResult; @@ -27,6 +28,8 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching; +import static org.testng.AssertJUnit.assertNotNull; +import static org.testng.AssertJUnit.assertTrue; public class OpenAPIV3ParserTest { @@ -132,6 +135,15 @@ private void setUpWireMockServer() throws IOException { .withBody(pathFile .getBytes(StandardCharsets.UTF_8)))); + pathFile = FileUtils.readFileToString(new File("src/test/resources/flatten.json")); + + WireMock.stubFor(get(urlPathMatching("/remote/json")) + .willReturn(aResponse() + .withStatus(HttpURLConnection.HTTP_OK) + .withHeader("Content-type", "application/json") + .withBody(pathFile + .getBytes(StandardCharsets.UTF_8)))); + } @AfterClass @@ -177,6 +189,33 @@ public void testResolveFully(@Injectable final List auths) t Assert.assertEquals(result.getOpenAPI().getComponents().getSchemas().get("OrderRef").getType(),"object"); } + @Test + public void testInlineModelResolver(@Injectable final List auths) throws Exception{ + + + String pathFile = FileUtils.readFileToString(new File("src/test/resources/flatten.json")); + pathFile = pathFile.replace("${dynamicPort}", String.valueOf(this.serverPort)); + ParseOptions options = new ParseOptions(); + options.setFlatten(true); + + SwaggerParseResult result = new OpenAPIV3Parser().readContents(pathFile, auths, options); + + Assert.assertNotNull(result); + OpenAPI openAPI = result.getOpenAPI(); + Assert.assertNotNull(openAPI); + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema address = (Schema)user.getProperties().get("address"); + + assertTrue((address.get$ref()!= null)); + + Schema userAddress = openAPI.getComponents().getSchemas().get("User_address"); + assertNotNull(userAddress); + assertNotNull(userAddress.getProperties().get("city")); + assertNotNull(userAddress.getProperties().get("street")); + } + @Test public void test30NoOptions(@Injectable final List auths) throws Exception{ @@ -204,6 +243,32 @@ public void testShellMethod(@Injectable final List auths){ Assert.assertEquals(openAPI.getOpenapi(), "3.0.0"); } + @Test + public void testInlineModelResolverByUrl(@Injectable final List auths){ + + String url = "http://localhost:${dynamicPort}/remote/json"; + url = url.replace("${dynamicPort}", String.valueOf(this.serverPort)); + + ParseOptions options = new ParseOptions(); + options.setFlatten(true); + + SwaggerParseResult result = new OpenAPIV3Parser().readLocation(url,auths,options); + Assert.assertNotNull(result); + OpenAPI openAPI = result.getOpenAPI(); + Assert.assertNotNull(openAPI); + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema address = (Schema)user.getProperties().get("address"); + + assertTrue((address.get$ref()!= null)); + + Schema userAddress = openAPI.getComponents().getSchemas().get("User_address"); + assertNotNull(userAddress); + assertNotNull(userAddress.getProperties().get("city")); + assertNotNull(userAddress.getProperties().get("street")); + } + private static int getDynamicPort() { return new Random().ints(10000, 20000).findFirst().getAsInt(); diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/parser/v3/util/InlineModelResolverTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/parser/v3/util/InlineModelResolverTest.java new file mode 100644 index 0000000000..33d67b8a35 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/parser/v3/util/InlineModelResolverTest.java @@ -0,0 +1,1212 @@ +package io.swagger.parser.v3.util; + + + +import io.swagger.oas.models.Components; +import io.swagger.oas.models.OpenAPI; +import io.swagger.oas.models.Operation; +import io.swagger.oas.models.PathItem; +import io.swagger.oas.models.media.ArraySchema; +import io.swagger.oas.models.media.Content; +import io.swagger.oas.models.media.MediaType; +import io.swagger.oas.models.media.ObjectSchema; +import io.swagger.oas.models.media.Schema; +import io.swagger.oas.models.media.StringSchema; +import io.swagger.oas.models.parameters.Parameter; +import io.swagger.oas.models.parameters.RequestBody; +import io.swagger.oas.models.responses.ApiResponse; +import io.swagger.oas.models.responses.ApiResponses; +import io.swagger.util.Json; +import org.testng.annotations.Test; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import static org.testng.AssertJUnit.*; + +@SuppressWarnings("static-method") +public class InlineModelResolverTest { + @Test + public void resolveInlineModelTestWithoutTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + + Schema objectSchema = new ObjectSchema(); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + Schema schema = new Schema(); + schema.setName("user"); + schema.setDescription("a common user"); + List required = new ArrayList<>(); + required.add("address"); + schema.setRequired(required); + schema.addProperties("name", new StringSchema()); + schema.addProperties("address", objectSchema); + + + openAPI.getComponents().addSchemas("User", schema); + + new InlineModelResolver().flatten(openAPI); + + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema address = (Schema)user.getProperties().get("address"); + assertTrue((address.get$ref()!= null)); + + Schema userAddress = openAPI.getComponents().getSchemas().get("User_address"); + assertNotNull(userAddress); + assertNotNull(userAddress.getProperties().get("city")); + assertNotNull(userAddress.getProperties().get("street")); + } + + @Test + public void resolveInlineModelTestWithTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + + Schema objectSchema = new ObjectSchema(); + objectSchema.setTitle("UserAddressTitle"); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + Schema schema = new Schema(); + schema.setName("user"); + schema.setDescription("a common user"); + List required = new ArrayList<>(); + required.add("address"); + schema.setRequired(required); + schema.addProperties("name", new StringSchema()); + schema.addProperties("address", objectSchema); + + + openAPI.getComponents().addSchemas("User", schema); + + new InlineModelResolver().flatten(openAPI); + + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema address = (Schema)user.getProperties().get("address"); + assertTrue( address.get$ref() != null); + + Schema userAddressTitle = openAPI.getComponents().getSchemas().get("UserAddressTitle"); + assertNotNull(userAddressTitle); + assertNotNull(userAddressTitle.getProperties().get("city")); + assertNotNull(userAddressTitle.getProperties().get("street")); + } + + @Test + public void resolveInlineModel2EqualInnerModels() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + + Schema objectSchema = new ObjectSchema(); + objectSchema.setTitle("UserAddressTitle"); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + Schema schema = new Schema(); + schema.setName("user"); + schema.setDescription("a common user"); + List required = new ArrayList<>(); + required.add("address"); + schema.setRequired(required); + schema.addProperties("name", new StringSchema()); + schema.addProperties("address", objectSchema); + + + openAPI.getComponents().addSchemas("User", schema); + + Schema addressSchema = new ObjectSchema(); + addressSchema.setTitle("UserAddressTitle"); + addressSchema.setDefault("default"); + addressSchema.setReadOnly(false); + addressSchema.setDescription("description"); + addressSchema.setName("name"); + addressSchema.addProperties("street", new StringSchema()); + addressSchema.addProperties("city", new StringSchema()); + + Schema anotherSchema = new Schema(); + anotherSchema.setName("user"); + anotherSchema.setDescription("a common user"); + List requiredFields = new ArrayList<>(); + requiredFields.add("address"); + anotherSchema.setRequired(requiredFields); + anotherSchema.addProperties("name", new StringSchema()); + anotherSchema.addProperties("lastName", new StringSchema()); + anotherSchema.addProperties("address", addressSchema); + + openAPI.getComponents().addSchemas("AnotherUser", anotherSchema); + + new InlineModelResolver().flatten(openAPI); + + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema addressSchema1 = (Schema) user.getProperties().get("address"); + assertTrue(addressSchema1.get$ref() != null); + + Schema address = openAPI.getComponents().getSchemas().get("UserAddressTitle"); + assertNotNull(address); + assertNotNull(address.getProperties().get("city")); + assertNotNull(address.getProperties().get("street")); + Schema duplicateAddress = openAPI.getComponents().getSchemas().get("UserAddressTitle_0"); + assertNull(duplicateAddress); + } + + @Test + public void resolveInlineModel2DifferentInnerModelsWIthSameTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema objectSchema = new ObjectSchema(); + objectSchema.setTitle("UserAddressTitle"); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + Schema schema = new Schema(); + schema.setName("user"); + schema.setDescription("a common user"); + List required = new ArrayList<>(); + required.add("address"); + schema.setRequired(required); + schema.addProperties("name", new StringSchema()); + schema.addProperties("address", objectSchema); + + + openAPI.getComponents().addSchemas("User", schema); + + Schema addressSchema = new ObjectSchema(); + addressSchema.setTitle("UserAddressTitle"); + addressSchema.setDefault("default"); + addressSchema.setReadOnly(false); + addressSchema.setDescription("description"); + addressSchema.setName("name"); + addressSchema.addProperties("street", new StringSchema()); + addressSchema.addProperties("city", new StringSchema()); + addressSchema.addProperties("apartment", new StringSchema()); + + + Schema anotherSchema = new Schema(); + anotherSchema.setName("AnotherUser"); + anotherSchema.setDescription("a common user"); + List requiredFields = new ArrayList<>(); + requiredFields.add("address"); + anotherSchema.setRequired(requiredFields); + anotherSchema.addProperties("name", new StringSchema()); + anotherSchema.addProperties("lastName", new StringSchema()); + anotherSchema.addProperties("address", addressSchema); + + + openAPI.getComponents().addSchemas("AnotherUser", anotherSchema); + + new InlineModelResolver().flatten(openAPI); + + Schema user = openAPI.getComponents().getSchemas().get("User"); + + assertNotNull(user); + Schema userAddress = (Schema) user.getProperties().get("address"); + assertTrue( userAddress.get$ref()!= null); + + Schema address = openAPI.getComponents().getSchemas().get("UserAddressTitle"); + assertNotNull(address); + assertNotNull(address.getProperties().get("city")); + assertNotNull(address.getProperties().get("street")); + Schema duplicateAddress = openAPI.getComponents().getSchemas().get("UserAddressTitle_1"); + assertNotNull(duplicateAddress); + assertNotNull(duplicateAddress.getProperties().get("city")); + assertNotNull(duplicateAddress.getProperties().get("street")); + assertNotNull(duplicateAddress.getProperties().get("apartment")); + } + + + @Test + public void testInlineResponseModel() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + + StringSchema stringSchema1 = new StringSchema(); + + ObjectSchema objectSchema1 = new ObjectSchema(); + objectSchema1.addProperties("name", stringSchema1); + objectSchema1.addExtension("x-ext", "ext-prop"); + + MediaType mediaType1 = new MediaType(); + mediaType1.setSchema(objectSchema1); + + Content content1 = new Content(); + content1.addMediaType("*/*", mediaType1 ); + + ApiResponse response1= new ApiResponse(); + response1.setDescription("it works!"); + response1.setContent(content1); + + ApiResponses responses1 = new ApiResponses(); + responses1.addApiResponse("200",response1); + + Operation operation1 = new Operation(); + operation1.setResponses(responses1); + + PathItem pathItem1 = new PathItem(); + pathItem1.setGet(operation1); + openAPI.path("/foo/bar",pathItem1); + + + + StringSchema stringSchema2 = new StringSchema(); + + ObjectSchema objectSchema2 = new ObjectSchema(); + objectSchema2.addProperties("name", stringSchema2); + objectSchema2.addExtension("x-ext", "ext-prop"); + MediaType mediaType2 = new MediaType(); + mediaType2.setSchema(objectSchema2); + + Content content2 = new Content(); + content2.addMediaType("*/*", mediaType2 ); + + ApiResponse response2 = new ApiResponse(); + response2.setDescription("it works!"); + response2.addExtension("x-foo","bar"); + response2.setContent(content2); + + ApiResponses responses2 = new ApiResponses(); + responses2.addApiResponse("200",response2); + + Operation operation2 = new Operation(); + operation2.setResponses(responses2); + + PathItem pathItem2 = new PathItem(); + pathItem2.setGet(operation2); + openAPI.path("/foo/baz",pathItem2); + + + new InlineModelResolver().flatten(openAPI); + + Map responses = openAPI.getPaths().get("/foo/bar").getGet().getResponses(); + + ApiResponse response = responses.get("200"); + assertNotNull(response); + + Schema schema = response.getContent().get("*/*").getSchema(); + assertTrue(schema.get$ref() != null); + assertEquals(1, schema.getExtensions().size()); + assertEquals("ext-prop", schema.getExtensions().get("x-ext")); + + Schema model = openAPI.getComponents().getSchemas().get("inline_response_200"); + assertTrue(model.getProperties().size() == 1); + assertNotNull(model.getProperties().get("name")); + assertTrue(model.getProperties().get("name") instanceof StringSchema); + } + + + @Test + public void testInlineResponseModelWithTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + String responseTitle = "GetBarResponse"; + + StringSchema stringSchema1 = new StringSchema(); + + ObjectSchema objectSchema1 = new ObjectSchema(); + objectSchema1.setTitle(responseTitle); + objectSchema1.addProperties("name", stringSchema1); + + + MediaType mediaType1 = new MediaType(); + mediaType1.setSchema(objectSchema1); + + Content content1 = new Content(); + content1.addMediaType("*/*", mediaType1 ); + + ApiResponse response1= new ApiResponse(); + response1.setDescription("it works!"); + response1.setContent(content1); + + ApiResponses responses1 = new ApiResponses(); + responses1.addApiResponse("200",response1); + + Operation operation1 = new Operation(); + operation1.setResponses(responses1); + + PathItem pathItem1 = new PathItem(); + pathItem1.setGet(operation1); + openAPI.path("/foo/bar",pathItem1); + + + + StringSchema stringSchema2 = new StringSchema(); + + ObjectSchema objectSchema2 = new ObjectSchema(); + objectSchema2.addProperties("name", stringSchema2); + objectSchema2.addExtension("x-foo", "bar"); + + MediaType mediaType2 = new MediaType(); + mediaType2.setSchema(objectSchema2); + + Content content2 = new Content(); + content2.addMediaType("*/*", mediaType2 ); + + ApiResponse response2 = new ApiResponse(); + response2.setDescription("it works!"); + + response2.setContent(content2); + + ApiResponses responses2 = new ApiResponses(); + responses2.addApiResponse("200",response2); + + Operation operation2 = new Operation(); + operation2.setResponses(responses2); + + PathItem pathItem2 = new PathItem(); + pathItem2.setGet(operation2); + openAPI.path("/foo/baz",pathItem2); + + + + new InlineModelResolver().flatten(openAPI); + + Map responses = openAPI.getPaths().get("/foo/bar").getGet().getResponses(); + + ApiResponse response = responses.get("200"); + assertNotNull(response); + assertTrue(response.getContent().get("*/*").getSchema().get$ref() != null ); + + Schema model = openAPI.getComponents().getSchemas().get(responseTitle); + assertTrue(model.getProperties().size() == 1); + assertNotNull(model.getProperties().get("name")); + assertTrue(model.getProperties().get("name") instanceof StringSchema); + } + + + @Test + public void resolveInlineArrayModelWithTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema objectSchema = new ObjectSchema(); + objectSchema.setTitle("InnerUserTitle"); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + ArraySchema arraySchema = new ArraySchema(); + List required = new LinkedList<>(); + required.add("name"); + arraySchema.setRequired(required); + arraySchema.setItems(objectSchema); + + + openAPI.getComponents().addSchemas("User", arraySchema); + + + new InlineModelResolver().flatten(openAPI); + + Schema model = openAPI.getComponents().getSchemas().get("User"); + assertTrue(model instanceof ArraySchema); + + Schema user = openAPI.getComponents().getSchemas().get("InnerUserTitle"); + assertNotNull(user); + assertEquals("description", user.getDescription()); + } + + @Test + public void resolveInlineArrayModelWithoutTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema objectSchema = new ObjectSchema(); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("city", new StringSchema()); + + ArraySchema arraySchema = new ArraySchema(); + List required = new LinkedList<>(); + required.add("name"); + arraySchema.setRequired(required); + arraySchema.setItems(objectSchema); + + openAPI.getComponents().addSchemas("User", arraySchema); + + + new InlineModelResolver().flatten(openAPI); + + Schema model = openAPI.getComponents().getSchemas().get("User"); + assertTrue(model instanceof ArraySchema); + + Schema user = openAPI.getComponents().getSchemas().get("User_inner"); + assertNotNull(user); + assertEquals("description", user.getDescription()); + } + + + + + @Test + public void resolveInlineRequestBody() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.addProperties("street", new StringSchema()); + + Schema schema = new Schema(); + schema.addProperties("address", objectSchema); + schema.addProperties("name", new StringSchema()); + + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + + Content content = new Content(); + content.addMediaType("*/*", mediaType ); + + RequestBody requestBody = new RequestBody(); + requestBody.setContent(content); + + Operation operation = new Operation(); + operation.setRequestBody(requestBody); + + PathItem pathItem = new PathItem(); + pathItem.setGet(operation); + openAPI.path("/hello",pathItem); + + new InlineModelResolver().flatten(openAPI); + + Operation getOperation = openAPI.getPaths().get("/hello").getGet(); + RequestBody body = getOperation.getRequestBody(); + assertTrue(body.getContent().get("*/*").getSchema().get$ref() != null); + + Schema bodySchema = openAPI.getComponents().getSchemas().get("body"); + assertTrue(bodySchema instanceof Schema); + + assertNotNull(bodySchema.getProperties().get("address")); + } + + @Test + public void resolveInlineParameter() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.addProperties("street", new StringSchema()); + + Schema schema = new Schema(); + schema.addProperties("address", objectSchema); + schema.addProperties("name", new StringSchema()); + + Parameter parameter = new Parameter(); + parameter.setName("name"); + parameter.setSchema(schema); + + List parameters = new ArrayList(); + parameters.add(parameter); + + + Operation operation = new Operation(); + operation.setParameters(parameters); + + PathItem pathItem = new PathItem(); + pathItem.setGet(operation); + openAPI.path("/hello",pathItem); + + new InlineModelResolver().flatten(openAPI); + + Operation getOperation = openAPI.getPaths().get("/hello").getGet(); + Parameter param = getOperation.getParameters().get(0); + assertTrue(param.getSchema().get$ref() != null); + + + + Schema bodySchema = openAPI.getComponents().getSchemas().get("name"); + assertTrue(bodySchema instanceof Schema); + + assertNotNull(bodySchema.getProperties().get("address")); + } + + @Test + public void resolveInlineRequestBodyWithTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.addProperties("street", new StringSchema()); + objectSchema.addProperties("name", new StringSchema()); + Schema addressModelItem = new Schema(); + String addressModelName = "DetailedAddress"; + addressModelItem.setTitle(addressModelName); + addressModelItem.addProperties("address", objectSchema); + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*", new MediaType() + .schema(addressModelItem)))))); + + new InlineModelResolver().flatten(openAPI); + + Operation operation = openAPI.getPaths().get("/hello").getGet(); + RequestBody requestBody = operation.getRequestBody(); + assertTrue(requestBody.getContent().get("*/*").getSchema().get$ref() != null); + + Schema body = openAPI.getComponents().getSchemas().get(addressModelName); + assertTrue(body instanceof Schema); + + assertNotNull(body.getProperties().get("address")); + } + + @Test + public void notResolveNonModelRequestBody() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*", new MediaType().schema(new Schema() + .type("string") + .format("binary"))))))); + + new InlineModelResolver().flatten(openAPI); + + Operation operation = openAPI.getPaths().get("/hello").getGet(); + RequestBody body = operation.getRequestBody(); + assertTrue(body.getContent().get("*/*").getSchema() instanceof Schema); + Schema schema = body.getContent().get("*/*").getSchema(); + assertEquals("string", schema.getType()); + assertEquals("binary", schema.getFormat()); + } + + @Test + public void resolveInlineArrayRequestBody() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema addressSchema = new ObjectSchema(); + addressSchema.addProperties("street",new StringSchema()); + + ObjectSchema objectSchema =new ObjectSchema(); + objectSchema.addProperties("address", addressSchema); + + ArraySchema arraySchema = new ArraySchema(); + arraySchema.items(objectSchema); + + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType() + .schema(arraySchema)))))); + + new InlineModelResolver().flatten(openAPI); + + RequestBody body = openAPI.getPaths().get("/hello").getGet().getRequestBody(); + Schema schema = body.getContent().get("*/*").getSchema(); + + assertTrue(schema instanceof ArraySchema); + + ArraySchema am = (ArraySchema) schema; + Schema inner = am.getItems(); + assertTrue(inner.get$ref() != null); + + assertEquals( "#/components/schemas/body",inner.get$ref()); + + Schema inline = openAPI.getComponents().getSchemas().get("body"); + assertNotNull(inline); + assertTrue(inline instanceof Schema); + + Schema address = (Schema) inline.getProperties().get("address"); + assertNotNull(address); + + assertEquals( "#/components/schemas/hello_address",address.get$ref()); + + + Schema inlineProp = openAPI.getComponents().getSchemas().get("hello_address"); + assertNotNull(inlineProp); + assertTrue(inlineProp instanceof Schema); + + assertNotNull(inlineProp.getProperties().get("street")); + assertTrue(inlineProp.getProperties().get("street") instanceof StringSchema); + } + + @Test + public void resolveInlineArrayResponse() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema items = new ObjectSchema(); + items.addExtension("x-ext", "ext-items"); + items.addProperties("name", new StringSchema()); + + + ArraySchema schema = new ArraySchema() + .items(items); + schema.addExtension("x-ext", "ext-prop"); + + ApiResponse response = new ApiResponse(); + response.addExtension("x-foo", "bar"); + response.description("it works!"); + response.content(new Content().addMediaType("*/*", new MediaType().schema(schema))); + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200",response)))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse apiResponse = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + assertNotNull(apiResponse); + + assertNotNull(apiResponse.getContent().get("*/*").getSchema()); + Schema responseProperty = apiResponse.getContent().get("*/*").getSchema(); + + // no need to flatten more + assertTrue(responseProperty instanceof ArraySchema); + + ArraySchema ap = (ArraySchema) responseProperty; + assertEquals(1, ap.getExtensions().size()); + assertEquals("ext-prop", ap.getExtensions().get("x-ext")); + + Schema p = ap.getItems(); + + assertNotNull(p); + + assertEquals("#/components/schemas/inline_response_200", p.get$ref()); + + assertEquals(1, p.getExtensions().size()); + assertEquals("ext-items", p.getExtensions().get("x-ext")); + + Schema inline = openAPI.getComponents().getSchemas().get("inline_response_200"); + assertNotNull(inline); + assertTrue(inline instanceof Schema); + + assertNotNull(inline.getProperties().get("name")); + assertTrue(inline.getProperties().get("name") instanceof StringSchema); + } + + @Test + public void resolveInlineArrayResponseWithTitle() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + ApiResponse apiResponse = new ApiResponse(); + apiResponse.addExtension("x-foo", "bar"); + apiResponse.description("it works!"); + + Map properties = new HashMap<>(); + properties.put("name", new StringSchema()); + + apiResponse.content(new Content().addMediaType("*/*", new MediaType().schema(new ArraySchema() + .items(new ObjectSchema() + .title("FooBar") + .properties(properties))))); + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200",apiResponse)))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + assertNotNull(response); + + assertNotNull(response.getContent().get("*/*").getSchema()); + Schema responseProperty = response.getContent().get("*/*").getSchema(); + + // no need to flatten more + assertTrue(responseProperty instanceof ArraySchema); + + ArraySchema ap = (ArraySchema) responseProperty; + Schema p = ap.getItems(); + + assertNotNull(p); + + assertEquals(p.get$ref(), "#/components/schemas/"+ "FooBar"); + + + Schema inline = openAPI.getComponents().getSchemas().get("FooBar"); + assertNotNull(inline); + assertTrue(inline instanceof Schema); + assertNotNull(inline.getProperties().get("name")); + assertTrue(inline.getProperties().get("name") instanceof StringSchema); + } + + @Test + public void testInlineMapResponse() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + Schema schema = new Schema(); + schema.setAdditionalProperties(new StringSchema()); + schema.addExtension("x-ext", "ext-prop"); + + ApiResponse apiResponse = new ApiResponse(); + apiResponse.description("it works!"); + MediaType mediaType = new MediaType(); + mediaType.setSchema(schema); + + Content content = new Content(); + content.addMediaType("*/*",mediaType); + + apiResponse.setContent(content); + apiResponse.addExtension("x-foo", "bar"); + + ApiResponses apiResponses = new ApiResponses(); + apiResponses.addApiResponse("200",apiResponse); + + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(apiResponses))); + + + new InlineModelResolver().flatten(openAPI); + Json.prettyPrint(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + + Schema property = response.getContent().get("*/*").getSchema(); + assertTrue(property.getAdditionalProperties() != null); + assertTrue(openAPI.getComponents().getSchemas() == null); + assertEquals(1, property.getExtensions().size()); + assertEquals("ext-prop", property.getExtensions().get("x-ext")); + } + + @Test + public void testInlineMapResponseWithObjectSchema() throws Exception { + OpenAPI openAPI = new OpenAPI(); + + Schema schema = new Schema(); + schema.setAdditionalProperties(new ObjectSchema() + .addProperties("name", new StringSchema())); + schema.addExtension("x-ext", "ext-prop"); + + ApiResponse apiResponse = new ApiResponse() + .description("it works!") + .content(new Content().addMediaType("*/*",new MediaType().schema(schema))); + apiResponse.addExtension("x-foo", "bar"); + + ApiResponses apiResponses = new ApiResponses().addApiResponse("200",apiResponse); + + + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(apiResponses))); + + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + Schema property = response.getContent().get("*/*").getSchema(); + assertTrue(property.getAdditionalProperties() != null); + assertEquals(1, property.getExtensions().size()); + assertEquals("ext-prop", property.getExtensions().get("x-ext")); + assertTrue(openAPI.getComponents().getSchemas().size() == 1); + + Schema inline = openAPI.getComponents().getSchemas().get("inline_response_200"); + assertTrue(inline instanceof Schema); + assertNotNull(inline.getProperties().get("name")); + assertTrue(inline.getProperties().get("name") instanceof StringSchema); + } + + @Test + public void testArrayResponse() { + OpenAPI openAPI = new OpenAPI(); + + + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.addProperties("name", new StringSchema()); + ArraySchema schema = new ArraySchema(); + schema.setItems(objectSchema); + + ApiResponse apiResponse = new ApiResponse(); + apiResponse.addExtension("x-foo", "bar"); + apiResponse.setDescription("it works!"); + apiResponse.setContent(new Content().addMediaType("*/*", new MediaType().schema(schema))); + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200", apiResponse)))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + assertTrue(response.getContent().get("*/*").getSchema() instanceof ArraySchema); + + ArraySchema am = (ArraySchema) response.getContent().get("*/*").getSchema(); + Schema items = am.getItems(); + assertTrue(items.get$ref() != null); + + assertEquals(items.get$ref(), "#/components/schemas/inline_response_200"); + + + Schema inline = openAPI.getComponents().getSchemas().get("inline_response_200"); + assertTrue(inline instanceof Schema); + + assertNotNull(inline.getProperties().get("name")); + assertTrue(inline.getProperties().get("name") instanceof StringSchema); + } + + @Test + public void testBasicInput() { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema user = new Schema(); + user.addProperties("name", new StringSchema()); + + openAPI.path("/foo/baz", new PathItem() + .post(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType().schema(new Schema().$ref("User"))))))); + + openAPI.getComponents().addSchemas("User", user); + + new InlineModelResolver().flatten(openAPI); + + Json.prettyPrint(openAPI); + } + + @Test + public void testArbitraryRequestBody() { + OpenAPI openAPI = new OpenAPI(); + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType().schema(new Schema())))))); + + new InlineModelResolver().flatten(openAPI); + + Operation operation = openAPI.getPaths().get("/hello").getGet(); + RequestBody requestBody = operation.getRequestBody(); + assertTrue(requestBody.getContent().get("*/*").getSchema() instanceof Schema); + Schema schema = requestBody.getContent().get("*/*").getSchema(); + assertNull(schema.getType()); + } + + @Test + public void testArbitraryObjectRequestBodyInline() { + OpenAPI swagger = new OpenAPI(); + + Schema schema = new Schema(); + schema.addProperties("arbitrary", new ObjectSchema()); + + swagger.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType().schema(schema)))))); + + new InlineModelResolver().flatten(swagger); + + Operation operation = swagger.getPaths().get("/hello").getGet(); + RequestBody requestBody = operation.getRequestBody(); + assertTrue(requestBody.getContent().get("*/*").getSchema().get$ref() != null); + + Schema body = swagger.getComponents().getSchemas().get("body"); + assertTrue(body instanceof Schema); + + + Schema property = (Schema) body.getProperties().get("arbitrary"); + assertNotNull(property); + assertTrue(property instanceof ObjectSchema); + } + + @Test + public void testArbitraryObjectBodyParamWithArray() { + OpenAPI openAPI = new OpenAPI(); + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType().schema(new ArraySchema() + .items(new ObjectSchema()))))))); + + new InlineModelResolver().flatten(openAPI); + + RequestBody requestBody = openAPI.getPaths().get("/hello").getGet().getRequestBody(); + + + Schema schema = requestBody.getContent().get("*/*").getSchema(); + + assertTrue(schema instanceof ArraySchema); + + ArraySchema arraySchema = (ArraySchema) schema; + Schema inner = arraySchema.getItems(); + assertTrue(inner instanceof ObjectSchema); + + ObjectSchema property = (ObjectSchema) inner; + assertNotNull(property); + assertNull(property.getProperties()); + } + + @Test + public void testArbitraryObjectBodyParamArrayInline() { + OpenAPI openAPI = new OpenAPI(); + + ObjectSchema items = new ObjectSchema(); + items.addProperties("arbitrary", new ObjectSchema()); + + openAPI.path("/hello", new PathItem() + .get(new Operation() + .requestBody(new RequestBody() + .content(new Content().addMediaType("*/*",new MediaType().schema(new ArraySchema().items(items))))))); + + new InlineModelResolver().flatten(openAPI); + + RequestBody requestBody = openAPI.getPaths().get("/hello").getGet().getRequestBody(); + + Schema schema = requestBody.getContent().get("*/*").getSchema(); + + assertTrue(schema instanceof ArraySchema); + + ArraySchema arraySchema = (ArraySchema) schema; + Schema inner = arraySchema.getItems(); + assertTrue(inner.get$ref() != null); + + + assertEquals(inner.get$ref(), "#/components/schemas/body"); + + Schema inline = openAPI.getComponents().getSchemas().get("body"); + assertNotNull(inline); + + Schema p = (Schema)inline.getProperties().get("arbitrary"); + assertNotNull(p); + assertTrue(p instanceof ObjectSchema); + } + + @Test + public void testArbitraryObjectResponse() { + OpenAPI openAPI = new OpenAPI(); + + openAPI.path("/foo/bar", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200", new ApiResponse() + .description("it works!") + .content(new Content().addMediaType("*/*", new MediaType().schema(new ObjectSchema()))))))); + + new InlineModelResolver().flatten(openAPI); + + Map responses = openAPI.getPaths().get("/foo/bar").getGet().getResponses(); + + ApiResponse response = responses.get("200"); + assertNotNull(response); + assertTrue(response.getContent().get("*/*").getSchema() instanceof ObjectSchema); + ObjectSchema op = (ObjectSchema) response.getContent().get("*/*").getSchema(); + assertNull(op.getProperties()); + } + + @Test + public void testArbitraryObjectResponseArray() { + OpenAPI openAPI = new OpenAPI(); + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200", new ApiResponse() + .description("it works!") + .content(new Content().addMediaType("*/*", new MediaType().schema(new ArraySchema() + .items(new ObjectSchema())))))))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + assertTrue(response.getContent().get("*/*").getSchema() instanceof ArraySchema); + + ArraySchema arraySchema = (ArraySchema) response.getContent().get("*/*").getSchema(); + Schema items = arraySchema.getItems(); + assertTrue(items instanceof ObjectSchema); + ObjectSchema op = (ObjectSchema) items; + assertNull(op.getProperties()); + } + + @Test + public void testArbitraryObjectResponseArrayInline() { + OpenAPI openAPI = new OpenAPI(); + + ArraySchema arraySchema = new ArraySchema(); + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.addProperties("arbitrary", new ObjectSchema()); + arraySchema.items(objectSchema); + + + ApiResponse apiResponse = new ApiResponse(); + apiResponse.addExtension("x-foo", "bar"); + apiResponse.description("it works!"); + apiResponse.content(new Content().addMediaType("*/*", new MediaType().schema(arraySchema))); + + + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200",apiResponse)))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + assertNotNull(response); + + assertNotNull(response.getContent().get("*/*").getSchema()); + Schema responseProperty = response.getContent().get("*/*").getSchema(); + assertTrue(responseProperty instanceof ArraySchema); + + ArraySchema arraySchema1 = (ArraySchema) responseProperty; + Schema items = arraySchema1.getItems(); + assertNotNull(items); + + assertEquals( "#/components/schemas/inline_response_200",items.get$ref()); + + Schema inline = openAPI.getComponents().getSchemas().get("inline_response_200"); + assertNotNull(inline); + assertTrue(inline instanceof Schema); + + Schema inlineProp = (Schema) inline.getProperties().get("arbitrary"); + assertNotNull(inlineProp); + assertTrue(inlineProp instanceof ObjectSchema); + assertNull(inlineProp.getProperties()); + } + + @Test + public void testArbitraryObjectResponseMapInline() { + OpenAPI openAPI = new OpenAPI(); + + Schema schema = new Schema(); + schema.setAdditionalProperties(new ObjectSchema()); + + openAPI.path("/foo/baz", new PathItem() + .get(new Operation() + .responses(new ApiResponses().addApiResponse("200", new ApiResponse() + .description("it works!") + .content(new Content().addMediaType("*/*", new MediaType().schema(schema))))))); + + new InlineModelResolver().flatten(openAPI); + + ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200"); + + Schema property = response.getContent().get("*/*").getSchema(); + assertTrue(property.getAdditionalProperties() != null); + assertTrue(openAPI.getComponents().getSchemas() == null); + Schema inlineProp = property.getAdditionalProperties(); + assertTrue(inlineProp instanceof ObjectSchema); + ObjectSchema op = (ObjectSchema) inlineProp; + assertNull(op.getProperties()); + } + + @Test + public void testArbitraryObjectModelInline() { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema userSchema = new Schema(); + userSchema.setName("user"); + userSchema.setDescription("a common user"); + userSchema.addProperties("name", new StringSchema()); + + ObjectSchema objectSchema = new ObjectSchema(); + objectSchema.setTitle("title"); + objectSchema.setDefault("default"); + objectSchema.setReadOnly(false); + objectSchema.setDescription("description"); + objectSchema.setName("name"); + + userSchema.addProperties("arbitrary", objectSchema); + List required = new ArrayList(); + required.add("arbitrary"); + userSchema.setRequired(required); + + + openAPI.getComponents().addSchemas("User", userSchema); + + new InlineModelResolver().flatten(openAPI); + + Schema user = openAPI.getComponents().getSchemas().get("User"); + assertNotNull(user); + Schema inlineProp = (Schema) user.getProperties().get("arbitrary"); + assertTrue(inlineProp instanceof ObjectSchema); + assertNull(inlineProp.getProperties()); + } + + @Test + public void testArbitraryObjectModelWithArrayInlineWithoutTitle() { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema items = new ObjectSchema(); + items.setDefault("default"); + items.setReadOnly(false); + items.setDescription("description"); + items.setName("name"); + items.addProperties("arbitrary", new ObjectSchema()); + + openAPI.getComponents().addSchemas("User", new ArraySchema().items(items).addRequiredItem("name")); + + new InlineModelResolver().flatten(openAPI); + + Schema model = openAPI.getComponents().getSchemas().get("User"); + assertTrue(model instanceof ArraySchema); + ArraySchema am = (ArraySchema) model; + Schema inner = am.getItems(); + assertTrue(inner.get$ref() != null); + + Schema userInner = openAPI.getComponents().getSchemas().get("User_inner"); + assertNotNull(userInner); + Schema inlineProp = (Schema)userInner.getProperties().get("arbitrary"); + assertTrue(inlineProp instanceof ObjectSchema); + ObjectSchema op = (ObjectSchema) inlineProp; + assertNull(op.getProperties()); + } + + @Test + public void testArbitraryObjectModelWithArrayInlineWithTitle() { + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + + Schema items = new ObjectSchema(); + items.setTitle("InnerUserTitle"); + items.setDefault("default"); + items.setReadOnly(false); + items.setDescription("description"); + items.setName("name"); + items.addProperties("arbitrary", new ObjectSchema()); + + openAPI.getComponents().addSchemas("User", new ArraySchema().items(items).addRequiredItem("name")); + + new InlineModelResolver().flatten(openAPI); + + Schema model = openAPI.getComponents().getSchemas().get("User"); + assertTrue(model instanceof ArraySchema); + ArraySchema am = (ArraySchema) model; + Schema inner = am.getItems(); + assertTrue(inner.get$ref() != null); + + Schema userInner = openAPI.getComponents().getSchemas().get("InnerUserTitle"); + assertNotNull(userInner); + Schema inlineProp = (Schema) userInner.getProperties().get("arbitrary"); + assertTrue(inlineProp instanceof ObjectSchema); + ObjectSchema op = (ObjectSchema) inlineProp; + assertNull(op.getProperties()); + } +} \ No newline at end of file diff --git a/modules/swagger-parser-v3/src/test/resources/flatten.json b/modules/swagger-parser-v3/src/test/resources/flatten.json new file mode 100644 index 0000000000..7535fbaa49 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/flatten.json @@ -0,0 +1,30 @@ +{ + "openapi" : "3.0.0", + "components" : { + "schemas" : { + "User" : { + "required" : [ "address" ], + "properties" : { + "name" : { + "type" : "string" + }, + "address" : { + "type" : "object", + "properties" : { + "street" : { + "type" : "string" + }, + "city" : { + "type" : "string" + } + }, + "description" : "description", + "readOnly" : false, + "default" : "default" + } + }, + "description" : "a common user" + } + } + } +} \ No newline at end of file