Skip to content

Commit 61c4047

Browse files
authored
[BUG][java][resttemplate] Fix NPE when query param with value null is exploded (OpenAPITools#17568)
* Fix NPE when query param with value null is exploded * Polish * Add test * Update tests * Add integration test
1 parent d0e533d commit 61c4047

File tree

17 files changed

+150
-56
lines changed

17 files changed

+150
-56
lines changed

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,12 @@ public class {{classname}} {
126126
final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
127127
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();{{#hasQueryParams}}
128128

129-
{{#queryParams}}{{#isExplode}}{{#hasVars}}{{#vars}} localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}.{{getter}}()));
130-
{{/vars}}{{/hasVars}}{{^hasVars}} localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}));
131-
{{/hasVars}}{{/isExplode}}{{^isExplode}} localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}));
132-
{{/isExplode}}{{/queryParams}}{{/hasQueryParams}}{{#hasHeaderParams}}
129+
{{#queryParams}}{{#isExplode}}{{#hasVars}}
130+
if ({{paramName}} != null) {
131+
{{#vars}} localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}.{{getter}}()));
132+
{{/vars}}}{{/hasVars}}{{^hasVars}}localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}));
133+
{{/hasVars}}{{/isExplode}}{{^isExplode}}localVarQueryParams.putAll(apiClient.parameterToMultiValueMap({{#collectionFormat}}ApiClient.CollectionFormat.valueOf("{{{.}}}".toUpperCase(Locale.ROOT)){{/collectionFormat}}{{^collectionFormat}}null{{/collectionFormat}}, "{{baseName}}", {{paramName}}));
134+
{{/isExplode}}{{/queryParams}}{{/hasQueryParams}}{{#hasHeaderParams}}
133135

134136
{{#headerParams}}if ({{paramName}} != null)
135137
localVarHeaderParams.add("{{baseName}}", apiClient.parameterToString({{paramName}}));{{^-last}}

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2938,4 +2938,32 @@ private void testHandleURIEnum(String library, String[] expectedInnerEnumLines,
29382938
assertNotNull(apiFile);
29392939
JavaFileAssert.assertThat(apiFile).fileContains(expectedInnerEnumLines);
29402940
}
2941+
2942+
@Test
2943+
public void testQueryParamsExploded_whenQueryParamIsNull() throws IOException {
2944+
2945+
Map<String, Object> properties = new HashMap<>();
2946+
properties.put(CodegenConstants.API_PACKAGE, "xyz.abcdef.api");
2947+
2948+
File output = Files.createTempDirectory("test").toFile();
2949+
output.deleteOnExit();
2950+
2951+
final CodegenConfigurator configurator = new CodegenConfigurator()
2952+
.setGeneratorName("java")
2953+
.setLibrary(JavaClientCodegen.RESTTEMPLATE)
2954+
.setAdditionalProperties(properties)
2955+
.setInputSpec("src/test/resources/3_0/issue_17555.yaml")
2956+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
2957+
2958+
2959+
DefaultGenerator generator = new DefaultGenerator();
2960+
List<File> files = generator.opts(configurator.toClientOptInput()).generate();
2961+
files.forEach(File::deleteOnExit);
2962+
2963+
validateJavaSourceFiles(files);
2964+
2965+
2966+
Path petApi = Paths.get(output + "/src/main/java/xyz/abcdef/api/DepartmentApi.java");
2967+
TestUtils.assertFileContains(petApi, "if (filter != null) {");
2968+
}
29412969
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
openapi: 3.0.0
2+
info:
3+
description: My description
4+
version: 1.0.0
5+
title: OpenAPI Petstore
6+
license:
7+
name: Apache-2.0
8+
url: 'https://www.apache.org/licenses/LICENSE-2.0.html'
9+
tags:
10+
- name: department
11+
description: Everything about your Department
12+
paths:
13+
/api/department/{departmentId}/teams:
14+
get:
15+
tags:
16+
- Department
17+
summary: Find Department Teams
18+
operationId: findDepartmentTeams
19+
description: Find Department Teams
20+
parameters:
21+
- $ref: '#/components/parameters/departmentIdParam'
22+
- $ref: '#/components/parameters/teamQueryParam'
23+
responses:
24+
'200':
25+
description: OK
26+
components:
27+
schemas:
28+
TeamCriteriaDTO:
29+
type: object
30+
title: TeamCriteriaDTO
31+
properties:
32+
key:
33+
type: string
34+
name:
35+
type: string
36+
parameters:
37+
departmentIdParam:
38+
name: departmentId
39+
in: path
40+
description: ID of the department to search
41+
required: true
42+
schema:
43+
type: string
44+
teamQueryParam:
45+
name: filter
46+
in: query
47+
description: Fetch teams query
48+
required: false
49+
allowEmptyValue: true
50+
schema:
51+
$ref: '#/components/schemas/TeamCriteriaDTO'

samples/client/echo_api/java/resttemplate/src/main/java/org/openapitools/client/api/QueryApi.java

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public ResponseEntity<String> testEnumRefStringWithHttpInfo(String enumNonrefStr
8484

8585
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "enum_nonref_string_query", enumNonrefStringQuery));
8686
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "enum_ref_string_query", enumRefStringQuery));
87-
87+
8888

8989
final String[] localVarAccepts = {
9090
"text/plain"
@@ -134,7 +134,7 @@ public ResponseEntity<String> testQueryDatetimeDateStringWithHttpInfo(OffsetDate
134134
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "datetime_query", datetimeQuery));
135135
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "date_query", dateQuery));
136136
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "string_query", stringQuery));
137-
137+
138138

139139
final String[] localVarAccepts = {
140140
"text/plain"
@@ -184,7 +184,7 @@ public ResponseEntity<String> testQueryIntegerBooleanStringWithHttpInfo(Integer
184184
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "integer_query", integerQuery));
185185
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "boolean_query", booleanQuery));
186186
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "string_query", stringQuery));
187-
187+
188188

