From ffc7ad221dbf75195fc897ca6d52b4e4a47fc667 Mon Sep 17 00:00:00 2001 From: eshizhan Date: Fri, 22 Sep 2023 02:11:25 +0800 Subject: [PATCH 1/3] clear logic for flat parameter object --- .../extractor/DelegatingMethodParameter.java | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java index 42f7f6e03..d3320398e 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java @@ -3,7 +3,7 @@ * * * * * * * * * - * * * * * Copyright 2019-2022 the original author or authors. + * * * * * Copyright 2019-2023 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. @@ -124,31 +124,16 @@ public static MethodParameter[] customize(String[] pNames, MethodParameter[] par MethodParameter p = parameters[i]; Class paramClass = AdditionalModelsConverter.getParameterObjectReplacement(p.getParameterType()); - if (!MethodParameterPojoExtractor.isSimpleType(paramClass) && (p.hasParameterAnnotation(ParameterObject.class) || AnnotatedElementUtils.isAnnotated(paramClass, ParameterObject.class))) { + boolean hasFlatAnnotation = p.hasParameterAnnotation(ParameterObject.class) || AnnotatedElementUtils.isAnnotated(paramClass, ParameterObject.class); + boolean hasNotFlatAnnotation = Arrays.stream(p.getParameterAnnotations()) + .anyMatch(annotation -> Arrays.asList(RequestBody.class, RequestPart.class).contains(annotation.annotationType())); + if (!MethodParameterPojoExtractor.isSimpleType(paramClass) + && (hasFlatAnnotation || (defaultFlatParamObject && !hasNotFlatAnnotation && !AbstractRequestService.isRequestTypeToIgnore(paramClass)))) { MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> { optionalDelegatingMethodParameterCustomizer.ifPresent(customizer -> customizer.customize(p, methodParameter)); explodedParameters.add(methodParameter); }); } - else if (defaultFlatParamObject) { - boolean isSimpleType = MethodParameterPojoExtractor.isSimpleType(paramClass); - List annotations = Arrays.stream(p.getParameterAnnotations()) - .filter(annotation -> Arrays.asList(RequestBody.class, RequestPart.class).contains(annotation.annotationType())) - .toList(); - boolean hasAnnotation = !annotations.isEmpty(); - boolean shouldFlat = !isSimpleType && !hasAnnotation; - if (shouldFlat && !AbstractRequestService.isRequestTypeToIgnore(paramClass)) { - MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> { - optionalDelegatingMethodParameterCustomizer - .ifPresent(customizer -> customizer.customize(p, methodParameter)); - explodedParameters.add(methodParameter); - }); - } - else { - String name = pNames != null ? pNames[i] : p.getParameterName(); - explodedParameters.add(new DelegatingMethodParameter(p, name, null, false, false)); - } - } else { String name = pNames != null ? pNames[i] : p.getParameterName(); explodedParameters.add(new DelegatingMethodParameter(p, name, null, false, false)); From d4a6d76d664341a8c4512b29f2b86eaca2578341 Mon Sep 17 00:00:00 2001 From: eshizhan Date: Fri, 22 Sep 2023 02:28:05 +0800 Subject: [PATCH 2/3] support get javadoc description from getter method --- .../JavadocPropertyCustomizer.java | 30 ++++++-- .../springdoc/api/app170/HelloController.java | 40 ++++++++++ .../org/springdoc/api/app170/PersonDTO.java | 73 +++++++++++++++++++ .../api/app170/PersonProjection.java | 42 +++++++++++ .../api/app170/SpringDocApp170Test.java | 34 +++++++++ .../src/test/resources/results/app170.json | 65 +++++++++++++++++ 6 files changed, 276 insertions(+), 8 deletions(-) create mode 100644 springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/HelloController.java create mode 100644 springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonDTO.java create mode 100644 springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonProjection.java create mode 100644 springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/SpringDocApp170Test.java create mode 100644 springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app170.json diff --git a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java index 62ee626a6..430486dda 100644 --- a/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java +++ b/springdoc-openapi-starter-common/src/main/java/org/springdoc/core/customizers/JavadocPropertyCustomizer.java @@ -24,11 +24,11 @@ package org.springdoc.core.customizers; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.lang.reflect.Field; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import com.fasterxml.jackson.databind.JavaType; import io.swagger.v3.core.converter.AnnotatedType; @@ -76,15 +76,19 @@ public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterato Class cls = javaType.getRawClass(); Schema resolvedSchema = chain.next().resolve(type, context, chain); List fields = FieldUtils.getAllFieldsList(cls); - if (!CollectionUtils.isEmpty(fields)) { + List clsProperties = new ArrayList<>(); + try { + clsProperties = Arrays.asList(Introspector.getBeanInfo(cls).getPropertyDescriptors()); + } catch (IntrospectionException ignored) {} + if (!CollectionUtils.isEmpty(fields) || !CollectionUtils.isEmpty(clsProperties)) { if (!type.isSchemaProperty()) { Schema existingSchema = context.resolve(type); - setJavadocDescription(cls, fields, existingSchema); + setJavadocDescription(cls, fields, clsProperties, existingSchema); } else if (resolvedSchema != null && resolvedSchema.get$ref() != null && resolvedSchema.get$ref().contains(AnnotationsUtils.COMPONENTS_REF)) { String schemaName = resolvedSchema.get$ref().substring(21); Schema existingSchema = context.getDefinedModels().get(schemaName); - setJavadocDescription(cls, fields, existingSchema); + setJavadocDescription(cls, fields, clsProperties, existingSchema); } } return resolvedSchema; @@ -96,10 +100,12 @@ else if (resolvedSchema != null && resolvedSchema.get$ref() != null && resolvedS /** * Sets javadoc description. * + * @param cls the cls * @param fields the fields + * @param clsProperties the bean properties of cls * @param existingSchema the existing schema */ - private void setJavadocDescription(Class cls, List fields, Schema existingSchema) { + private void setJavadocDescription(Class cls, List fields, List clsProperties, Schema existingSchema) { if (existingSchema != null) { if (StringUtils.isBlank(existingSchema.getDescription())) { existingSchema.setDescription(javadocProvider.getClassJavadoc(cls)); @@ -124,6 +130,14 @@ private void setJavadocDescription(Class cls, List fields, Schema exis if (StringUtils.isNotBlank(fieldJavadoc)) stringSchemaEntry.getValue().setDescription(fieldJavadoc); }); + if (StringUtils.isBlank(stringSchemaEntry.getValue().getDescription())) { + Optional optionalPd = clsProperties.stream().filter(pd -> pd.getName().equals(stringSchemaEntry.getKey())).findAny(); + optionalPd.ifPresent(pd1 -> { + String fieldJavadoc = javadocProvider.getMethodJavadocDescription(pd1.getReadMethod()); + if (StringUtils.isNotBlank(fieldJavadoc)) + stringSchemaEntry.getValue().setDescription(fieldJavadoc); + }); + } }); } } diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/HelloController.java b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/HelloController.java new file mode 100644 index 000000000..3716e2c2c --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/HelloController.java @@ -0,0 +1,40 @@ +/* + * + * * Copyright 2019-2023 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.app170; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * The type Hello controller. + */ +@RestController +public class HelloController { + + /** + * PersonProjection interface. + * + * @return the PersonProjection + */ + @GetMapping(value = "/persons") + public PersonProjection persons() { + return new PersonDTO(); + } + +} diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonDTO.java b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonDTO.java new file mode 100644 index 000000000..95e867185 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonDTO.java @@ -0,0 +1,73 @@ +/* + * + * * Copyright 2019-2023 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.app170; + +/** + * Simulate a dynamically generated class that implements the PersonProjection interface. + */ +public class PersonDTO implements PersonProjection { + private String email; + + private String firstName; + + private String lastName; + + /** + * Instantiates a new Person dto. + */ + public PersonDTO() { + } + + /** + * Instantiates a new Person dto. + * + * @param email the email + * @param firstName the first name + * @param lastName the last name + */ + public PersonDTO(final String email, final String firstName, final String lastName) { + this.email = email; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(final String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } +} diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonProjection.java b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonProjection.java new file mode 100644 index 000000000..7e7573622 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/PersonProjection.java @@ -0,0 +1,42 @@ +/* + * + * * Copyright 2019-2023 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.app170; + +/** + * The type PersonProjection dto interface. + */ +public interface PersonProjection { + /** + * The Email. + * + */ + String getEmail(); + + /** + * The First name. + * + */ + String getFirstName(); + + /** + * The Last name. + * + */ + String getLastName(); +} diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/SpringDocApp170Test.java b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/SpringDocApp170Test.java new file mode 100644 index 000000000..d88799ea2 --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/java/test/org/springdoc/api/app170/SpringDocApp170Test.java @@ -0,0 +1,34 @@ +/* + * + * * Copyright 2019-2023 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.app170; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import test.org.springdoc.api.AbstractSpringDocTest; + +/** + * The type Spring doc app 170 test. + */ +public class SpringDocApp170Test extends AbstractSpringDocTest { + + /** + * The type Spring doc test app. + */ + @SpringBootApplication + static class SpringDocTestApp {} +} \ No newline at end of file diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app170.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app170.json new file mode 100644 index 000000000..a64303bed --- /dev/null +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app170.json @@ -0,0 +1,65 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "tags": [ + { + "name": "hello-controller", + "description": "The type Hello controller." + } + ], + "paths": { + "/persons": { + "get": { + "tags": [ + "hello-controller" + ], + "summary": "PersonProjection interface.", + "description": "PersonProjection interface.", + "operationId": "persons", + "responses": { + "200": { + "description": "the PersonProjection", + "content": { + "*/*": { + "schema": { + "$ref": "#/components/schemas/PersonProjection" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PersonProjection" : { + "type" : "object", + "properties" : { + "email" : { + "type" : "string", + "description" : "The Email." + }, + "firstName" : { + "type" : "string", + "description" : "The First name." + }, + "lastName" : { + "type" : "string", + "description" : "The Last name." + } + }, + "description" : "The type PersonProjection dto interface." + } + } + } +} \ No newline at end of file From 7dfc40501914ecd828511e87c4bd3e52f9c1d750 Mon Sep 17 00:00:00 2001 From: eshizhan Date: Fri, 22 Sep 2023 14:11:28 +0800 Subject: [PATCH 3/3] add getter method javadoc description for other test cases --- .../src/test/resources/results/app104.json | 3 ++- .../src/test/resources/results/app114.json | 3 ++- .../src/test/resources/results/app126.json | 14 ++++++++++---- .../src/test/resources/results/app87.json | 3 ++- 4 files changed, 16 insertions(+), 7 deletions(-) diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app104.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app104.json index 686cad12f..010ae485e 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app104.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app104.json @@ -72,7 +72,8 @@ "components": { "schemas": { "Design": { - "type": "object" + "type": "object", + "description": "The type Design." } } } diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app114.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app114.json index 51a4d927f..412a6593f 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app114.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app114.json @@ -73,7 +73,8 @@ "type": "string", "example": "USD" } - } + }, + "description": "" } } } diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app126.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app126.json index 583d687ca..60da48d1f 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app126.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app126.json @@ -56,27 +56,33 @@ "properties": { "instance": { "type": "string", - "format": "uri" + "format": "uri", + "description": "An absolute URI that identifies the specific occurrence of the problem.\n It may or may not yield further information if dereferenced." }, "type": { "type": "string", + "description": "An absolute URI that identifies the problem type. When dereferenced,\n it SHOULD provide human-readable documentation for the problem type\n (e.g., using HTML). When this member is not present, its value is\n assumed to be \"about:blank\".", "format": "uri" }, "parameters": { "type": "object", "additionalProperties": { "type": "object" - } + }, + "description": "Optional, additional attributes of the problem. Implementations can choose to ignore this in favor of concrete,\n typed fields." }, "title": { - "type": "string" + "type": "string", + "description": "A short, human-readable summary of the problem type. It SHOULD NOT\n change from occurrence to occurrence of the problem, except for\n purposes of localisation." }, "status": { "type": "integer", + "description": "The HTTP status code generated by the origin server for this\n occurrence of the problem.", "format": "int32" }, "detail": { - "type": "string" + "type": "string", + "description": "A human readable explanation specific to this occurrence of the problem." } }, "description": "The interface Problem." diff --git a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app87.json b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app87.json index 3b9078ccb..685d59b0f 100644 --- a/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app87.json +++ b/springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app87.json @@ -74,7 +74,8 @@ "components": { "schemas": { "Item": { - "type": "object" + "type": "object", + "description": "The type Item." } } }