Skip to content

Commit

Permalink
feat: default flat param object
Browse files Browse the repository at this point in the history
  • Loading branch information
NaccOll committed Aug 20, 2022
1 parent 14bb1bf commit 6720e1e
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
Expand Down Expand Up @@ -322,7 +323,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
Entry<String, Parameter> 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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer) {
List<MethodParameter> 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
Expand Down Expand Up @@ -274,4 +240,4 @@ public static MethodParameter changeContainingClass(MethodParameter methodParame
return result;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -122,6 +125,13 @@ public class GenericParameterService {
*/
private final ObjectMapperProvider objectMapperProvider;

/**
* The constant PARAM_TYPES_TO_IGNORE.
*/
private final List<Class<?>> PARAM_TYPES_TO_IGNORE = Collections.synchronizedList(new ArrayList<>());

private final boolean defaultFlatParamObject;

/**
* Instantiates a new Generic parameter builder.
* @param propertyResolverUtils the property resolver utils
Expand All @@ -130,13 +140,18 @@ public class GenericParameterService {
* @param objectMapperProvider the object mapper provider
*/
public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
Optional<WebConversionServiceProvider> 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<Class<?>> classes) {
PARAM_TYPES_TO_IGNORE.addAll(classes);
}

/**
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -566,6 +581,7 @@ public io.swagger.v3.oas.annotations.Parameter generateParameterBySchema(io.swag
public Class<? extends Annotation> annotationType() {
return io.swagger.v3.oas.annotations.Parameter.class;
}

@Override
public String name() {
return schema.name();
Expand Down Expand Up @@ -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<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer) {
List<MethodParameter> 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]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}

Expand All @@ -173,7 +175,7 @@ private static Stream<MethodParameter> 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()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ public class SpringDocConfigProperties {
*/
private boolean showSpringCloudFunctions;

/**
* The param default flatten
*/
private boolean defaultFlatParamObject;

/**
* The model Converters
*/
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ PolymorphicModelConverter polymorphicModelConverter(ObjectMapperProvider objectM
@Lazy(false)
OpenAPIService openAPIBuilder(Optional<OpenAPI> openAPI,
SecurityService securityParser,
SpringDocConfigProperties springDocConfigProperties,PropertyResolverUtils propertyResolverUtils,
SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils,
Optional<List<OpenApiBuilderCustomizer>> openApiBuilderCustomisers,
Optional<List<ServerBaseUrlCustomizer>> serverBaseUrlCustomisers, Optional<JavadocProvider> javadocProvider) {
return new OpenAPIService(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider);
Expand Down Expand Up @@ -333,16 +333,18 @@ 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
@ConditionalOnMissingBean
@Lazy(false)
GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils,
Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider,
SpringDocConfigProperties springDocConfigProperties) {
return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizer,
optionalWebConversionServiceProvider, objectMapperProvider);
optionalWebConversionServiceProvider, objectMapperProvider, springDocConfigProperties.isDefaultFlatParamObject());
}

/**
Expand Down Expand Up @@ -409,7 +411,7 @@ static BeanFactoryPostProcessor springdocBeanFactoryPostProcessor2() {
@Lazy(false)
SpringDocProviders springDocProviders(Optional<ActuatorProvider> actuatorProvider, Optional<CloudFunctionProvider> springCloudFunctionProvider, Optional<SecurityOAuth2Provider> springSecurityOAuth2Provider,
Optional<RepositoryRestResourceProvider> repositoryRestResourceProvider, Optional<RouterFunctionProvider> routerFunctionProvider,
Optional<SpringWebProvider> springWebProvider, Optional<WebConversionServiceProvider> webConversionServiceProvider,
Optional<SpringWebProvider> springWebProvider, Optional<WebConversionServiceProvider> webConversionServiceProvider,
ObjectMapperProvider objectMapperProvider) {
return new SpringDocProviders(actuatorProvider, springCloudFunctionProvider, springSecurityOAuth2Provider, repositoryRestResourceProvider, routerFunctionProvider, springWebProvider, webConversionServiceProvider, objectMapperProvider);
}
Expand Down Expand Up @@ -609,7 +611,7 @@ CloudFunctionProvider springCloudFunctionProvider(Optional<FunctionCatalog> func
@Bean
@ConditionalOnMissingBean
@Lazy(false)
ObjectMapperProvider springDocObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties){
ObjectMapperProvider springDocObjectMapperProvider(SpringDocConfigProperties springDocConfigProperties) {
return new ObjectMapperProvider(springDocConfigProperties);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,11 @@ public class Dog {
)
String displayName;

}
public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,12 @@
public class SpecificClientModel extends BaseClientModel {
@JsonProperty("name")
String name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

0 comments on commit 6720e1e

Please sign in to comment.