Skip to content

Commit e3cdb4c

Browse files
authored
[Java][jersey2] Add nullable body support (#6784)
* add nullable body support * update serializeToString with nullable body * add nullable support to body parameter * minor code format change * Revert "minor code format change" This reverts commit 3af1838. * code format fix
1 parent d949c81 commit e3cdb4c

File tree

19 files changed

+161
-121
lines changed

19 files changed

+161
-121
lines changed

bin/configs/java-jersey2-8.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ generatorName: java
22
outputDir: samples/openapi3/client/petstore/java/jersey2-java8
33
library: jersey2
44
inputSpec: modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-with-http-signature.yaml
5+
templateDir: modules/openapi-generator/src/main/resources/Java
56
additionalProperties:
67
artifactId: petstore-openapi3-jersey2-java8
78
hideGenerationTimestamp: true

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,20 +1486,20 @@ public DefaultCodegen() {
14861486

14871487
// option to change how we process + set the data in the 'additionalProperties' keyword.
14881488
CliOption disallowAdditionalPropertiesIfNotPresentOpt = CliOption.newBoolean(
1489-
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
1490-
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString());
1489+
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT,
1490+
CodegenConstants.DISALLOW_ADDITIONAL_PROPERTIES_IF_NOT_PRESENT_DESC).defaultValue(Boolean.TRUE.toString());
14911491
Map<String, String> disallowAdditionalPropertiesIfNotPresentOpts = new HashMap<>();
14921492
disallowAdditionalPropertiesIfNotPresentOpts.put("false",
1493-
"The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
1493+
"The 'additionalProperties' implementation is compliant with the OAS and JSON schema specifications.");
14941494
disallowAdditionalPropertiesIfNotPresentOpts.put("true",
1495-
"when the 'additionalProperties' keyword is not present in a schema, " +
1496-
"the value of 'additionalProperties' is automatically set to false, i.e. no additional properties are allowed. " +
1497-
"Note: this mode is not compliant with the JSON schema specification. " +
1498-
"This is the original openapi-generator behavior.");
1495+
"when the 'additionalProperties' keyword is not present in a schema, " +
1496+
"the value of 'additionalProperties' is automatically set to false, i.e. no additional properties are allowed. " +
1497+
"Note: this mode is not compliant with the JSON schema specification. " +
1498+
"This is the original openapi-generator behavior.");
14991499
disallowAdditionalPropertiesIfNotPresentOpt.setEnum(disallowAdditionalPropertiesIfNotPresentOpts);
15001500
cliOptions.add(disallowAdditionalPropertiesIfNotPresentOpt);
15011501
this.setDisallowAdditionalPropertiesIfNotPresent(true);
1502-
1502+
15031503
// initialize special character mapping
15041504
initalizeSpecialCharacterMapping();
15051505

