Skip to content

Commit 1a5fbe0

Browse files
fix(java): serialize JSON header parameter content
1 parent 271e4dd commit 1a5fbe0

24 files changed

Lines changed: 797 additions & 11 deletions

File tree

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

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public class CodegenParameter implements IJsonSchemaValidationProperties {
5050
* If a query parameter should be serialized as json
5151
*/
5252
public boolean queryIsJsonMimeType;
53+
/**
54+
* True when a header parameter uses a JSON media type in Parameter.content.
55+
*/
56+
public boolean headerIsJsonMimeType;
5357
/**
5458
* datatype is the generic inner parameter of a std::optional for C++, or Optional (Java)
5559
*/
@@ -270,6 +274,7 @@ public CodegenParameter copy() {
270274
output.isArray = this.isArray;
271275
output.isMap = this.isMap;
272276
output.queryIsJsonMimeType = this.queryIsJsonMimeType;
277+
output.headerIsJsonMimeType = this.headerIsJsonMimeType;
273278
output.isOptional = this.isOptional;
274279
output.isExplode = this.isExplode;
275280
output.style = this.style;
@@ -294,15 +299,15 @@ public int hashCode() {
294299
isFormStyle, isSpaceDelimited, isPipeDelimited,
295300
jsonSchema, isString, isNumeric, isInteger, isLong, isNumber, isFloat, isDouble, isDecimal,
296301
isByteArray, isBinary, isBoolean, isDate, isDateTime, isUuid, isUri, isEmail, isPassword,
297-
isFreeFormObject, isAnyType, isArray, isMap, queryIsJsonMimeType, isOptional, isFile, isEnum, isEnumRef, _enum, allowableValues,
298-
items, mostInnerItems, additionalProperties, vars, requiredVars, vendorExtensions, hasValidation,
299-
getMaxProperties(), getMinProperties(), isNullable, isDeprecated, required, getMaximum(),
300-
getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(), getMaxLength(), getMinLength(),
301-
getPattern(), getMaxItems(), getMinItems(), getUniqueItems(), contentType, multipleOf, isNull, isVoid,
302-
additionalPropertiesIsAnyType, hasVars, hasRequired, isShort, isUnboundedInteger,
303-
hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, schema, content,
304-
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties,
305-
nameInPascalCase, nameInCamelCase, nameInLowerCase, nameInSnakeCase);
302+
isFreeFormObject, isAnyType, isArray, isMap, queryIsJsonMimeType, headerIsJsonMimeType, isOptional,
303+
isFile, isEnum, isEnumRef, _enum, allowableValues, items, mostInnerItems, additionalProperties, vars,
304+
requiredVars, vendorExtensions, hasValidation, getMaxProperties(), getMinProperties(), isNullable,
305+
isDeprecated, required, getMaximum(), getExclusiveMaximum(), getMinimum(), getExclusiveMinimum(),
306+
getMaxLength(), getMinLength(), getPattern(), getMaxItems(), getMinItems(), getUniqueItems(),
307+
contentType, multipleOf, isNull, isVoid, additionalPropertiesIsAnyType, hasVars, hasRequired, isShort,
308+
isUnboundedInteger, hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, schema,
309+
content, requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties, nameInPascalCase,
310+
nameInCamelCase, nameInLowerCase, nameInSnakeCase);
306311
}
307312

