diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java index 9b43bbfdc..6eaa0c54b 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java @@ -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); diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/ConcreteSubclassFromGeneric.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/ConcreteSubclassFromGeneric.java new file mode 100644 index 000000000..e5900c157 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/ConcreteSubclassFromGeneric.java @@ -0,0 +1,13 @@ +package test.org.springdoc.api.app166; + +public class ConcreteSubclassFromGeneric extends SimpleGeneric { + + /** + * Return the top name + */ + private String topName; + + public String getTopName() { + return topName; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/HelloController.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/HelloController.java new file mode 100644 index 000000000..0a338d893 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/HelloController.java @@ -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 nested(@ParameterObject final SimpleOuterClass filter) { + return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK); + } + + @GetMapping( "/nestedTypeErasureGeneric") + public ResponseEntity nestedTypeErasureGeneric( @ParameterObject final SimpleGeneric filter) { + return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK); + } + + @GetMapping( "/nestedReifiableGeneric") + public ResponseEntity nestedReifiableGeneric( @ParameterObject final ConcreteSubclassFromGeneric filter) { + return new ResponseEntity<>("{\"Say\": \"Hello\"}", HttpStatus.OK); + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/MyData.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/MyData.java new file mode 100644 index 000000000..279f0899a --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/MyData.java @@ -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; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleGeneric.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleGeneric.java new file mode 100644 index 000000000..882730a83 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleGeneric.java @@ -0,0 +1,22 @@ +package test.org.springdoc.api.app166; + +public class SimpleGeneric { + + /** + * Returns name + */ + private String name; + + /** + * Returns the generic child + */ + private T child; + + public T getChild() { + return child; + } + + public String getName() { + return name; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerClass.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerClass.java new file mode 100644 index 000000000..86825b4d2 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerClass.java @@ -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; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerInnerClass.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerInnerClass.java new file mode 100644 index 000000000..0bac97dfb --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleInnerInnerClass.java @@ -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; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleOuterClass.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleOuterClass.java new file mode 100644 index 000000000..8431acc80 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SimpleOuterClass.java @@ -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; + } +} diff --git a/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SpringDocApp166Test.java b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SpringDocApp166Test.java new file mode 100644 index 000000000..36af2dec5 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/java/test/org/springdoc/api/app166/SpringDocApp166Test.java @@ -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 { + } +} diff --git a/springdoc-openapi-javadoc/src/test/resources/results/app166.json b/springdoc-openapi-javadoc/src/test/resources/results/app166.json new file mode 100644 index 000000000..2fd58ad62 --- /dev/null +++ b/springdoc-openapi-javadoc/src/test/resources/results/app166.json @@ -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": {} +}