@@ -2640,7 +2640,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) {
26402640
Integer hasDiscriminatorCnt = 0;
26412641
Integer hasNullTypeCnt = 0;
26422642
Set<String> discriminatorsPropNames = new HashSet<>();
2643-
for (Schema oneOf: composedSchema.getOneOf()) {
2643+
for (Schema oneOf : composedSchema.getOneOf()) {
26442644
if (ModelUtils.isNullType(oneOf)) {
26452645
// The null type does not have a discriminator. Skip.
26462646
hasNullTypeCnt++;
@@ -2654,7 +2654,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) {
26542654
}
26552655
if (discriminatorsPropNames.size() > 1) {
26562656
throw new RuntimeException("The oneOf schemas have conflicting discriminator property names. " +
2657-
"oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames));
2657+
"oneOf schemas must have the same property name, but found " + String.join(", ", discriminatorsPropNames));
26582658
}
26592659
if ((hasDiscriminatorCnt + hasNullTypeCnt) == composedSchema.getOneOf().size() && discriminatorsPropNames.size() == 1) {
26602660
disc.setPropertyName(foundDisc.getPropertyName());
@@ -2669,7 +2669,7 @@ private Discriminator recursiveGetDiscriminator(Schema sc, OpenAPI openAPI) {
26692669
Integer hasDiscriminatorCnt = 0;
26702670
Integer hasNullTypeCnt = 0;
26712671
Set<String> discriminatorsPropNames = new HashSet<>();
2672-
for (Schema anyOf: composedSchema.getAnyOf()) {
2672+
for (Schema anyOf : composedSchema.getAnyOf()) {
26732673
if (ModelUtils.isNullType(anyOf)) {
26742674
// The null type does not have a discriminator. Skip.
26752675
hasNullTypeCnt++;
@@ -2720,7 +2720,7 @@ protected List<MappedModel> getOneOfAnyOfDescendants(String composedSchemaName,
27202720
if (schemaList == null) {
27212721
continue;
27222722
}
2723-
for (Schema sc: schemaList) {
2723+
for (Schema sc : schemaList) {
27242724
if (ModelUtils.isNullType(sc)) {
27252725
continue;
27262726
}
@@ -2875,7 +2875,7 @@ protected CodegenDiscriminator createDiscriminator(String schemaName, Schema sch
28752875
* Handle the model for the 'additionalProperties' keyword in the OAS schema.
28762876
*
28772877
* @param codegenModel The codegen representation of the schema.
2878-
* @param schema the input OAS schema.
2878+
* @param schema The input OAS schema.
28792879
*/
28802880
protected void addAdditionPropertiesToCodeGenModel(CodegenModel codegenModel, Schema schema) {
28812881
addParentContainer(codegenModel, codegenModel.name, schema);
@@ -3503,7 +3503,7 @@ protected void handleMethodResponse(Operation operation,
35033503
op.returnType = cm.dataType;
35043504
op.returnFormat = cm.dataFormat;
35053505
op.hasReference = schemas != null && schemas.containsKey(op.returnBaseType);
3506-
3506+
35073507
// lookup discriminator
35083508
Schema schema = schemas.get(op.returnBaseType);
35093509
if (schema != null) {
@@ -3965,8 +3965,8 @@ public CodegenResponse fromResponse(String responseCode, ApiResponse response) {
39653965
r.containerType = cp.containerType;
39663966
r.isMapContainer = "map".equals(cp.containerType);
39673967
r.isListContainer = "list".equalsIgnoreCase(cp.containerType) ||
3968-
"array".equalsIgnoreCase(cp.containerType) ||
3969-
"set".equalsIgnoreCase(cp.containerType);
3968+
"array".equalsIgnoreCase(cp.containerType) ||
3969+
"set".equalsIgnoreCase(cp.containerType);
39703970
} else {
39713971
r.simpleType = true;
39723972
}
@@ -4317,7 +4317,7 @@ public boolean isDataTypeBinary(String dataType) {
43174317
return false;
43184318
}
43194319
}
4320-
4320+
43214321
// TODO revise below as it should be replaced by ModelUtils.isFileSchema(parameterSchema)
43224322
public boolean isDataTypeFile(String dataType) {
43234323
if (dataType != null) {
@@ -5795,6 +5795,7 @@ private void addBodyModelSchema(CodegenParameter codegenParameter, String name,
57955795
codegenParameter.baseType = codegenModel.classname;
57965796
codegenParameter.dataType = getTypeDeclaration(codegenModel.classname);
57975797
codegenParameter.description = codegenModel.description;
5798+
codegenParameter.isNullable = codegenModel.isNullable;
57985799
imports.add(codegenParameter.baseType);
57995800
} else {
58005801
CodegenProperty codegenProperty = fromProperty("property", schema);
@@ -5808,6 +5809,7 @@ private void addBodyModelSchema(CodegenParameter codegenParameter, String name,
58085809
codegenParameter.baseType = codegenParameter.baseName;
58095810
codegenParameter.dataType = getTypeDeclaration(codegenModelName);
58105811
codegenParameter.description = codegenProperty.getDescription();
5812+
codegenParameter.isNullable = codegenProperty.isNullable;
58115813
} else {
58125814
if (ModelUtils.isMapSchema(schema)) {// http body is map
58135815
LOGGER.error("Map should be supported. Please report to openapi-generator github repo about the issue.");
@@ -5916,6 +5918,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
59165918
codegenParameter.baseType = getSchemaType(inner);
59175919
codegenParameter.isContainer = Boolean.TRUE;
59185920
codegenParameter.isMapContainer = Boolean.TRUE;
5921+
codegenParameter.isNullable = codegenProperty.isNullable;
59195922

59205923
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
59215924

@@ -5958,6 +5961,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
59585961
codegenParameter.baseType = getSchemaType(inner);
59595962
codegenParameter.isContainer = Boolean.TRUE;
59605963
codegenParameter.isListContainer = Boolean.TRUE;
5964+
codegenParameter.isNullable = codegenProperty.isNullable;
59615965

59625966
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
59635967
// set nullable
@@ -5981,6 +5985,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
59815985
codegenParameter.baseType = codegenProperty.baseType;
59825986
codegenParameter.dataType = codegenProperty.dataType;
59835987
codegenParameter.description = codegenProperty.description;
5988+
codegenParameter.isNullable = codegenProperty.isNullable;
59845989
codegenParameter.paramName = toParamName(codegenParameter.baseName);
59855990
}
59865991
setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty);
@@ -6010,6 +6015,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
60106015
codegenParameter.minLength = codegenProperty.minLength;
60116016
codegenParameter.maxLength = codegenProperty.maxLength;
60126017
codegenParameter.pattern = codegenProperty.pattern;
6018+
codegenParameter.isNullable = codegenProperty.isNullable;
60136019

60146020
if (codegenProperty.complexType != null) {
60156021
imports.add(codegenProperty.complexType);

modules/openapi-generator/src/main/resources/Java/libraries/jersey2/ApiClient.mustache

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ public class ApiClient {
839839
* @return Entity
840840
* @throws ApiException API exception
841841
*/
842-
public Entity<?> serialize(Object obj, Map<String, Object> formParams, String contentType) throws ApiException {
842+
public Entity<?> serialize(Object obj, Map<String, Object> formParams, String contentType, boolean isBodyNullable) throws ApiException {
843843
Entity<?> entity;
844844
if (contentType.startsWith("multipart/form-data")) {
845845
MultiPart multiPart = new MultiPart();
@@ -863,7 +863,11 @@ public class ApiClient {
863863
entity = Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE);
864864
} else {
865865
// We let jersey handle the serialization
866-
entity = Entity.entity(obj == null ? Entity.text("") : obj, contentType);
866+
if (isBodyNullable) { // payload is nullable
867+
entity = Entity.entity(obj == null ? Entity.text("null") : obj, contentType);
868+
} else {
869+
entity = Entity.entity(obj == null ? Entity.text("") : obj, contentType);
870+
}
867871
}
868872
return entity;
869873
}
@@ -874,10 +878,11 @@ public class ApiClient {
874878
* @param obj Object
875879
* @param formParams Form parameters
876880
* @param contentType Context type
881+
* @param isBodyNulalble True if the body is nullable
877882
* @return String
878883
* @throws ApiException API exception
879884
*/
880-
public String serializeToString(Object obj, Map<String, Object> formParams, String contentType) throws ApiException {
885+
public String serializeToString(Object obj, Map<String, Object> formParams, String contentType, boolean isBodyNullable) throws ApiException {
881886
try {
882887
if (contentType.startsWith("multipart/form-data")) {
883888
throw new ApiException("multipart/form-data not yet supported for serializeToString (http signature authentication)");
@@ -893,7 +898,11 @@ public class ApiClient {
893898
return formString.substring(0, formString.length() - 1);
894899
}
895900
} else {
896-
return json.getMapper().writeValueAsString(obj);
901+
if (isBodyNullable) {
902+
return obj == null ? "null" : json.getMapper().writeValueAsString(obj);
903+
} else {
904+
return json.getMapper().writeValueAsString(obj);
905+
}
897906
}
898907
} catch (Exception ex) {
899908
throw new ApiException("Failed to perform serializeToString: " + ex.toString());
@@ -1007,6 +1016,7 @@ public class ApiClient {
10071016
* @param contentType The request's Content-Type header
10081017
* @param authNames The authentications to apply
10091018
* @param returnType The return type into which to deserialize the response
1019+
* @param isBodyNullable True if the body is nullable
10101020
* @return The response body in type of string
10111021
* @throws ApiException API exception
10121022
*/
@@ -1022,7 +1032,8 @@ public class ApiClient {
10221032
String accept,
10231033
String contentType,
10241034
String[] authNames,
1025-
GenericType<T> returnType)
1035+
GenericType<T> returnType,
1036+
boolean isBodyNullable)
10261037
throws ApiException {
10271038
10281039
// Not using `.target(targetURL).path(path)` below,
@@ -1069,7 +1080,7 @@ public class ApiClient {
10691080
}
10701081
}
10711082

1072-
Entity<?> entity = serialize(body, formParams, contentType);
1083+
Entity<?> entity = serialize(body, formParams, contentType, isBodyNullable);
10731084

10741085
// put all headers in one place
10751086
Map<String, String> allHeaderParams = new HashMap<>(defaultHeaderMap);
@@ -1081,7 +1092,7 @@ public class ApiClient {
10811092
queryParams,
10821093
allHeaderParams,
10831094
cookieParams,
1084-
serializeToString(body, formParams, contentType),
1095+
serializeToString(body, formParams, contentType, isBodyNullable),
10851096
method,
10861097
target.getUri());
10871098

@@ -1170,8 +1181,8 @@ public class ApiClient {
11701181
* @deprecated Add qualified name of the operation as a first parameter.
11711182
*/
11721183
@Deprecated
1173-
public <T> ApiResponse<T> invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType) throws ApiException {
1174-
return invokeAPI(null, path, method, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType);
1184+
public <T> ApiResponse<T> invokeAPI(String path, String method, List<Pair> queryParams, Object body, Map<String, String> headerParams, Map<String, String> cookieParams, Map<String, Object> formParams, String accept, String contentType, String[] authNames, GenericType<T> returnType, boolean isBodyNullable) throws ApiException {
1185+
return invokeAPI(null, path, method, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames, returnType, isBodyNullable);
11751186
}
11761187

11771188
/**

modules/openapi-generator/src/main/resources/Java/libraries/jersey2/api.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public class {{classname}} {
167167
{{/returnType}}
168168
return apiClient.invokeAPI("{{classname}}.{{operationId}}", localVarPath, "{{httpMethod}}", localVarQueryParams, localVarPostBody,
169169
localVarHeaderParams, localVarCookieParams, localVarFormParams, localVarAccept, localVarContentType,
170-
localVarAuthNames, {{#returnType}}localVarReturnType{{/returnType}}{{^returnType}}null{{/returnType}});
170+
localVarAuthNames, {{#returnType}}localVarReturnType{{/returnType}}{{^returnType}}null{{/returnType}}, {{#bodyParam}}{{#isNullable}}true{{/isNullable}}{{^isNullable}}false{{/isNullable}}{{/bodyParam}}{{^bodyParam}}false{{/bodyParam}});
171171
}
172172
{{#vendorExtensions.x-group-parameters}}
173173

0 commit comments

Comments
 (0)