308313
@Override
@@ -345,6 +350,7 @@ public boolean equals(Object o) {
345350
isArray == that.isArray &&
346351
isMap == that.isMap &&
347352
queryIsJsonMimeType == that.queryIsJsonMimeType &&
353+
headerIsJsonMimeType == that.headerIsJsonMimeType &&
348354
isOptional == that.isOptional &&
349355
isFile == that.isFile &&
350356
isEnum == that.isEnum &&
@@ -486,6 +492,7 @@ public String toString() {
486492
sb.append(", isArray=").append(isArray);
487493
sb.append(", isMap=").append(isMap);
488494
sb.append(", queryIsJsonMimeType=").append(queryIsJsonMimeType);
495+
sb.append(", headerIsJsonMimeType=").append(headerIsJsonMimeType);
489496
sb.append(", isOptional=").append(isOptional);
490497
sb.append(", isFile=").append(isFile);
491498
sb.append(", isEnum=").append(isEnum);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5722,6 +5722,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set<String> imports)
57225722
codegenParameter.isPathParam = true;
57235723
} else if (parameter instanceof HeaderParameter || "header".equalsIgnoreCase(parameter.getIn())) {
57245724
codegenParameter.isHeaderParam = true;
5725+
codegenParameter.headerIsJsonMimeType = isJsonMimeType(codegenParameter.contentType);
57255726
} else if (parameter instanceof CookieParameter || "cookie".equalsIgnoreCase(parameter.getIn())) {
57265727
codegenParameter.isCookieParam = true;
57275728
} else {

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/ApiClient.mustache‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,6 +912,43 @@ public class ApiClient {
912912
}
913913
}
914914
915+
/**
916+
* Format the given parameter object as an ASCII-safe JSON string.
917+
*
918+
* @param param Parameter
919+
* @return JSON representation of the parameter
920+
*/
921+
public String parameterToJsonString(Object param) {
922+
String json = JSON.serialize(param);
923+
int firstUnsafe = -1;
924+
for (int i = 0; i < json.length(); i++) {
925+
if (json.charAt(i) >= 0x7f) {
926+
firstUnsafe = i;
927+
break;
928+
}
929+
}
930+
if (firstUnsafe == -1) {
931+
return json;
932+
}
933+
934+
StringBuilder escaped = new StringBuilder(json.length());
935+
escaped.append(json, 0, firstUnsafe);
936+
for (int i = firstUnsafe; i < json.length(); i++) {
937+
char c = json.charAt(i);
938+
if (c >= 0x7f) {
939+
escaped.append("\\u");
940+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
941+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
942+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
943+
escaped.append(Character.forDigit(c & 0xf, 16));
944+
} else {
945+
escaped.append(c);
946+
}
947+
}
948+
949+
return escaped.toString();
950+
}
951+
915952
/**
916953
* Formats the specified query parameter to a list containing a single {@code Pair} object.
917954
*
@@ -1954,7 +1991,11 @@ public class ApiClient {
19541991
}
19551992
break;
19561993
case "header":
1957-
headerParams.put(param.getName(), parameterToString(value));
1994+
if (param.getContent() != null && param.getContent().containsKey("application/json")) {
1995+
headerParams.put(param.getName(), parameterToJsonString(value));
1996+
} else {
1997+
headerParams.put(param.getName(), parameterToString(value));
1998+
}
19581999
break;
19592000
case "cookie":
19602001
cookieParams.put(param.getName(), parameterToString(value));

‎modules/openapi-generator/src/main/resources/Java/libraries/okhttp-gson/api.mustache‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,12 @@ public class {{classname}} {
229229
{{#headerParams}}
230230

231231
if ({{paramName}} != null) {
232+
{{#headerIsJsonMimeType}}
233+
localVarHeaderParams.put("{{baseName}}", localVarApiClient.parameterToJsonString({{paramName}}));
234+
{{/headerIsJsonMimeType}}
235+
{{^headerIsJsonMimeType}}
232236
localVarHeaderParams.put("{{baseName}}", localVarApiClient.parameterToString({{paramName}}));
237+
{{/headerIsJsonMimeType}}
233238
}
234239

235240
{{/headerParams}}

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5362,6 +5362,29 @@ public void testQueryIsJsonMimeType() {
53625362
assertTrue(codegenOperation.queryParams.stream().allMatch(p -> p.queryIsJsonMimeType));
53635363
}
53645364

5365+
@Test
5366+
public void testHeaderIsJsonMimeType() {
5367+
DefaultCodegen codegen = new DefaultCodegen();
5368+
final OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_1/java/json-header-content.yaml");
5369+
codegen.setOpenAPI(openAPI);
5370+
String path = "/test";
5371+
CodegenOperation codegenOperation = codegen.fromOperation(path, "POST", openAPI.getPaths().get(path).getPost(), null);
5372+
5373+
assertThat(codegenOperation.headerParams).hasSize(2);
5374+
5375+
CodegenParameter jsonHeader = codegenOperation.headerParams.stream()
5376+
.filter(param -> "X-Json-Arg".equals(param.baseName))
5377+
.findFirst()
5378+
.orElseThrow();
5379+
assertThat(jsonHeader.headerIsJsonMimeType).isTrue();
5380+
5381+
CodegenParameter plainHeader = codegenOperation.headerParams.stream()
5382+
.filter(param -> "X-Plain-Arg".equals(param.baseName))
5383+
.findFirst()
5384+
.orElseThrow();
5385+
assertThat(plainHeader.headerIsJsonMimeType).isFalse();
5386+
}
5387+
53655388
@Test
53665389
public void testDefaultOauthIsNotNull() {
53675390
final DefaultCodegen codegen = new DefaultCodegen();

‎modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,59 @@ public void testJersey3NullableNullDefaultRemainsUndefined() {
588588
.doesNotContain("JsonNullable.<List<String>>of(null)");
589589
}
590590

591+
@Test
592+
public void testJsonContentHeaderUsesJsonSerialization() {
593+
final Path output = newTempFolder();
594+
final CodegenConfigurator configurator = new CodegenConfigurator()
595+
.setGeneratorName(JAVA_GENERATOR)
596+
.setLibrary(JavaClientCodegen.OKHTTP_GSON)
597+
.setInputSpec("src/test/resources/3_1/java/json-header-content.yaml")
598+
.setOutputDir(output.toString().replace("\\", "/"));
599+
600+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
601+
602+
validateJavaSourceFiles(files);
603+
604+
assertThat(output.resolve("src/main/java/org/openapitools/client/ApiClient.java"))
605+
.content()
606+
.contains("public String parameterToJsonString(Object param)");
607+
608+
assertThat(output.resolve("src/main/java/org/openapitools/client/api/DefaultApi.java"))
609+
.content()
610+
.contains(
611+
"localVarHeaderParams.put(\"X-Json-Arg\", localVarApiClient.parameterToJsonString(xJsonArg));"
612+
)
613+
.doesNotContain(
614+
"localVarHeaderParams.put(\"X-Json-Arg\", localVarApiClient.parameterToString(xJsonArg));"
615+
);
616+
}
617+
618+
@Test
619+
public void testDynamicJsonContentHeaderUsesJsonSerialization() {
620+
final Path output = newTempFolder();
621+
final CodegenConfigurator configurator = new CodegenConfigurator()
622+
.setGeneratorName(JAVA_GENERATOR)
623+
.setLibrary(JavaClientCodegen.OKHTTP_GSON)
624+
.setInputSpec("src/test/resources/3_1/java/json-header-content.yaml")
625+
.addAdditionalProperty("dynamicOperations", true)
626+
.setOutputDir(output.toString().replace("\\", "/"));
627+
628+
List<File> files = new DefaultGenerator().opts(configurator.toClientOptInput()).generate();
629+
630+
validateJavaSourceFiles(files);
631+
632+
assertThat(output.resolve("src/main/java/org/openapitools/client/ApiClient.java"))
633+
.content()
634+
.contains("if (param.getContent() != null && param.getContent().containsKey(\"application/json\")) {")
635+
.contains("headerParams.put(param.getName(), parameterToJsonString(value));")
636+
.contains("headerParams.put(param.getName(), parameterToString(value));");
637+
638+
assertThat(output.resolve("src/main/java/org/openapitools/client/api/DefaultApi.java"))
639+
.content()
640+
.contains("paramMap.put(\"X-Json-Arg\", xJsonArg);")
641+
.contains("paramMap.put(\"X-Plain-Arg\", xPlainArg);");
642+
}
643+
591644
@Test
592645
public void testGeneratePingSomeObj() {
593646
final Path output = newTempFolder();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
openapi: 3.1.0
2+
3+
info:
4+
title: JSON header repro
5+
version: 1.0.0
6+
7+
paths:
8+
/test:
9+
post:
10+
operationId: testJsonHeader
11+
parameters:
12+
- name: X-Json-Arg
13+
in: header
14+
required: true
15+
content:
16+
application/json:
17+
schema:
18+
$ref: '#/components/schemas/HeaderArg'
19+
- name: X-Plain-Arg
20+
in: header
21+
schema:
22+
type: string
23+
responses:
24+
'204':
25+
description: OK
26+
27+
components:
28+
schemas:
29+
HeaderArg:
30+
type: object
31+
required:
32+
- path
33+
properties:
34+
path:
35+
type: string
36+

‎samples/client/echo_api/java/okhttp-gson-user-defined-templates/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,43 @@ public String parameterToString(Object param) {
793793
}
794794
}
795795

796+
/**
797+
* Format the given parameter object as an ASCII-safe JSON string.
798+
*
799+
* @param param Parameter
800+
* @return JSON representation of the parameter
801+
*/
802+
public String parameterToJsonString(Object param) {
803+
String json = JSON.serialize(param);
804+
int firstUnsafe = -1;
805+
for (int i = 0; i < json.length(); i++) {
806+
if (json.charAt(i) >= 0x7f) {
807+
firstUnsafe = i;
808+
break;
809+
}
810+
}
811+
if (firstUnsafe == -1) {
812+
return json;
813+
}
814+
815+
StringBuilder escaped = new StringBuilder(json.length());
816+
escaped.append(json, 0, firstUnsafe);
817+
for (int i = firstUnsafe; i < json.length(); i++) {
818+
char c = json.charAt(i);
819+
if (c >= 0x7f) {
820+
escaped.append("\\u");
821+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
822+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
823+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
824+
escaped.append(Character.forDigit(c & 0xf, 16));
825+
} else {
826+
escaped.append(c);
827+
}
828+
}
829+
830+
return escaped.toString();
831+
}
832+
796833
/**
797834
* Formats the specified query parameter to a list containing a single {@code Pair} object.
798835
*

‎samples/client/echo_api/java/okhttp-gson/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,43 @@ public String parameterToString(Object param) {
723723
}
724724
}
725725

726+
/**
727+
* Format the given parameter object as an ASCII-safe JSON string.
728+
*
729+
* @param param Parameter
730+
* @return JSON representation of the parameter
731+
*/
732+
public String parameterToJsonString(Object param) {
733+
String json = JSON.serialize(param);
734+
int firstUnsafe = -1;
735+
for (int i = 0; i < json.length(); i++) {
736+
if (json.charAt(i) >= 0x7f) {
737+
firstUnsafe = i;
738+
break;
739+
}
740+
}
741+
if (firstUnsafe == -1) {
742+
return json;
743+
}
744+
745+
StringBuilder escaped = new StringBuilder(json.length());
746+
escaped.append(json, 0, firstUnsafe);
747+
for (int i = firstUnsafe; i < json.length(); i++) {
748+
char c = json.charAt(i);
749+
if (c >= 0x7f) {
750+
escaped.append("\\u");
751+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
752+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
753+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
754+
escaped.append(Character.forDigit(c & 0xf, 16));
755+
} else {
756+
escaped.append(c);
757+
}
758+
}
759+
760+
return escaped.toString();
761+
}
762+
726763
/**
727764
* Formats the specified query parameter to a list containing a single {@code Pair} object.
728765
*

‎samples/client/others/java/okhttp-gson-oneOf-array/src/main/java/org/openapitools/client/ApiClient.java‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,43 @@ public String parameterToString(Object param) {
697697
}
698698
}
699699

700+
/**
701+
* Format the given parameter object as an ASCII-safe JSON string.
702+
*
703+
* @param param Parameter
704+
* @return JSON representation of the parameter
705+
*/
706+
public String parameterToJsonString(Object param) {
707+
String json = JSON.serialize(param);
708+
int firstUnsafe = -1;
709+
for (int i = 0; i < json.length(); i++) {
710+
if (json.charAt(i) >= 0x7f) {
711+
firstUnsafe = i;
712+
break;
713+
}
714+
}
715+
if (firstUnsafe == -1) {
716+
return json;
717+
}
718+
719+
StringBuilder escaped = new StringBuilder(json.length());
720+
escaped.append(json, 0, firstUnsafe);
721+
for (int i = firstUnsafe; i < json.length(); i++) {
722+
char c = json.charAt(i);
723+
if (c >= 0x7f) {
724+
escaped.append("\\u");
725+
escaped.append(Character.forDigit((c >> 12) & 0xf, 16));
726+
escaped.append(Character.forDigit((c >> 8) & 0xf, 16));
727+
escaped.append(Character.forDigit((c >> 4) & 0xf, 16));
728+
escaped.append(Character.forDigit(c & 0xf, 16));
729+
} else {
730+
escaped.append(c);
731+
}
732+
}
733+
734+
return escaped.toString();
735+
}
736+
700737
/**
701738
* Formats the specified query parameter to a list containing a single {@code Pair} object.
702739
*

0 commit comments

Comments
 (0)