Apply style and explode values from encoding for form-encoded request body parameters (#12162)

This commit is contained in:
Katsuyuki Omuro 2022-05-20 00:13:54 +09:00 committed by GitHub
parent 5578f178d4
commit c3976a4a4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 148 additions and 11 deletions

View File

@ -1932,16 +1932,49 @@ public class DefaultCodegen implements CodegenConfig {
} }
/** /**
* 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 codegenParameter Codegen parameter
* @param mediaType MediaType from the request body * @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) { if (mediaType != null && mediaType.getEncoding() != null) {
Encoding encoding = mediaType.getEncoding().get(codegenParameter.baseName); Encoding encoding = mediaType.getEncoding().get(codegenParameter.baseName);
if (encoding != null) { if (encoding != null) {
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.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 { } else {
LOGGER.debug("encoding not specified for {}", codegenParameter.baseName); LOGGER.debug("encoding not specified for {}", codegenParameter.baseName);
} }
@ -4087,7 +4120,7 @@ public class DefaultCodegen implements CodegenConfig {
formParams = fromRequestBodyToFormParameters(requestBody, imports); formParams = fromRequestBodyToFormParameters(requestBody, imports);
op.isMultipart = contentType.startsWith("multipart"); op.isMultipart = contentType.startsWith("multipart");
for (CodegenParameter cp : formParams) { for (CodegenParameter cp : formParams) {
setParameterContentType(cp, requestBody.getContent().get(contentType)); setParameterEncodingValues(cp, requestBody.getContent().get(contentType));
postProcessParameter(cp); postProcessParameter(cp);
} }
// add form parameters to the beginning of all parameter list // add form parameters to the beginning of all parameter list
@ -6398,11 +6431,12 @@ public class DefaultCodegen implements CodegenConfig {
LOGGER.warn("Could not compute datatypeWithEnum from {}, {}", arrayInnerProperty.baseType, arrayInnerProperty.enumName); LOGGER.warn("Could not compute datatypeWithEnum from {}, {}", arrayInnerProperty.baseType, arrayInnerProperty.enumName);
} }
// end of hoisting // 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); String collectionFormat = getCollectionFormat(codegenParameter);
// default to csv:
codegenParameter.collectionFormat = StringUtils.isEmpty(collectionFormat) ? "csv" : collectionFormat; codegenParameter.collectionFormat = StringUtils.isEmpty(collectionFormat) ? "csv" : collectionFormat;
codegenParameter.isCollectionFormatMulti = "multi".equals(collectionFormat);
// recursively add import // recursively add import
while (arrayInnerProperty != null) { while (arrayInnerProperty != null) {
@ -6444,8 +6478,6 @@ public class DefaultCodegen implements CodegenConfig {
// set nullable // set nullable
setParameterNullable(codegenParameter, codegenProperty); setParameterNullable(codegenParameter, codegenProperty);
//TODO collectionFormat for form parameter not yet supported
//codegenParameter.collectionFormat = getCollectionFormat(propertySchema);
return codegenParameter; return codegenParameter;
} }
@ -7367,15 +7399,31 @@ public class DefaultCodegen implements CodegenConfig {
} }
/** /**
* 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}. * for the {@link CodegenParameter}.
* *
* @param codegenParameter parameter * @param codegenParameter parameter
* @return string for a collectionFormat. * @return string for a collectionFormat.
*/ */
protected String getCollectionFormat(CodegenParameter codegenParameter) { protected String getCollectionFormat(CodegenParameter codegenParameter) {
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; return null;
} }
}
private CodegenComposedSchemas getComposedSchemas(Schema schema) { private CodegenComposedSchemas getComposedSchemas(Schema schema) {
if (!(schema instanceof ComposedSchema) && schema.getNot()==null) { if (!(schema instanceof ComposedSchema) && schema.getNot()==null) {

View File

@ -4064,6 +4064,44 @@ public class DefaultCodegenTest {
cp = mt.getSchema(); cp = mt.getSchema();
assertEquals(cp.baseName, "SchemaForRequestBodyTextPlain"); assertEquals(cp.baseName, "SchemaForRequestBodyTextPlain");
assertTrue(cp.isString); assertTrue(cp.isString);
path = "/requestBodyWithEncodingTypes";
co = codegen.fromOperation(path, "POST", openAPI.getPaths().get(path).getPost(), null);
List<CodegenParameter> 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 @Test

View File

@ -98,6 +98,57 @@ paths:
responses: responses:
200: 200:
description: OK 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: components:
headers: headers:
X-Rate-Limit: X-Rate-Limit: