Skip to content

Commit

Permalink
Javadoc description of the @RequestPart param of multipart/form-data …
Browse files Browse the repository at this point in the history
…goes to requestBody description, not to parameter description. Fixes #1923
  • Loading branch information
bnasslahsen committed Nov 16, 2022
1 parent 93c31c8 commit 1e64c20
Show file tree
Hide file tree
Showing 22 changed files with 153 additions and 111 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
package org.springdoc.core;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
Expand Down Expand Up @@ -63,7 +62,6 @@
import io.swagger.v3.oas.models.parameters.RequestBody;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springdoc.core.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
import org.springdoc.core.customizers.ParameterCustomizer;
import org.springdoc.core.providers.JavadocProvider;
Expand All @@ -87,10 +85,8 @@
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.util.UriComponentsBuilder;

import static org.springdoc.core.Constants.DOT;
import static org.springdoc.core.Constants.OPENAPI_ARRAY_TYPE;
import static org.springdoc.core.Constants.OPENAPI_STRING_TYPE;
import static org.springdoc.core.Constants.QUERY_PARAM;
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;

/**
Expand Down Expand Up @@ -254,7 +250,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
Components components = openAPI.getComponents();

JavadocProvider javadocProvider = operationService.getJavadocProvider();
JavadocProvider javadocProvider = parameterBuilder.getJavadocProvider();

for (MethodParameter methodParameter : parameters) {
// check if query param
Expand Down Expand Up @@ -290,7 +286,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
if (isValidParameter(parameter)) {
// Add param javadoc
if (StringUtils.isBlank(parameter.getDescription()) && javadocProvider != null) {
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter, pName);
String paramJavadocDescription = parameterBuilder.getParamJavadoc(javadocProvider, methodParameter);
if (!StringUtils.isBlank(paramJavadocDescription)) {
parameter.setDescription(paramJavadocDescription);
}
Expand All @@ -300,15 +296,9 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
if (operation.getRequestBody() != null)
requestBodyInfo.setRequestBody(operation.getRequestBody());
requestBodyService.calculateRequestBodyInfo(components, methodAttributes, parameterInfo, requestBodyInfo);
// Add requestBody javadoc
if (StringUtils.isBlank(requestBodyInfo.getRequestBody().getDescription()) && javadocProvider != null) {
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter, pName);
if (!StringUtils.isBlank(paramJavadocDescription)) {
requestBodyInfo.getRequestBody().setDescription(paramJavadocDescription);
}
}
applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional());
} customiseParameter(parameter, parameterInfo, operationParameters);
}
customiseParameter(parameter, parameterInfo, operationParameters);
}
}

Expand Down Expand Up @@ -487,7 +477,8 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
if (parameterInfo.getParamType() != null) {
if (!ValueConstants.DEFAULT_NONE.equals(parameterInfo.getDefaultValue()))
parameterInfo.setRequired(false);
else parameterInfo.setDefaultValue(null);
else
parameterInfo.setDefaultValue(null);
return this.buildParam(parameterInfo, components, jsonView);
}
// By default
Expand All @@ -496,7 +487,8 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
//parameterInfo.setParamType(QUERY_PARAM);
parameterInfo.setDefaultValue(null);
return this.buildParam(parameterInfo, components, jsonView);
} return null;
}
return null;
}

/**
Expand Down Expand Up @@ -537,7 +529,8 @@ public Parameter buildParam(ParameterInfo parameterInfo, Components components,
primitiveSchema.setDefault(parameterInfo.getDefaultValue());
defaultValue = primitiveSchema.getDefault();
} schema.setDefault(defaultValue);
} parameter.setSchema(schema);
}
parameter.setSchema(schema);
} return parameter;
}

Expand Down Expand Up @@ -680,29 +673,6 @@ private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo pa
return (isBodyAllowed && (parameterInfo.getParameterModel() == null || parameterInfo.getParameterModel().getIn() == null) && !delegatingMethodParameter.isParameterObject()) && ((methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null || methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null || methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestPart.class) != null || AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null) || (!ClassUtils.isPrimitiveOrWrapper(methodParameter.getParameterType()) && (!ArrayUtils.isEmpty(methodParameter.getParameterAnnotations()))));
}

/**
* Gets param javadoc.
*
* @param javadocProvider the javadoc provider
* @param methodParameter the method parameter
* @param pName the p name
* @return the param javadoc
*/
private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter, String pName) {
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
final String paramJavadocDescription;
if (delegatingMethodParameter.isParameterObject()) {
String fieldName; if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
fieldName = StringUtils.substringAfterLast(pName, DOT);
else fieldName = pName;
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
paramJavadocDescription = javadocProvider.getFieldJavadoc(field);
}
else
paramJavadocDescription = javadocProvider.getParamJavadoc(methodParameter.getMethod(), pName);
return paramJavadocDescription;
}

/**
* Is default flat param object boolean.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
Expand All @@ -34,6 +35,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import com.fasterxml.jackson.annotation.JsonView;
Expand All @@ -53,9 +55,11 @@
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
import org.springdoc.core.providers.JavadocProvider;
import org.springdoc.core.providers.ObjectMapperProvider;
import org.springdoc.core.providers.WebConversionServiceProvider;

Expand All @@ -64,11 +68,14 @@
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;
import org.springframework.web.multipart.MultipartRequest;

import static org.springdoc.core.Constants.DOT;

/**
* The type Generic parameter builder.
* @author bnasslahsen, coutin
Expand Down Expand Up @@ -123,20 +130,28 @@ public class GenericParameterService {
private final ObjectMapperProvider objectMapperProvider;

/**
* Instantiates a new Generic parameter builder.
* The javadoc provider.
*/
private final Optional<JavadocProvider> javadocProviderOptional;

