From 6720e1e78c7223c0ae76ab8e4485bb93f318e412 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Sat, 20 Aug 2022 21:49:14 +0800 Subject: [PATCH] feat: default flat param object --- .../core/AbstractRequestService.java | 5 +- .../core/DelegatingMethodParameter.java | 36 +--------- .../core/GenericParameterService.java | 65 ++++++++++++++++++- .../core/MethodParameterPojoExtractor.java | 6 +- .../core/SpringDocConfigProperties.java | 17 +++++ .../core/SpringDocConfiguration.java | 12 ++-- .../rest/core/DataRestRequestService.java | 3 +- .../test/org/springdoc/api/v30/app71/Dog.java | 9 ++- .../api/v30/app93/BaseClientModel.java | 8 +++ .../api/v30/app93/SpecificClientModel.java | 10 ++- 10 files changed, 120 insertions(+), 51 deletions(-) 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 6eaa0c54b..9fc543aea 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 @@ -185,6 +185,7 @@ protected AbstractRequestService(GenericParameterService parameterBuilder, Reque parameterCustomizers.ifPresent(customizers -> customizers.removeIf(Objects::isNull)); this.parameterCustomizers = parameterCustomizers; this.localSpringDocParameterNameDiscoverer = localSpringDocParameterNameDiscoverer; + parameterBuilder.addIgnoreType(PARAM_TYPES_TO_IGNORE); } /** @@ -239,7 +240,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new); if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull)) pNames = reflectionParametersNames; - parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer()); + parameters = parameterBuilder.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer()); RequestBodyInfo requestBodyInfo = new RequestBodyInfo(); List operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>(); Map parametersDocMap = getApiParameters(handlerMethod.getMethod()); @@ -322,7 +323,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) { Entry entry = it.next(); Parameter parameter = entry.getValue(); if (!ParameterIn.PATH.toString().equals(parameter.getIn())) { - io.swagger.v3.oas.models.media.Schema itemSchema = new io.swagger.v3.oas.models.media.Schema() ; + io.swagger.v3.oas.models.media.Schema itemSchema = new io.swagger.v3.oas.models.media.Schema(); itemSchema.setName(entry.getKey()); itemSchema.setDescription(parameter.getDescription()); itemSchema.setDeprecated(parameter.getDeprecated()); diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java index 92c76bdd3..872a89167 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java @@ -29,23 +29,16 @@ import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; import java.util.Objects; -import java.util.Optional; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springdoc.api.annotations.ParameterObject; -import org.springdoc.core.converters.AdditionalModelsConverter; -import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; @@ -103,33 +96,6 @@ public class DelegatingMethodParameter extends MethodParameter { this.isNotRequired = isNotRequired; } - /** - * Customize method parameter [ ]. - * - * @param pNames the p names - * @param parameters the parameters - * @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer - * @return the method parameter [ ] - */ - public static MethodParameter[] customize(String[] pNames, MethodParameter[] parameters, Optional optionalDelegatingMethodParameterCustomizer) { - List explodedParameters = new ArrayList<>(); - for (int i = 0; i < parameters.length; ++i) { - MethodParameter p = parameters[i]; - Class paramClass = AdditionalModelsConverter.getParameterObjectReplacement(p.getParameterType()); - - if (!MethodParameterPojoExtractor.isSimpleType(paramClass) && (p.hasParameterAnnotation(ParameterObject.class) || AnnotatedElementUtils.isAnnotated(paramClass, ParameterObject.class))) { - 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)); - } - } - return explodedParameters.toArray(new MethodParameter[0]); - } @Override @NonNull @@ -274,4 +240,4 @@ public static MethodParameter changeContainingClass(MethodParameter methodParame return result; } -} \ No newline at end of file +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java index 5322a2a6f..691de21dd 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java @@ -55,6 +55,8 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springdoc.api.annotations.ParameterObject; +import org.springdoc.core.converters.AdditionalModelsConverter; import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer; import org.springdoc.core.providers.ObjectMapperProvider; import org.springdoc.core.providers.WebConversionServiceProvider; @@ -64,6 +66,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; +import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.io.Resource; import org.springframework.web.context.request.RequestScope; import org.springframework.web.multipart.MultipartFile; @@ -122,6 +125,13 @@ public class GenericParameterService { */ private final ObjectMapperProvider objectMapperProvider; + /** + * The constant PARAM_TYPES_TO_IGNORE. + */ + private final List> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>()); + + private final boolean defaultFlatParamObject; + /** * Instantiates a new Generic parameter builder. * @param propertyResolverUtils the property resolver utils @@ -130,13 +140,18 @@ public class GenericParameterService { * @param objectMapperProvider the object mapper provider */ public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional optionalDelegatingMethodParameterCustomizer, - Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) { + Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, boolean defaultFlatParamObject) { this.propertyResolverUtils = propertyResolverUtils; this.optionalDelegatingMethodParameterCustomizer = optionalDelegatingMethodParameterCustomizer; this.optionalWebConversionServiceProvider = optionalWebConversionServiceProvider; this.configurableBeanFactory = propertyResolverUtils.getFactory(); this.expressionContext = (configurableBeanFactory != null ? new BeanExpressionContext(configurableBeanFactory, new RequestScope()) : null); this.objectMapperProvider = objectMapperProvider; + this.defaultFlatParamObject = defaultFlatParamObject; + } + + protected void addIgnoreType(List> classes) { + PARAM_TYPES_TO_IGNORE.addAll(classes); } /** @@ -333,9 +348,9 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque if (parameterInfo.getParameterModel() == null || parameterInfo.getParameterModel().getSchema() == null) { Type type = ReturnTypeParser.getType(methodParameter); - if(type instanceof Class && optionalWebConversionServiceProvider.isPresent()){ + if (type instanceof Class && optionalWebConversionServiceProvider.isPresent()) { WebConversionServiceProvider webConversionServiceProvider = optionalWebConversionServiceProvider.get(); - if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class)==null) + if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) == null) type = webConversionServiceProvider.getSpringConvertedType(methodParameter.getParameterType()); } schemaN = SpringDocAnnotationsUtils.extractSchema(components, type, jsonView, methodParameter.getParameterAnnotations()); @@ -566,6 +581,7 @@ public io.swagger.v3.oas.annotations.Parameter generateParameterBySchema(io.swag public Class annotationType() { return io.swagger.v3.oas.annotations.Parameter.class; } + @Override public String name() { return schema.name(); @@ -652,4 +668,47 @@ public String ref() { } }; } + + /** + * Customize method parameter [ ]. + * + * @param pNames the p names + * @param parameters the parameters + * @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer + * @return the method parameter [ ] + */ + public MethodParameter[] customize(String[] pNames, MethodParameter[] parameters, Optional optionalDelegatingMethodParameterCustomizer) { + List explodedParameters = new ArrayList<>(); + for (int i = 0; i < parameters.length; ++i) { + MethodParameter p = parameters[i]; + Class paramClass = AdditionalModelsConverter.getParameterObjectReplacement(p.getParameterType()); + if (!MethodParameterPojoExtractor.isSimpleType(paramClass) && (p.hasParameterAnnotation(ParameterObject.class) || AnnotatedElementUtils.isAnnotated(paramClass, ParameterObject.class))) { + MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> { + optionalDelegatingMethodParameterCustomizer.ifPresent(customizer -> customizer.customize(p, methodParameter)); + explodedParameters.add(methodParameter); + }); + } + else if (defaultFlatParamObject) { + boolean isSimpleType = MethodParameterPojoExtractor.isSimpleType(paramClass); + boolean hasAnnotation = p.hasParameterAnnotations(); + boolean shouldFlat = !isSimpleType && !hasAnnotation; + if (shouldFlat && PARAM_TYPES_TO_IGNORE.stream().noneMatch(ignore -> ignore.isAssignableFrom(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)); + } + } + return explodedParameters.toArray(new MethodParameter[0]); + } } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodParameterPojoExtractor.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodParameterPojoExtractor.java index 5d6846458..de9c44c79 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodParameterPojoExtractor.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/MethodParameterPojoExtractor.java @@ -28,6 +28,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.nio.charset.Charset; import java.time.Duration; import java.time.LocalTime; import java.util.ArrayList; @@ -83,6 +84,7 @@ private MethodParameterPojoExtractor() { SIMPLE_TYPES.add(OptionalDouble.class); SIMPLE_TYPES.add(AtomicLong.class); SIMPLE_TYPES.add(AtomicInteger.class); + SIMPLE_TYPES.add(Charset.class); SIMPLE_TYPES.add(Map.class); SIMPLE_TYPES.add(Iterable.class); @@ -154,7 +156,7 @@ private static Class extractType(Class paramClass, Field field) { if (fieldType instanceof Class) type = (Class) fieldType; - else // This is the case for not reifiable types + else // This is the case for not reifiable types type = null; } @@ -173,7 +175,7 @@ private static Stream fromSimpleClass(Class paramClass, Fiel Annotation[] fieldAnnotations = field.getDeclaredAnnotations(); try { Parameter parameter = field.getAnnotation(Parameter.class); - boolean isNotRequired = parameter == null || !parameter.required(); + boolean isNotRequired = parameter == null || !parameter.required(); Annotation[] finalFieldAnnotations = fieldAnnotations; return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors()) .filter(d -> d.getName().equals(field.getName())) diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfigProperties.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfigProperties.java index 4e3d661a5..26441d569 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfigProperties.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfigProperties.java @@ -176,6 +176,11 @@ public class SpringDocConfigProperties { */ private boolean showSpringCloudFunctions; + /** + * The param default flatten + */ + private boolean defaultFlatParamObject; + /** * The model Converters */ @@ -222,6 +227,18 @@ public void setShowSpringCloudFunctions(boolean showSpringCloudFunctions) { this.showSpringCloudFunctions = showSpringCloudFunctions; } + /** + * Is default flat param object + * @return the boolean + */ + public boolean isDefaultFlatParamObject() { + return defaultFlatParamObject; + } + + public void setDefaultFlatParamObject(boolean defaultFlatParamObject) { + this.defaultFlatParamObject = defaultFlatParamObject; + } + /** * Gets model converters. * diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java index c0d18062f..36957dce3 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java @@ -238,7 +238,7 @@ PolymorphicModelConverter polymorphicModelConverter(ObjectMapperProvider objectM @Lazy(false) OpenAPIService openAPIBuilder(Optional openAPI, SecurityService securityParser, - SpringDocConfigProperties springDocConfigProperties,PropertyResolverUtils propertyResolverUtils, + SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, Optional> openApiBuilderCustomisers, Optional> serverBaseUrlCustomisers, Optional javadocProvider) { return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider); @@ -333,6 +333,7 @@ ReturnTypeParser genericReturnTypeParser() { * @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer * @param optionalWebConversionServiceProvider the optional web conversion service provider * @param objectMapperProvider the object mapper provider + * @param springDocConfigProperties the springdoc config properties * @return the generic parameter builder */ @Bean @@ -340,9 +341,10 @@ ReturnTypeParser genericReturnTypeParser() { @Lazy(false) GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils, Optional optionalDelegatingMethodParameterCustomizer, - Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) { + Optional optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, + SpringDocConfigProperties springDocConfigProperties) { return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizer, - optionalWebConversionServiceProvider, objectMapperProvider); + optionalWebConversionServiceProvider, objectMapperProvider, springDocConfigProperties.isDefaultFlatParamObject()); } /** @@ -409,7 +411,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor2() { @Lazy(false) SpringDocProviders springDocProviders(Optional actuatorProvider, Optional springCloudFunctionProvider, Optional springSecurityOAuth2Provider, Optional repositoryRestResourceProvider, Optional routerFunctionProvider, - Optional springWebProvider, Optional webConversionServiceProvider, + Optional springWebProvider, Optional webConversionServiceProvider, ObjectMapperProvider objectMapperProvider) { return new SpringDocProviders(actuatorProvider, springCloudFunctionProvider, springSecurityOAuth2Provider, repositoryRestResourceProvider, routerFunctionProvider, springWebProvider, webConversionServiceProvider, objectMapperProvider); } @@ -609,7 +611,7 @@ CloudFunctionProvider springCloudFunctionProvider(Optional func @Bean @ConditionalOnMissingBean @Lazy(false) - ObjectMapperProvider springDocObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties){ + ObjectMapperProvider springDocObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties) { return new ObjectMapperProvider(springDocConfigProperties); } diff --git a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRequestService.java b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRequestService.java index f07292575..bd3010bc8 100644 --- a/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRequestService.java +++ b/springdoc-openapi-data-rest/src/main/java/org/springdoc/data/rest/core/DataRestRequestService.java @@ -40,7 +40,6 @@ import io.swagger.v3.oas.models.parameters.Parameter; import org.apache.commons.lang3.ArrayUtils; import org.springdoc.core.AbstractRequestService; -import org.springdoc.core.DelegatingMethodParameter; import org.springdoc.core.GenericParameterService; import org.springdoc.core.MethodAttributes; import org.springdoc.core.ParameterInfo; @@ -149,7 +148,7 @@ public void buildParameters(OpenAPI openAPI, HandlerMethod handlerMethod, Reques */ public void buildCommonParameters(OpenAPI openAPI, RequestMethod requestMethod, MethodAttributes methodAttributes, Operation operation, String[] pNames, MethodParameter[] parameters, DataRestRepository dataRestRepository) { - parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer()); + parameters = parameterBuilder.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer()); Class domainType = dataRestRepository.getDomainType(); for (MethodParameter methodParameter : parameters) { final String pName = methodParameter.getParameterName(); diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app71/Dog.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app71/Dog.java index c217acd12..882d29984 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app71/Dog.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app71/Dog.java @@ -37,4 +37,11 @@ public class Dog { ) String displayName; -} \ No newline at end of file + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/BaseClientModel.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/BaseClientModel.java index d55bcf73f..bb17d81db 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/BaseClientModel.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/BaseClientModel.java @@ -27,4 +27,12 @@ public abstract class BaseClientModel { @JsonProperty("id") int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } } diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/SpecificClientModel.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/SpecificClientModel.java index adf3db566..42ac89005 100644 --- a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/SpecificClientModel.java +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/v30/app93/SpecificClientModel.java @@ -27,4 +27,12 @@ public class SpecificClientModel extends BaseClientModel { @JsonProperty("name") String name; -} \ No newline at end of file + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +}