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 1dc1cd8713b5..4e84aa1e7cc8 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 @@ -1925,16 +1925,49 @@ public void setParameterExampleValue(CodegenParameter codegenParameter, RequestB } /** - * Sets the content type of the parameter based on the encoding specified in the request body. + * Sets the content type, style, and explode of the parameter based on the encoding specified + * in the request body. * * @param codegenParameter Codegen parameter * @param mediaType MediaType from the request body */ - public void setParameterContentType(CodegenParameter codegenParameter, MediaType mediaType) { + public void setParameterEncodingValues(CodegenParameter codegenParameter, MediaType mediaType) { if (mediaType != null && mediaType.getEncoding() != null) { Encoding encoding = mediaType.getEncoding().get(codegenParameter.baseName); if (encoding != null) { - codegenParameter.contentType = encoding.getContentType(); + boolean styleGiven = true; + Encoding.StyleEnum style = encoding.getStyle(); + if(style == null || style == Encoding.StyleEnum.FORM) { + // (Unfortunately, swagger-parser-v3 will always provide 'form' + // when style is not specified, so we can't detect that) + style = Encoding.StyleEnum.FORM; + styleGiven = false; + } + boolean explodeGiven = true; + Boolean explode = encoding.getExplode(); + if(explode == null) { + explode = style == Encoding.StyleEnum.FORM; // Default to True when form, False otherwise + explodeGiven = false; + } + + if(!styleGiven && !explodeGiven) { + // Ignore contentType if style or explode are specified. + codegenParameter.contentType = encoding.getContentType(); + } + + codegenParameter.style = style.toString(); + codegenParameter.isDeepObject = Encoding.StyleEnum.DEEP_OBJECT == style; + + if(codegenParameter.isContainer) { + codegenParameter.isExplode = explode; + String collectionFormat = getCollectionFormat(codegenParameter); + codegenParameter.collectionFormat = StringUtils.isEmpty(collectionFormat) ? "csv" : collectionFormat; + codegenParameter.isCollectionFormatMulti = "multi".equals(collectionFormat); + } else { + codegenParameter.isExplode = false; + codegenParameter.collectionFormat = null; + codegenParameter.isCollectionFormatMulti = false; + } } else { LOGGER.debug("encoding not specified for {}", codegenParameter.baseName); } @@ -4080,7 +4113,7 @@ public CodegenOperation fromOperation(String path, formParams = fromRequestBodyToFormParameters(requestBody, imports); op.isMultipart = contentType.startsWith("multipart"); for (CodegenParameter cp : formParams) { - setParameterContentType(cp, requestBody.getContent().get(contentType)); + setParameterEncodingValues(cp, requestBody.getContent().get(contentType)); postProcessParameter(cp); } // add form parameters to the beginning of all parameter list @@ -6391,11 +6424,12 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set LOGGER.warn("Could not compute datatypeWithEnum from {}, {}", arrayInnerProperty.baseType, arrayInnerProperty.enumName); } // end of hoisting - //TODO fix collectionFormat for form parameters - //collectionFormat = getCollectionFormat(s); + + // collectionFormat for form parameter does not consider + // style and explode from encoding at this point String collectionFormat = getCollectionFormat(codegenParameter); - // default to csv: codegenParameter.collectionFormat = StringUtils.isEmpty(collectionFormat) ? "csv" : collectionFormat; + codegenParameter.isCollectionFormatMulti = "multi".equals(collectionFormat); // recursively add import while (arrayInnerProperty != null) { @@ -6437,8 +6471,6 @@ public CodegenParameter fromFormProperty(String name, Schema propertySchema, Set // set nullable setParameterNullable(codegenParameter, codegenProperty); - //TODO collectionFormat for form parameter not yet supported - //codegenParameter.collectionFormat = getCollectionFormat(propertySchema); return codegenParameter; } @@ -7360,14 +7392,30 @@ protected static boolean isJsonVendorMimeType(String mime) { } /** - * Returns null by default but can be overwritten to return a valid collectionFormat + * Builds OAPI 2.0 collectionFormat value based on style and explode values * for the {@link CodegenParameter}. * * @param codegenParameter parameter * @return string for a collectionFormat. */ protected String getCollectionFormat(CodegenParameter codegenParameter) { - return null; + if ("form".equals(codegenParameter.style)) { + // Ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#style-values + if (codegenParameter.isExplode) { + return "multi"; + } else { + return "csv"; + } + } else if ("simple".equals(codegenParameter.style)) { + return "csv"; + } else if ("pipeDelimited".equals(codegenParameter.style)) { + return "pipes"; + } else if ("spaceDelimited".equals(codegenParameter.style)) { + return "ssv"; + } else { + // Doesn't map to any of the collectionFormat strings + return null; + } } private CodegenComposedSchemas getComposedSchemas(Schema schema) { 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 a7fd0ebb462c..94037dbeace4 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 @@ -4049,6 +4049,44 @@ public void testRequestBodyContent() { cp = mt.getSchema(); assertEquals(cp.baseName, "SchemaForRequestBodyTextPlain"); assertTrue(cp.isString); + + path = "/requestBodyWithEncodingTypes"; + co = codegen.fromOperation(path, "POST", openAPI.getPaths().get(path).getPost(), null); + List formParams = co.formParams; + + assertEquals(formParams.get(0).paramName, "intParam"); + assertFalse(formParams.get(0).isContainer); + assertFalse(formParams.get(0).isExplode); // Should not be true for non-container + + assertEquals(formParams.get(1).paramName, "explodeTrue"); + assertTrue(formParams.get(1).isContainer); + assertEquals(formParams.get(1).style, Encoding.StyleEnum.FORM.toString()); + assertTrue(formParams.get(1).isExplode); + assertNull(formParams.get(1).contentType); + + assertEquals(formParams.get(2).paramName, "explodeFalse"); + assertTrue(formParams.get(2).isContainer); + assertEquals(formParams.get(2).style, Encoding.StyleEnum.FORM.toString()); + assertFalse(formParams.get(2).isExplode); + assertNull(formParams.get(2).contentType); + + assertEquals(formParams.get(3).paramName, "noStyleNoExplode"); + assertTrue(formParams.get(3).isContainer); + assertEquals(formParams.get(3).style, Encoding.StyleEnum.FORM.toString()); + assertTrue(formParams.get(3).isExplode); // Defaults to true for style == FORM + assertEquals(formParams.get(3).contentType, "text/plain"); + + assertEquals(formParams.get(4).paramName, "styleSpecified"); + assertTrue(formParams.get(4).isContainer); + assertEquals(formParams.get(4).style, Encoding.StyleEnum.SPACE_DELIMITED.toString()); + assertFalse(formParams.get(4).isExplode); + assertNull(formParams.get(4).contentType); + + assertEquals(formParams.get(5).paramName, "styleSpecifiedNoExplode"); + assertTrue(formParams.get(5).isContainer); + assertEquals(formParams.get(5).style, Encoding.StyleEnum.SPACE_DELIMITED.toString()); + assertFalse(formParams.get(5).isExplode); // Defaults to false for style other than FORM + assertNull(formParams.get(5).contentType); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_0/content-data.yaml b/modules/openapi-generator/src/test/resources/3_0/content-data.yaml index ae3966447538..033677693b4d 100644 --- a/modules/openapi-generator/src/test/resources/3_0/content-data.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/content-data.yaml @@ -98,6 +98,57 @@ paths: responses: 200: description: OK + /requestBodyWithEncodingTypes: + post: + requestBody: + content: + application/x-www-form-urlencoded: + schema: + type: object + properties: + int-param: + type: integer + explode-true: + type: array + items: + type: string + explode-false: + type: array + items: + type: string + no-style-no-explode: + type: array + items: + type: string + style-specified: + type: array + items: + type: string + style-specified-no-explode: + type: array + items: + type: string + encoding: + int-param: + explode: true # should be disregarded for non-container + explode-true: + contentType: text/plain # should be disregarded + explode: true + explode-false: + contentType: text/plain # should be disregarded + explode: false + no-style-no-explode: + contentType: text/plain + style-specified: + contentType: text/plain # should be disregarded + style: spaceDelimited + explode: false + style-specified-no-explode: + contentType: text/plain # should be disregarded + style: spaceDelimited + responses: + 200: + description: OK components: headers: X-Rate-Limit: