Skip to content

Fixes NPE in springdoc-openapi-javadoc for nested parameters with a depth > 1 #1778

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@ private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter
if (delegatingMethodParameter.isParameterObject()) {
String fieldName;
if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
fieldName = StringUtils.substringAfter(pName, DOT);
fieldName = StringUtils.substringAfterLast(pName, DOT);
else
fieldName = pName;
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package test.org.springdoc.api.app166;

public class ConcreteSubclassFromGeneric extends SimpleGeneric<MyData> {

/**
* Return the top name
*/
private String topName;

public String getTopName() {
return topName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
*
* *
* * *
* * * * Copyright 2019-2022 the original author or authors.
* * * *
* * * * Licensed under the Apache License, Version 2.0 (the "License");
* * * * you may not use this file except in compliance with the License.
* * * * You may obtain a copy of the License at
* * * *
* * * * https://www.apache.org/licenses/LICENSE-2.0
* * * *
* * * * Unless required by applicable law or agreed to in writing, software
* * * * distributed under the License is distributed on an "AS IS" BASIS,
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * * * See the License for the specific language governing permissions and
* * * * limitations under the License.
* * *
* *
*
*/

package test.org.springdoc.api.app166;


import org.springdoc.api.annotations.ParameterObject;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
@GetMapping("/nested")
public ResponseEntity<String> nested(@ParameterObject final SimpleOuterClass filter) {
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
}

@GetMapping( "/nestedTypeErasureGeneric")
public ResponseEntity<String> nestedTypeErasureGeneric( @ParameterObject final SimpleGeneric<MyData> filter) {
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
}

@GetMapping( "/nestedReifiableGeneric")
public ResponseEntity<String> nestedReifiableGeneric( @ParameterObject final ConcreteSubclassFromGeneric filter) {
return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package test.org.springdoc.api.app166;

public class MyData {

/**
* Returns the first name
*/
private String firstName;

/**
* Returns the max number
*/
private Integer maxNumber;

public Integer getMaxNumber() {
return maxNumber;
}

public String getFirstName() {
return firstName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package test.org.springdoc.api.app166;

public class SimpleGeneric<T> {

/**
* Returns name
*/
private String name;

/**
* Returns the generic child
*/
private T child;

public T getChild() {
return child;
}

public String getName() {
return name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package test.org.springdoc.api.app166;

public class SimpleInnerClass {

/**
* Returns the inner inner class
*/
private SimpleInnerInnerClass innerInnerClass;

/**
* Returns the boolean name
*/
private Boolean name;

/**
* Returns the max number
*/
private Integer maxNumber;

public Integer getMaxNumber() {
return maxNumber;
}

public Boolean getName() {
return name;
}

public SimpleInnerInnerClass getInnerInnerClass() {
return innerInnerClass;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package test.org.springdoc.api.app166;

public class SimpleInnerInnerClass {
/**
* Returns the name of the inner inner class
*/
Boolean name;

/**
* Returns the maxNumber of the inner inner class
*/
private Integer maxNumber;

public Integer getMaxNumber() {
return maxNumber;
}

public Boolean getName() {
return name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package test.org.springdoc.api.app166;

public class SimpleOuterClass {

/**
* Returns the name of the outer class
*/
private String name;

/**
* Returns the inner class
*/
private SimpleInnerClass innerClass;

public String getName() {
return name;
}

public SimpleInnerClass getInnerClass() {
return innerClass;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
*
* * Copyright 2019-2020 the original author or authors.
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * https://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/

package test.org.springdoc.api.app166;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import test.org.springdoc.api.AbstractSpringDocTest;

/**
* The type Spring doc app 165 test.
*/
public class SpringDocApp166Test extends AbstractSpringDocTest {

/**
* The type Spring doc test app.
*/
@SpringBootApplication
static class SpringDocTestApp {
}
}
122 changes: 122 additions & 0 deletions springdoc-openapi-javadoc/src/test/resources/results/app166.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
{
"openapi": "3.0.1",
"info": { "title": "OpenAPI definition", "version": "v0" },
"servers": [
{ "url": "http://localhost", "description": "Generated server url" }
],
"paths": {
"/nested": {
"get": {
"tags": ["hello-controller"],
"operationId": "nested",
"parameters": [
{
"name": "name",
"in": "query",
"description": "Returns the name of the outer class",
"required": false,
"schema": { "type": "string" }
},
{
"name": "innerClass.innerInnerClass.name",
"in": "query",
"description": "Returns the name of the inner inner class",
"required": false,
"schema": { "type": "boolean" }
},
{
"name": "innerClass.innerInnerClass.maxNumber",
"in": "query",
"description": "Returns the maxNumber of the inner inner class",
"required": false,
"schema": { "type": "integer", "format": "int32" }
},
{
"name": "innerClass.name",
"in": "query",
"description": "Returns the boolean name",
"required": false,
"schema": { "type": "boolean" }
},
{
"name": "innerClass.maxNumber",
"in": "query",
"description": "Returns the max number",
"required": false,
"schema": { "type": "integer", "format": "int32" }
}
],
"responses": {
"200": {
"description": "OK",
"content": { "*/*": { "schema": { "type": "string" } } }
}
}
}
},
"/nestedTypeErasureGeneric": {
"get": {
"tags": ["hello-controller"],
"operationId": "nestedTypeErasureGeneric",
"parameters": [
{
"name": "name",
"in": "query",
"description": "Returns name",
"required": false,
"schema": { "type": "string" }
}
],
"responses": {
"200": {
"description": "OK",
"content": { "*/*": { "schema": { "type": "string" } } }
}
}
}
},
"/nestedReifiableGeneric": {
"get": {
"tags": ["hello-controller"],
"operationId": "nestedReifiableGeneric",
"parameters": [
{
"name": "topName",
"in": "query",
"description": "Return the top name",
"required": false,
"schema": { "type": "string" }
},
{
"name": "name",
"in": "query",
"description": "Returns name",
"required": false,
"schema": { "type": "string" }
},
{
"name": "child.firstName",
"in": "query",
"description": "Returns the first name",
"required": false,
"schema": { "type": "string" }
},
{
"name": "child.maxNumber",
"in": "query",
"description": "Returns the max number",
"required": false,
"schema": { "type": "integer", "format": "int32" }
}
],
"responses": {
"200": {
"description": "OK",
"content": { "*/*": { "schema": { "type": "string" } } }
}
}
}
}
},
"components": {}
}