Skip to content

Commit c445cc9

Browse files
authored
Merge pull request springdoc#1778 from rudolfgrauberger/null-pointer-exception-for-nested-query-param-with-depth-greater-1
Fixes NPE in springdoc-openapi-javadoc for nested parameters with a depth > 1
2 parents 7ec945f + eed76db commit c445cc9

File tree

10 files changed

+337
-1
lines changed

10 files changed

+337
-1
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@ private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter
729729
if (delegatingMethodParameter.isParameterObject()) {
730730
String fieldName;
731731
if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
732-
fieldName = StringUtils.substringAfter(pName, DOT);
732+
fieldName = StringUtils.substringAfterLast(pName, DOT);
733733
else
734734
fieldName = pName;
735735
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class ConcreteSubclassFromGeneric extends SimpleGeneric<MyData> {
4+
5+
/**
6+
* Return the top name
7+
*/
8+
private String topName;
9+
10+
public String getTopName() {
11+
return topName;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2022 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*/
22+
23+
package test.org.springdoc.api.app166;
24+
25+
26+
import org.springdoc.api.annotations.ParameterObject;
27+
import org.springframework.http.HttpStatus;
28+
import org.springframework.http.ResponseEntity;
29+
import org.springframework.web.bind.annotation.GetMapping;
30+
import org.springframework.web.bind.annotation.RestController;
31+
32+
@RestController
33+
public class HelloController {
34+
@GetMapping("/nested")
35+
public ResponseEntity<String> nested(@ParameterObject final SimpleOuterClass filter) {
36+
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
37+
}
38+
39+
@GetMapping( "/nestedTypeErasureGeneric")
40+
public ResponseEntity<String> nestedTypeErasureGeneric( @ParameterObject final SimpleGeneric<MyData> filter) {
41+
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
42+
}
43+
44+
@GetMapping( "/nestedReifiableGeneric")
45+
public ResponseEntity<String> nestedReifiableGeneric( @ParameterObject final ConcreteSubclassFromGeneric filter) {
46+
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class MyData {
4+
5+
/**
6+
* Returns the first name
7+
*/
8+
private String firstName;
9+
10+
/**
11+
* Returns the max number
12+
*/
13+
private Integer maxNumber;
14+
15+
public Integer getMaxNumber() {
16+
return maxNumber;
17+
}
18+
19+
public String getFirstName() {
20+
return firstName;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class SimpleGeneric<T> {
4+
5+
/**
6+
* Returns name
7+
*/
8+
private String name;
9+
10+
/**
11+
* Returns the generic child
12+
*/
13+
private T child;
14+
15+
public T getChild() {
16+
return child;
17+
}
18+
19+
public String getName() {
20+
return name;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class SimpleInnerClass {
4+
5+
/**
6+
* Returns the inner inner class
7+
*/
8+
private SimpleInnerInnerClass innerInnerClass;
9+
10+
/**
11+
* Returns the boolean name
12+
*/
13+
private Boolean name;
14+
15+
/**
16+
* Returns the max number
17+
*/
18+
private Integer maxNumber;
19+
20+
public Integer getMaxNumber() {
21+
return maxNumber;
22+
}
23+
24+
public Boolean getName() {
25+
return name;
26+
}
27+
28+
public SimpleInnerInnerClass getInnerInnerClass() {
29+
return innerInnerClass;
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class SimpleInnerInnerClass {
4+
/**
5+
* Returns the name of the inner inner class
6+
*/
7+
Boolean name;
8+
9+
/**
10+
* Returns the maxNumber of the inner inner class
11+
*/
12+
private Integer maxNumber;
13+
14+
public Integer getMaxNumber() {
15+
return maxNumber;
16+
}
17+
18+
public Boolean getName() {
19+
return name;
20+
}
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package test.org.springdoc.api.app166;
2+
3+
public class SimpleOuterClass {
4+
5+
/**
6+
* Returns the name of the outer class
7+
*/
8+
private String name;
9+
10+
/**
11+
* Returns the inner class
12+
*/
13+
private SimpleInnerClass innerClass;
14+
15+
public String getName() {
16+
return name;
17+
}
18+
19+
public SimpleInnerClass getInnerClass() {
20+
return innerClass;
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
*
3+
* * Copyright 2019-2020 the original author or authors.
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * https://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
16+
*
17+
*/
18+
19+
package test.org.springdoc.api.app166;
20+
21+
import org.springframework.boot.autoconfigure.SpringBootApplication;
22+
import test.org.springdoc.api.AbstractSpringDocTest;
23+
24+
/**
25+
* The type Spring doc app 165 test.
26+
*/
27+
public class SpringDocApp166Test extends AbstractSpringDocTest {
28+
29+
/**
30+
* The type Spring doc test app.
31+
*/
32+
@SpringBootApplication
33+
static class SpringDocTestApp {
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": { "title": "OpenAPI definition", "version": "v0" },
4+
"servers": [
5+
{ "url": "http://localhost", "description": "Generated server url" }
6+
],
7+
"paths": {
8+
"/nested": {
9+
"get": {
10+
"tags": ["hello-controller"],
11+
"operationId": "nested",
12+
"parameters": [
13+
{
14+
"name": "name",
15+
"in": "query",
16+
"description": "Returns the name of the outer class",
17+
"required": false,
18+
"schema": { "type": "string" }
19+
},
20+
{
21+
"name": "innerClass.innerInnerClass.name",
22+
"in": "query",
23+
"description": "Returns the name of the inner inner class",
24+
"required": false,
25+
"schema": { "type": "boolean" }
26+
},
27+
{
28+
"name": "innerClass.innerInnerClass.maxNumber",
29+
"in": "query",
30+
"description": "Returns the maxNumber of the inner inner class",
31+
"required": false,
32+
"schema": { "type": "integer", "format": "int32" }
33+
},
34+
{
35+
"name": "innerClass.name",
36+
"in": "query",
37+
"description": "Returns the boolean name",
38+
"required": false,
39+
"schema": { "type": "boolean" }
40+
},
41+
{
42+
"name": "innerClass.maxNumber",
43+
"in": "query",
44+
"description": "Returns the max number",
45+
"required": false,
46+
"schema": { "type": "integer", "format": "int32" }
47+
}
48+
],
49+
"responses": {
50+
"200": {
51+
"description": "OK",
52+
"content": { "*/*": { "schema": { "type": "string" } } }
53+
}
54+
}
55+
}
56+
},
57+
"/nestedTypeErasureGeneric": {
58+
"get": {
59+
"tags": ["hello-controller"],
60+
"operationId": "nestedTypeErasureGeneric",
61+
"parameters": [
62+
{
63+
"name": "name",
64+
"in": "query",
65+
"description": "Returns name",
66+
"required": false,
67+
"schema": { "type": "string" }
68+
}
69+
],
70+
"responses": {
71+
"200": {
72+
"description": "OK",
73+
"content": { "*/*": { "schema": { "type": "string" } } }
74+
}
75+
}
76+
}
77+
},
78+
"/nestedReifiableGeneric": {
79+
"get": {
80+
"tags": ["hello-controller"],
81+
"operationId": "nestedReifiableGeneric",
82+
"parameters": [
83+
{
84+
"name": "topName",
85+
"in": "query",
86+
"description": "Return the top name",
87+
"required": false,
88+
"schema": { "type": "string" }
89+
},
90+
{
91+
"name": "name",
92+
"in": "query",
93+
"description": "Returns name",
94+
"required": false,
95+
"schema": { "type": "string" }
96+
},
97+
{
98+
"name": "child.firstName",
99+
"in": "query",
100+
"description": "Returns the first name",
101+
"required": false,
102+
"schema": { "type": "string" }
103+
},
104+
{
105+
"name": "child.maxNumber",
106+
"in": "query",
107+
"description": "Returns the max number",
108+
"required": false,
109+
"schema": { "type": "integer", "format": "int32" }
110+
}
111+
],
112+
"responses": {
113+
"200": {
114+
"description": "OK",
115+
"content": { "*/*": { "schema": { "type": "string" } } }
116+
}
117+
}
118+
}
119+
}
120+
},
121+
"components": {}
122+
}

0 commit comments

Comments
 (0)