/**
* Instantiates a new Generic parameter service.
*
* @param propertyResolverUtils the property resolver utils
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
* @param optionalWebConversionServiceProvider the optional web conversion service provider
* @param objectMapperProvider the object mapper provider
* @param javadocProviderOptional the javadoc provider
*/
public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional<JavadocProvider> javadocProviderOptional) {
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.javadocProviderOptional = javadocProviderOptional;
}

/**
Expand Down Expand Up @@ -338,9 +353,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 All @@ -350,6 +365,16 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque

if (requestBodyInfo != null) {
schemaN = calculateRequestBodySchema(components, parameterInfo, requestBodyInfo, schemaN, paramName);
JavadocProvider javadocProvider = javadocProviderOptional.orElse(null);
if (schemaN != null && javadocProvider != null && !isRequestBodyPresent(parameterInfo)) {
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter);
if (schemaN.getProperties() != null && schemaN.getProperties().containsKey(parameterInfo.getpName())) {
Map<String, Schema> properties = schemaN.getProperties();
if (!StringUtils.isBlank(paramJavadocDescription) && StringUtils.isBlank(properties.get(parameterInfo.getpName()).getDescription())) {
properties.get(parameterInfo.getpName()).setDescription(paramJavadocDescription);
}
}
}
}

return schemaN;
Expand Down Expand Up @@ -571,6 +596,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 @@ -657,4 +683,48 @@ public String ref() {
}
};
}

/**
* Gets javadoc provider.
*
* @return the javadoc provider
*/
public JavadocProvider getJavadocProvider() {
return javadocProviderOptional.orElse(null);
}

/**
* Is request body present boolean.
*
* @param parameterInfo the parameter info
* @return the boolean
*/
public boolean isRequestBodyPresent(ParameterInfo parameterInfo) {
return parameterInfo.getMethodParameter().getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null
|| parameterInfo.getMethodParameter().getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null
|| AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(parameterInfo.getMethodParameter().getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null;
}

/**
* Gets param javadoc.
*
* @param javadocProvider the javadoc provider
* @param methodParameter the method parameter
* @return the param javadoc
*/
String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter) {
String pName = methodParameter.getParameterName();
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
final String paramJavadocDescription;
if (delegatingMethodParameter.isParameterObject()) {
String fieldName; if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
fieldName = StringUtils.substringAfterLast(pName, DOT);
else fieldName = pName;
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
paramJavadocDescription = javadocProvider.getFieldJavadoc(field);
}
else
paramJavadocDescription = javadocProvider.getParamJavadoc(methodParameter.getMethod(), pName);
return paramJavadocDescription;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.LocalTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,20 @@ public class OperationService {
*/
private final PropertyResolverUtils propertyResolverUtils;

/**
* The javadoc provider.
*/
private final Optional<JavadocProvider> javadocProvider;

/**
* Instantiates a new Operation builder.
* @param parameterBuilder the parameter builder
* @param requestBodyService the request body builder
* @param securityParser the security parser
* @param propertyResolverUtils the property resolver utils
* @param javadocProvider the javadoc provider
*/
public OperationService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils, Optional<JavadocProvider> javadocProvider) {
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils) {
super();
this.parameterBuilder = parameterBuilder;
this.requestBodyService = requestBodyService;
this.securityParser = securityParser;
this.propertyResolverUtils = propertyResolverUtils;
this.javadocProvider = javadocProvider;
}

/**
Expand Down Expand Up @@ -630,6 +623,6 @@ public Operation mergeOperation(Operation operation, Operation operationModel) {
* @return the javadoc provider
*/
public JavadocProvider getJavadocProvider() {
return javadocProvider.orElse(null);
return parameterBuilder.getJavadocProvider();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,15 @@ else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
methodAttributes.getJsonViewAnnotationForRequestBody());
mergeContent(requestBody, methodAttributes, schema);
}

// Add requestBody javadoc
if (StringUtils.isBlank(requestBody.getDescription()) && parameterBuilder.getJavadocProvider() != null
&& parameterBuilder.isRequestBodyPresent(parameterInfo)) {
String paramJavadocDescription = parameterBuilder.getParamJavadoc(parameterBuilder.getJavadocProvider(), parameterInfo.getMethodParameter());
if (!StringUtils.isBlank(paramJavadocDescription)) {
requestBodyInfo.getRequestBody().setDescription(paramJavadocDescription);
}
}
return requestBody;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.convert.support.GenericConversionService;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
Expand Down Expand Up @@ -267,16 +266,15 @@ ModelConverterRegistrar modelConverterRegistrar(Optional<List<ModelConverter>> m
* @param requestBodyService the request body service
* @param securityParser the security parser
* @param propertyResolverUtils the property resolver utils
* @param javadocProvider the javadoc provider
* @return the operation service
*/
@Bean
@ConditionalOnMissingBean
@Lazy(false)
OperationService operationBuilder(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils, Optional<JavadocProvider> javadocProvider) {
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils) {
return new OperationService(parameterBuilder, requestBodyService,
securityParser, propertyResolverUtils, javadocProvider);
securityParser, propertyResolverUtils);
}

/**
Expand Down Expand Up @@ -337,16 +335,17 @@ 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 javadocProvider the javadoc provider
* @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,Optional<JavadocProvider> javadocProvider) {
return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizer,
optionalWebConversionServiceProvider, objectMapperProvider);
optionalWebConversionServiceProvider, objectMapperProvider,javadocProvider);
}

/**
Expand Down

0 comments on commit 1e64c20

Please sign in to comment.