189189
final String[] localVarAccepts = {
190190
"text/plain"
@@ -227,13 +227,15 @@ public ResponseEntity<String> testQueryStyleDeepObjectExplodeTrueObjectWithHttpI
227227
final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
228228
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
229229

230-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "id", queryObject.getId()));
231-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", queryObject.getName()));
232-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "category", queryObject.getCategory()));
233-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "photoUrls", queryObject.getPhotoUrls()));
234-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "tags", queryObject.getTags()));
235-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "status", queryObject.getStatus()));
236-
230+
231+
if (queryObject != null) {
232+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "id", queryObject.getId()));
233+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", queryObject.getName()));
234+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "category", queryObject.getCategory()));
235+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "photoUrls", queryObject.getPhotoUrls()));
236+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "tags", queryObject.getTags()));
237+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "status", queryObject.getStatus()));
238+
}
237239

238240
final String[] localVarAccepts = {
239241
"text/plain"
@@ -277,7 +279,7 @@ public ResponseEntity<String> testQueryStyleDeepObjectExplodeTrueObjectAllOfWith
277279
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
278280

279281
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "query_object", queryObject));
280-
282+
281283

282284
final String[] localVarAccepts = {
283285
"text/plain"
@@ -320,8 +322,10 @@ public ResponseEntity<String> testQueryStyleFormExplodeTrueArrayStringWithHttpIn
320322
final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
321323
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
322324

323-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "values", queryObject.getValues()));
324-
325+
326+
if (queryObject != null) {
327+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "values", queryObject.getValues()));
328+
}
325329

326330
final String[] localVarAccepts = {
327331
"text/plain"
@@ -364,13 +368,15 @@ public ResponseEntity<String> testQueryStyleFormExplodeTrueObjectWithHttpInfo(Pe
364368
final MultiValueMap<String, String> localVarCookieParams = new LinkedMultiValueMap<String, String>();
365369
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
366370

367-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "id", queryObject.getId()));
368-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", queryObject.getName()));
369-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "category", queryObject.getCategory()));
370-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "photoUrls", queryObject.getPhotoUrls()));
371-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "tags", queryObject.getTags()));
372-
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "status", queryObject.getStatus()));
373-
371+
372+
if (queryObject != null) {
373+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "id", queryObject.getId()));
374+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", queryObject.getName()));
375+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "category", queryObject.getCategory()));
376+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "photoUrls", queryObject.getPhotoUrls()));
377+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "tags", queryObject.getTags()));
378+
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "status", queryObject.getStatus()));
379+
}
374380

375381
final String[] localVarAccepts = {
376382
"text/plain"
@@ -414,7 +420,7 @@ public ResponseEntity<String> testQueryStyleFormExplodeTrueObjectAllOfWithHttpIn
414420
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
415421

416422
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "query_object", queryObject));
417-
423+
418424

