From 72ffc95d6e2d754a0c438a5b587a593e8c5aef58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Bresson?= Date: Sat, 21 Apr 2018 15:45:43 +0200 Subject: [PATCH] Consider '$ref' for 'getProducesInfo' and 'getConsumesInfo' (#176) --- .../openapitools/codegen/DefaultCodegen.java | 67 ++++-------- .../languages/AbstractJavaCodegen.java | 12 +-- .../codegen/languages/BashClientCodegen.java | 4 +- .../codegen/languages/RustServerCodegen.java | 4 +- .../codegen/utils/ModelUtils.java | 102 +++++++++++++++--- .../codegen/DefaultCodegenTest.java | 43 ++++++++ 6 files changed, 161 insertions(+), 71 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 77ac8431f46..e1dfe4a27dd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -2147,19 +2147,19 @@ public class DefaultCodegen implements CodegenConfig { ArraySchema as = (ArraySchema) responseSchema; if (as.getItems() != null && StringUtils.isEmpty(as.getItems().get$ref())) { // arary of primtive types op.examples = new ExampleGenerator(schemas).generate((Map) responseSchema.getExample(), - new ArrayList(getProducesInfo(operation)), as.getItems(), openAPI); + new ArrayList(getProducesInfo(openAPI, operation)), as.getItems(), openAPI); } else if (as.getItems() != null && !StringUtils.isEmpty(as.getItems().get$ref())) { // array of model op.examples = new ExampleGenerator(schemas).generate((Map) responseSchema.getExample(), - new ArrayList(getProducesInfo(operation)), getSimpleRef(as.getItems().get$ref()), openAPI); + new ArrayList(getProducesInfo(openAPI, operation)), getSimpleRef(as.getItems().get$ref()), openAPI); } else { // TODO log warning message as such case is not handled at the moment } } else if (StringUtils.isEmpty(responseSchema.get$ref())) { // primtiive type (e.g. integer, string) op.examples = new ExampleGenerator(schemas).generate((Map) responseSchema.getExample(), - new ArrayList(getProducesInfo(operation)), responseSchema, openAPI); + new ArrayList(getProducesInfo(openAPI, operation)), responseSchema, openAPI); } else { // model op.examples = new ExampleGenerator(schemas).generate((Map) responseSchema.getExample(), - new ArrayList(getProducesInfo(operation)), getSimpleRef(responseSchema.get$ref()), openAPI); + new ArrayList(getProducesInfo(openAPI, operation)), getSimpleRef(responseSchema.get$ref()), openAPI); } op.defaultResponse = toDefaultValue(responseSchema); @@ -2234,7 +2234,7 @@ public class DefaultCodegen implements CodegenConfig { // add example if (schemas != null) { - op.requestBodyExamples = new ExampleGenerator(schemas).generate(null, new ArrayList(getConsumesInfo(operation)), bodyParam.baseType, openAPI); + op.requestBodyExamples = new ExampleGenerator(schemas).generate(null, new ArrayList(getConsumesInfo(openAPI, operation)), bodyParam.baseType, openAPI); } } } @@ -3886,62 +3886,39 @@ public class DefaultCodegen implements CodegenConfig { } } - public static Set getConsumesInfo(Operation operation) { - if (operation.getRequestBody() == null || operation.getRequestBody().getContent() == null || operation.getRequestBody().getContent().isEmpty()) { + public static Set getConsumesInfo(OpenAPI openAPI, Operation operation) { + RequestBody requestBody = ModelUtils.getReferencedRequestBody(openAPI, operation.getRequestBody()); + + if (requestBody == null || requestBody.getContent() == null || requestBody.getContent().isEmpty()) { return Collections.emptySet(); // return emtpy set } - return operation.getRequestBody().getContent().keySet(); + return requestBody.getContent().keySet(); } - public Boolean hasFormParameter(Operation operation) { - Set consumesInfo = getConsumesInfo(operation); + public boolean hasFormParameter(OpenAPI openAPI, Operation operation) { + Set consumesInfo = getConsumesInfo(openAPI, operation); if (consumesInfo == null || consumesInfo.isEmpty()) { - return Boolean.FALSE; + return false; } - List consumes = new ArrayList(consumesInfo); - - if (consumes == null) { - return Boolean.FALSE; - } - - for (String consume : consumes) { + for (String consume : consumesInfo) { if ("application/x-www-form-urlencoded".equalsIgnoreCase(consume) || "multipart/form-data".equalsIgnoreCase(consume)) { - return Boolean.TRUE; + return true; } } - return Boolean.FALSE; + return false; } public boolean hasBodyParameter(OpenAPI openAPI, Operation operation) { - RequestBody requestBody = operation.getRequestBody(); + RequestBody requestBody = ModelUtils.getReferencedRequestBody(openAPI, operation.getRequestBody()); if (requestBody == null) { return false; } - if (StringUtils.isNotEmpty(requestBody.get$ref())) { - String name = ModelUtils.getSimpleRef(requestBody.get$ref()); - requestBody = ModelUtils.getRequestBody(openAPI, name); - if (requestBody == null) { - return false; - } - } - Schema schema = getSchemaFromBody(requestBody); - if (schema == null) { - return false; - } - - if (StringUtils.isNotEmpty(schema.get$ref())) { - String name = ModelUtils.getSimpleRef(schema.get$ref()); - schema = ModelUtils.getSchema(openAPI, name); - if (schema == null) { - return false; - } - } - return true; + return ModelUtils.getReferencedSchema(openAPI, schema) != null; } private void addProducesInfo(ApiResponse response, CodegenOperation codegenOperation) { @@ -3978,18 +3955,20 @@ public class DefaultCodegen implements CodegenConfig { /** * returns the list of MIME types the APIs can produce - * + * @param openAPI * @param operation Operation + * * @return a set of MIME types */ - public static Set getProducesInfo(Operation operation) { + public static Set getProducesInfo(OpenAPI openAPI, Operation operation) { if (operation.getResponses() == null || operation.getResponses().isEmpty()) { return null; } Set produces = new TreeSet(); - for (ApiResponse response : operation.getResponses().values()) { + for (ApiResponse r : operation.getResponses().values()) { + ApiResponse response = ModelUtils.getReferencedApiResponse(openAPI, r); if (response.getContent() != null) { produces.addAll(response.getContent().keySet()); } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index 0ee034048c2..c1a2b1452ee 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -933,23 +933,23 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code continue; } for (Operation operation : path.readOperations()) { - if (hasBodyParameter(openAPI, operation) || hasFormParameter(operation)) { - String defaultContentType = hasFormParameter(operation) ? "application/x-www-form-urlencoded" : "application/json"; - List consumes = new ArrayList(getConsumesInfo(operation)); + if (hasBodyParameter(openAPI, operation) || hasFormParameter(openAPI, operation)) { + String defaultContentType = hasFormParameter(openAPI, operation) ? "application/x-www-form-urlencoded" : "application/json"; + List consumes = new ArrayList(getConsumesInfo(openAPI, operation)); String contentType = consumes == null || consumes.isEmpty() ? defaultContentType : consumes.get(0); operation.addExtension("x-contentType", contentType); } - String accepts = getAccept(operation); + String accepts = getAccept(openAPI, operation); operation.addExtension("x-accepts", accepts); } } } - protected static String getAccept(Operation operation) { + protected static String getAccept(OpenAPI openAPI, Operation operation) { String accepts = null; String defaultContentType = "application/json"; - ArrayList produces = new ArrayList(getProducesInfo(operation)); + ArrayList produces = new ArrayList(getProducesInfo(openAPI, operation)); if (produces != null && !produces.isEmpty()) { StringBuilder sb = new StringBuilder(); for (String produce : produces) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java index ff60d660293..5ac361402bf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/BashClientCodegen.java @@ -606,8 +606,8 @@ public class BashClientCodegen extends DefaultCodegen implements CodegenConfig { * If the operation produces Json and has nonempty example * try to reformat it. */ - if (getConsumesInfo(operation) != null - && getConsumesInfo(operation).contains("application/json") + if (getConsumesInfo(openAPI, operation) != null + && getConsumesInfo(openAPI, operation).contains("application/json") && definitions.get(p.dataType).getExample() != null) { ObjectMapper mapper = new ObjectMapper(); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index f0a64b3ab82..22a187907cd 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -507,7 +507,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { boolean consumesXml = false; // if "consumes" is defined (per operation or using global definition) if (consumes != null && !consumes.isEmpty()) { - consumes.addAll(getConsumesInfo(operation)); + consumes.addAll(getConsumesInfo(openAPI, operation)); List> c = new ArrayList>(); for (String mimeType : consumes) { Map mediaType = new HashMap(); @@ -529,7 +529,7 @@ public class RustServerCodegen extends DefaultCodegen implements CodegenConfig { } - List produces = new ArrayList(getProducesInfo(operation)); + List produces = new ArrayList(getProducesInfo(openAPI, operation)); // if "consumes" is defined (per operation or using global definition) /* if (operation.getProduces() != null) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 75f20df61b1..3edbe8b11c9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -3,9 +3,29 @@ package org.openapitools.codegen.utils; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; -import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.media.ArraySchema; +import io.swagger.v3.oas.models.media.BinarySchema; +import io.swagger.v3.oas.models.media.BooleanSchema; +import io.swagger.v3.oas.models.media.ByteArraySchema; +import io.swagger.v3.oas.models.media.ComposedSchema; +import io.swagger.v3.oas.models.media.DateSchema; +import io.swagger.v3.oas.models.media.DateTimeSchema; +import io.swagger.v3.oas.models.media.EmailSchema; +import io.swagger.v3.oas.models.media.FileSchema; +import io.swagger.v3.oas.models.media.IntegerSchema; +import io.swagger.v3.oas.models.media.MapSchema; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.NumberSchema; +import io.swagger.v3.oas.models.media.ObjectSchema; +import io.swagger.v3.oas.models.media.PasswordSchema; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.media.StringSchema; +import io.swagger.v3.oas.models.media.UUIDSchema; import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.parser.util.SchemaTypeUtil; + +import org.apache.commons.lang3.StringUtils; import org.openapitools.codegen.CodegenModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,8 +35,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import org.apache.commons.lang3.StringUtils; - public class ModelUtils { static Logger LOGGER = LoggerFactory.getLogger(ModelUtils.class); @@ -99,7 +117,6 @@ public class ModelUtils { return unusedSchemas; } - // todo change it to public later public static String getSimpleRef(String ref) { if (ref.startsWith("#/components/")) { ref = ref.substring(ref.lastIndexOf("/") + 1); @@ -309,28 +326,79 @@ public class ModelUtils { return false; } - public static Schema getSchema(OpenAPI openapi, String name) { - if (name == null) { - return null; + /** + * If a Schema contains a reference to an other Schema with '$ref', returns the referenced Schema or the actual Schema in the other cases. + * @param openAPI + * @param schema potentially containing a '$ref' + * @return schema without '$ref' + */ + public static Schema getReferencedSchema(OpenAPI openAPI, Schema schema) { + if (schema != null && StringUtils.isNotEmpty(schema.get$ref())) { + String name = getSimpleRef(schema.get$ref()); + return getSchema(openAPI, name); } - - if (openapi != null && openapi.getComponents() != null && openapi.getComponents().getSchemas() != null) { - return openapi.getComponents().getSchemas().get(name); - } - - return null; + return schema; } - public static RequestBody getRequestBody(OpenAPI openapi, String name) { + public static Schema getSchema(OpenAPI openAPI, String name) { if (name == null) { return null; } - if (openapi != null && openapi.getComponents() != null && openapi.getComponents().getRequestBodies() != null) { - return openapi.getComponents().getRequestBodies().get(name); + if (openAPI != null && openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) { + return openAPI.getComponents().getSchemas().get(name); } - + return null; } + /** + * If a RequestBody contains a reference to an other RequestBody with '$ref', returns the referenced RequestBody or the actual RequestBody in the other cases. + * @param openAPI + * @param requestBody potentially containing a '$ref' + * @return requestBody without '$ref' + */ + public static RequestBody getReferencedRequestBody(OpenAPI openAPI, RequestBody requestBody) { + if (requestBody != null && StringUtils.isNotEmpty(requestBody.get$ref())) { + String name = getSimpleRef(requestBody.get$ref()); + return getRequestBody(openAPI, name); + } + return requestBody; + } + + public static RequestBody getRequestBody(OpenAPI openAPI, String name) { + if (name == null) { + return null; + } + + if (openAPI != null && openAPI.getComponents() != null && openAPI.getComponents().getRequestBodies() != null) { + return openAPI.getComponents().getRequestBodies().get(name); + } + return null; + } + + /** + * If a ApiResponse contains a reference to an other ApiResponse with '$ref', returns the referenced ApiResponse or the actual ApiResponse in the other cases. + * @param openAPI + * @param apiResponse potentially containing a '$ref' + * @return apiResponse without '$ref' + */ + public static ApiResponse getReferencedApiResponse(OpenAPI openAPI, ApiResponse apiResponse) { + if (apiResponse != null && StringUtils.isNotEmpty(apiResponse.get$ref())) { + String name = getSimpleRef(apiResponse.get$ref()); + return getApiResponse(openAPI, name); + } + return apiResponse; + } + + public static ApiResponse getApiResponse(OpenAPI openAPI, String name) { + if (name == null) { + return null; + } + + if (openAPI != null && openAPI.getComponents() != null && openAPI.getComponents().getRequestBodies() != null) { + return openAPI.getComponents().getResponses().get(name); + } + return null; + } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index 1567dd30643..1debc28643b 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -14,6 +14,8 @@ import io.swagger.v3.oas.models.responses.ApiResponses; import org.testng.Assert; import org.testng.annotations.Test; +import java.util.Set; + public class DefaultCodegenTest { @Test @@ -39,6 +41,47 @@ public class DefaultCodegenTest { Assert.assertEquals(codegen.hasBodyParameter(openAPI, pingOperation), false); Assert.assertEquals(codegen.hasBodyParameter(openAPI, createOperation), true); } + + @Test + public void testGetConsumesInfoAndGetProducesInfo() throws Exception { + final Schema refSchema = new Schema<>().$ref("#/components/schemas/Pet"); + OpenAPI openAPI = new OpenAPI(); + openAPI.setComponents(new Components()); + openAPI.getComponents().addSchemas("Pet", new ObjectSchema()); + openAPI.getComponents().addRequestBodies("MyRequestBody", new RequestBody() + .content(new Content().addMediaType("application/json", + new MediaType().schema(refSchema)))); + openAPI.getComponents().addResponses("MyResponse", new ApiResponse() + .description("Ok response") + .content(new Content().addMediaType("application/xml", + new MediaType().schema(refSchema)))); + + Operation createOperation = new Operation() + .requestBody(new RequestBody() + .content(new Content() + .addMediaType("application/json", new MediaType().schema(refSchema)) + .addMediaType("application/xml", new MediaType().schema(refSchema)) + )) + .responses( + new ApiResponses().addApiResponse("201", new ApiResponse() + .description("Created response"))); + Set createConsumesInfo = DefaultCodegen.getConsumesInfo(openAPI, createOperation); + Assert.assertEquals(createConsumesInfo.size(), 2); + Assert.assertTrue(createConsumesInfo.contains("application/json"), "contains 'application/json'"); + Assert.assertTrue(createConsumesInfo.contains("application/xml"), "contains 'application/xml'"); + Set createProducesInfo = DefaultCodegen.getProducesInfo(openAPI, createOperation); + Assert.assertEquals(createProducesInfo.size(), 0); + + Operation updateOperationWithRef = new Operation() + .requestBody(new RequestBody().$ref("#/components/requestBodies/MyRequestBody")) + .responses(new ApiResponses().addApiResponse("201", new ApiResponse().$ref("#/components/responses/MyResponse"))); + Set updateConsumesInfo = DefaultCodegen.getConsumesInfo(openAPI, updateOperationWithRef); + Assert.assertEquals(updateConsumesInfo.size(), 1); + Assert.assertTrue(updateConsumesInfo.contains("application/json"), "contains 'application/json'"); + Set updateProducesInfo = DefaultCodegen.getProducesInfo(openAPI, updateOperationWithRef); + Assert.assertEquals(updateProducesInfo.size(), 1); + Assert.assertTrue(updateProducesInfo.contains("application/xml"), "contains 'application/xml'"); + } @Test public void testInitialConfigValues() throws Exception {