419425
final String[] localVarAccepts = {
420426
"text/plain"

samples/client/echo_api/java/resttemplate/src/test/java/org/openapitools/client/CustomTest.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
package org.openapitools.client;
1515

1616
import org.junit.Assert;
17-
import org.openapitools.client.api.*;
18-
import org.openapitools.client.model.*;
1917
import org.junit.Test;
20-
import org.junit.Ignore;
18+
import org.openapitools.client.api.BodyApi;
19+
import org.openapitools.client.api.QueryApi;
20+
import org.openapitools.client.model.Category;
21+
import org.openapitools.client.model.Pet;
2122

22-
import java.io.IOException;
23-
import java.util.*;
23+
import java.util.Arrays;
24+
25+
import static org.junit.Assert.assertNotNull;
2426

2527

2628
/**
@@ -43,12 +45,17 @@ public void testEchoBodyPet() {
4345
photoUrls(Arrays.asList(new String[]{"http://a.com", "http://b.com"})).category(new Category().id(987L).name("new category"));
4446

4547
Pet p = bodyApi.testEchoBodyPet(pet);
46-
Assert.assertNotNull(p);
48+
assertNotNull(p);
4749
Assert.assertEquals("Hello World", p.getName());
4850
Assert.assertEquals(Long.valueOf(12345L), p.getId());
4951

5052
// response is empty body
5153
Pet p2 = bodyApi.testEchoBodyPet(null);
5254
Assert.assertNull(p2);
5355
}
56+
57+
@Test
58+
public void testQueryParamsExploded_whenQueryParamIsNull() {
59+
assertNotNull(api.testQueryStyleFormExplodeTrueObject(null));
60+
}
5461
}

samples/client/petstore/java/resttemplate-jakarta/src/main/java/org/openapitools/client/api/PetApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public ResponseEntity<List<Pet>> findPetsByStatusWithHttpInfo(List<String> statu
185185
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
186186

187187
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "status", status));
188-
188+
189189

190190
final String[] localVarAccepts = {
191191
"application/xml", "application/json"
@@ -240,7 +240,7 @@ public ResponseEntity<List<Pet>> findPetsByTagsWithHttpInfo(List<String> tags) t
240240
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
241241

242242
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "tags", tags));
243-
243+
244244

245245
final String[] localVarAccepts = {
246246
"application/xml", "application/json"

samples/client/petstore/java/resttemplate-jakarta/src/main/java/org/openapitools/client/api/UserApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ public ResponseEntity<String> loginUserWithHttpInfo(String username, String pass
327327

328328
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "username", username));
329329
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "password", password));
330-
330+
331331

332332
final String[] localVarAccepts = {
333333
"application/xml", "application/json"

samples/client/petstore/java/resttemplate-swagger1/src/main/java/org/openapitools/client/api/PetApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public ResponseEntity<List<Pet>> findPetsByStatusWithHttpInfo(List<String> statu
185185
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
186186

187187
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "status", status));
188-
188+
189189

190190
final String[] localVarAccepts = {
191191
"application/xml", "application/json"
@@ -240,7 +240,7 @@ public ResponseEntity<List<Pet>> findPetsByTagsWithHttpInfo(List<String> tags) t
240240
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
241241

242242
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "tags", tags));
243-
243+
244244

245245
final String[] localVarAccepts = {
246246
"application/xml", "application/json"

samples/client/petstore/java/resttemplate-swagger1/src/main/java/org/openapitools/client/api/UserApi.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ public ResponseEntity<String> loginUserWithHttpInfo(String username, String pass
327327

328328
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "username", username));
329329
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(null, "password", password));
330-
330+
331331

332332
final String[] localVarAccepts = {
333333
"application/xml", "application/json"

samples/client/petstore/java/resttemplate-swagger2/src/main/java/org/openapitools/client/api/PetApi.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ public ResponseEntity<List<Pet>> findPetsByStatusWithHttpInfo(List<String> statu
185185
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
186186

187187
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "status", status));
188-
188+
189189

190190
final String[] localVarAccepts = {
191191
"application/xml", "application/json"
@@ -240,7 +240,7 @@ public ResponseEntity<List<Pet>> findPetsByTagsWithHttpInfo(List<String> tags) t
240240
final MultiValueMap<String, Object> localVarFormParams = new LinkedMultiValueMap<String, Object>();
241241

242242
localVarQueryParams.putAll(apiClient.parameterToMultiValueMap(ApiClient.CollectionFormat.valueOf("csv".toUpperCase(Locale.ROOT)), "tags", tags));
243-
243+
244244

245245
final String[] localVarAccepts = {
246246
"application/xml", "application/json"

0 commit comments

Comments
 (0)