Skip to content

Commit 1e64c20

Browse files
committed
Javadoc description of the @RequestPart param of multipart/form-data goes to requestBody description, not to parameter description. Fixes #1923
1 parent 93c31c8 commit 1e64c20

File tree

22 files changed

+153
-111
lines changed

22 files changed

+153
-111
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestService.java

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
package org.springdoc.core;
2424

2525
import java.lang.annotation.Annotation;
26-
import java.lang.reflect.Field;
2726
import java.lang.reflect.Method;
2827
import java.math.BigDecimal;
2928
import java.util.ArrayList;
@@ -63,7 +62,6 @@
6362
import io.swagger.v3.oas.models.parameters.RequestBody;
6463
import org.apache.commons.lang3.ArrayUtils;
6564
import org.apache.commons.lang3.StringUtils;
66-
import org.apache.commons.lang3.reflect.FieldUtils;
6765
import org.springdoc.core.SpringDocConfigProperties.ApiDocs.OpenApiVersion;
6866
import org.springdoc.core.customizers.ParameterCustomizer;
6967
import org.springdoc.core.providers.JavadocProvider;
@@ -87,10 +85,8 @@
8785
import org.springframework.web.method.HandlerMethod;
8886
import org.springframework.web.util.UriComponentsBuilder;
8987

90-
import static org.springdoc.core.Constants.DOT;
9188
import static org.springdoc.core.Constants.OPENAPI_ARRAY_TYPE;
9289
import static org.springdoc.core.Constants.OPENAPI_STRING_TYPE;
93-
import static org.springdoc.core.Constants.QUERY_PARAM;
9490
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
9591

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

257-
JavadocProvider javadocProvider = operationService.getJavadocProvider();
253+
JavadocProvider javadocProvider = parameterBuilder.getJavadocProvider();
258254

259255
for (MethodParameter methodParameter : parameters) {
260256
// check if query param
@@ -290,7 +286,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
290286
if (isValidParameter(parameter)) {
291287
// Add param javadoc
292288
if (StringUtils.isBlank(parameter.getDescription()) && javadocProvider != null) {
293-
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter, pName);
289+
String paramJavadocDescription = parameterBuilder.getParamJavadoc(javadocProvider, methodParameter);
294290
if (!StringUtils.isBlank(paramJavadocDescription)) {
295291
parameter.setDescription(paramJavadocDescription);
296292
}
@@ -300,15 +296,9 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
300296
if (operation.getRequestBody() != null)
301297
requestBodyInfo.setRequestBody(operation.getRequestBody());
302298
requestBodyService.calculateRequestBodyInfo(components, methodAttributes, parameterInfo, requestBodyInfo);
303-
// Add requestBody javadoc
304-
if (StringUtils.isBlank(requestBodyInfo.getRequestBody().getDescription()) && javadocProvider != null) {
305-
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter, pName);
306-
if (!StringUtils.isBlank(paramJavadocDescription)) {
307-
requestBodyInfo.getRequestBody().setDescription(paramJavadocDescription);
308-
}
309-
}
310299
applyBeanValidatorAnnotations(requestBodyInfo.getRequestBody(), parameterAnnotations, methodParameter.isOptional());
311-
} customiseParameter(parameter, parameterInfo, operationParameters);
300+
}
301+
customiseParameter(parameter, parameterInfo, operationParameters);
312302
}
313303
}
314304

@@ -487,7 +477,8 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
487477
if (parameterInfo.getParamType() != null) {
488478
if (!ValueConstants.DEFAULT_NONE.equals(parameterInfo.getDefaultValue()))
489479
parameterInfo.setRequired(false);
490-
else parameterInfo.setDefaultValue(null);
480+
else
481+
parameterInfo.setDefaultValue(null);
491482
return this.buildParam(parameterInfo, components, jsonView);
492483
}
493484
// By default
@@ -496,7 +487,8 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
496487
//parameterInfo.setParamType(QUERY_PARAM);
497488
parameterInfo.setDefaultValue(null);
498489
return this.buildParam(parameterInfo, components, jsonView);
499-
} return null;
490+
}
491+
return null;
500492
}
501493

502494
/**
@@ -537,7 +529,8 @@ public Parameter buildParam(ParameterInfo parameterInfo, Components components,
537529
primitiveSchema.setDefault(parameterInfo.getDefaultValue());
538530
defaultValue = primitiveSchema.getDefault();
539531
} schema.setDefault(defaultValue);
540-
} parameter.setSchema(schema);
532+
}
533+
parameter.setSchema(schema);
541534
} return parameter;
542535
}
543536

@@ -680,29 +673,6 @@ private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo pa
680673
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()))));
681674
}
682675

683-
/**
684-
* Gets param javadoc.
685-
*
686-
* @param javadocProvider the javadoc provider
687-
* @param methodParameter the method parameter
688-
* @param pName the p name
689-
* @return the param javadoc
690-
*/
691-
private String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter, String pName) {
692-
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
693-
final String paramJavadocDescription;
694-
if (delegatingMethodParameter.isParameterObject()) {
695-
String fieldName; if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
696-
fieldName = StringUtils.substringAfterLast(pName, DOT);
697-
else fieldName = pName;
698-
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
699-
paramJavadocDescription = javadocProvider.getFieldJavadoc(field);
700-
}
701-
else
702-
paramJavadocDescription = javadocProvider.getParamJavadoc(methodParameter.getMethod(), pName);
703-
return paramJavadocDescription;
704-
}
705-
706676
/**
707677
* Is default flat param object boolean.
708678
*

springdoc-openapi-common/src/main/java/org/springdoc/core/GenericParameterService.java

Lines changed: 74 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import java.io.IOException;
2626
import java.lang.annotation.Annotation;
27+
import java.lang.reflect.Field;
2728
import java.lang.reflect.ParameterizedType;
2829
import java.lang.reflect.Type;
2930
import java.lang.reflect.WildcardType;
@@ -34,6 +35,7 @@
3435
import java.util.List;
3536
import java.util.Locale;
3637
import java.util.Map;
38+
import java.util.Objects;
3739
import java.util.Optional;
3840

3941
import com.fasterxml.jackson.annotation.JsonView;
@@ -53,9 +55,11 @@
5355
import io.swagger.v3.oas.models.media.Schema;
5456
import io.swagger.v3.oas.models.parameters.Parameter;
5557
import org.apache.commons.lang3.StringUtils;
58+
import org.apache.commons.lang3.reflect.FieldUtils;
5659
import org.slf4j.Logger;
5760
import org.slf4j.LoggerFactory;
5861
import org.springdoc.core.customizers.DelegatingMethodParameterCustomizer;
62+
import org.springdoc.core.providers.JavadocProvider;
5963
import org.springdoc.core.providers.ObjectMapperProvider;
6064
import org.springdoc.core.providers.WebConversionServiceProvider;
6165

@@ -64,11 +68,14 @@
6468
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
6569
import org.springframework.core.MethodParameter;
6670
import org.springframework.core.ResolvableType;
71+
import org.springframework.core.annotation.AnnotatedElementUtils;
6772
import org.springframework.core.io.Resource;
6873
import org.springframework.web.context.request.RequestScope;
6974
import org.springframework.web.multipart.MultipartFile;
7075
import org.springframework.web.multipart.MultipartRequest;
7176

77+
import static org.springdoc.core.Constants.DOT;
78+
7279
/**
7380
* The type Generic parameter builder.
7481
* @author bnasslahsen, coutin
@@ -123,20 +130,28 @@ public class GenericParameterService {
123130
private final ObjectMapperProvider objectMapperProvider;
124131

125132
/**
126-
* Instantiates a new Generic parameter builder.
133+
* The javadoc provider.
134+
*/
135+
private final Optional<JavadocProvider> javadocProviderOptional;
136+
137+
/**
138+
* Instantiates a new Generic parameter service.
139+
*
127140
* @param propertyResolverUtils the property resolver utils
128141
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
129142
* @param optionalWebConversionServiceProvider the optional web conversion service provider
130143
* @param objectMapperProvider the object mapper provider
144+
* @param javadocProviderOptional the javadoc provider
131145
*/
132146
public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
133-
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
147+
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional<JavadocProvider> javadocProviderOptional) {
134148
this.propertyResolverUtils = propertyResolverUtils;
135149
this.optionalDelegatingMethodParameterCustomizer = optionalDelegatingMethodParameterCustomizer;
136150
this.optionalWebConversionServiceProvider = optionalWebConversionServiceProvider;
137151
this.configurableBeanFactory = propertyResolverUtils.getFactory();
138152
this.expressionContext = (configurableBeanFactory != null ? new BeanExpressionContext(configurableBeanFactory, new RequestScope()) : null);
139153
this.objectMapperProvider = objectMapperProvider;
154+
this.javadocProviderOptional = javadocProviderOptional;
140155
}
141156

142157
/**
@@ -338,9 +353,9 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque
338353

339354
if (parameterInfo.getParameterModel() == null || parameterInfo.getParameterModel().getSchema() == null) {
340355
Type type = ReturnTypeParser.getType(methodParameter);
341-
if(type instanceof Class && optionalWebConversionServiceProvider.isPresent()){
356+
if (type instanceof Class && optionalWebConversionServiceProvider.isPresent()) {
342357
WebConversionServiceProvider webConversionServiceProvider = optionalWebConversionServiceProvider.get();
343-
if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class)==null)
358+
if (!MethodParameterPojoExtractor.isSwaggerPrimitiveType((Class) type) && methodParameter.getParameterType().getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class) == null)
344359
type = webConversionServiceProvider.getSpringConvertedType(methodParameter.getParameterType());
345360
}
346361
schemaN = SpringDocAnnotationsUtils.extractSchema(components, type, jsonView, methodParameter.getParameterAnnotations());
@@ -350,6 +365,16 @@ Schema calculateSchema(Components components, ParameterInfo parameterInfo, Reque
350365

351366
if (requestBodyInfo != null) {
352367
schemaN = calculateRequestBodySchema(components, parameterInfo, requestBodyInfo, schemaN, paramName);
368+
JavadocProvider javadocProvider = javadocProviderOptional.orElse(null);
369+
if (schemaN != null && javadocProvider != null && !isRequestBodyPresent(parameterInfo)) {
370+
String paramJavadocDescription = getParamJavadoc(javadocProvider, methodParameter);
371+
if (schemaN.getProperties() != null && schemaN.getProperties().containsKey(parameterInfo.getpName())) {
372+
Map<String, Schema> properties = schemaN.getProperties();
373+
if (!StringUtils.isBlank(paramJavadocDescription) && StringUtils.isBlank(properties.get(parameterInfo.getpName()).getDescription())) {
374+
properties.get(parameterInfo.getpName()).setDescription(paramJavadocDescription);
375+
}
376+
}
377+
}
353378
}
354379

355380
return schemaN;
@@ -571,6 +596,7 @@ public io.swagger.v3.oas.annotations.Parameter generateParameterBySchema(io.swag
571596
public Class<? extends Annotation> annotationType() {
572597
return io.swagger.v3.oas.annotations.Parameter.class;
573598
}
599+
574600
@Override
575601
public String name() {
576602
return schema.name();
@@ -657,4 +683,48 @@ public String ref() {
657683
}
658684
};
659685
}
686+
687+
/**
688+
* Gets javadoc provider.
689+
*
690+
* @return the javadoc provider
691+
*/
692+
public JavadocProvider getJavadocProvider() {
693+
return javadocProviderOptional.orElse(null);
694+
}
695+
696+
/**
697+
* Is request body present boolean.
698+
*
699+
* @param parameterInfo the parameter info
700+
* @return the boolean
701+
*/
702+
public boolean isRequestBodyPresent(ParameterInfo parameterInfo) {
703+
return parameterInfo.getMethodParameter().getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null
704+
|| parameterInfo.getMethodParameter().getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null
705+
|| AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(parameterInfo.getMethodParameter().getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null;
706+
}
707+
708+
/**
709+
* Gets param javadoc.
710+
*
711+
* @param javadocProvider the javadoc provider
712+
* @param methodParameter the method parameter
713+
* @return the param javadoc
714+
*/
715+
String getParamJavadoc(JavadocProvider javadocProvider, MethodParameter methodParameter) {
716+
String pName = methodParameter.getParameterName();
717+
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
718+
final String paramJavadocDescription;
719+
if (delegatingMethodParameter.isParameterObject()) {
720+
String fieldName; if (StringUtils.isNotEmpty(pName) && pName.contains(DOT))
721+
fieldName = StringUtils.substringAfterLast(pName, DOT);
722+
else fieldName = pName;
723+
Field field = FieldUtils.getDeclaredField(((DelegatingMethodParameter) methodParameter).getExecutable().getDeclaringClass(), fieldName, true);
724+
paramJavadocDescription = javadocProvider.getFieldJavadoc(field);
725+
}
726+
else
727+
paramJavadocDescription = javadocProvider.getParamJavadoc(methodParameter.getMethod(), pName);
728+
return paramJavadocDescription;
729+
}
660730
}

springdoc-openapi-common/src/main/java/org/springdoc/core/MethodParameterPojoExtractor.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
import java.beans.Introspector;
2626
import java.beans.PropertyDescriptor;
2727
import java.lang.annotation.Annotation;
28-
import java.lang.reflect.*;
28+
import java.lang.reflect.Field;
29+
import java.lang.reflect.InvocationTargetException;
30+
import java.lang.reflect.Method;
31+
import java.lang.reflect.Type;
32+
import java.lang.reflect.TypeVariable;
2933
import java.nio.charset.Charset;
3034
import java.time.Duration;
3135
import java.time.LocalTime;

springdoc-openapi-common/src/main/java/org/springdoc/core/OperationService.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,27 +90,20 @@ public class OperationService {
9090
*/
9191
private final PropertyResolverUtils propertyResolverUtils;
9292

93-
/**
94-
* The javadoc provider.
95-
*/
96-
private final Optional<JavadocProvider> javadocProvider;
97-
9893
/**
9994
* Instantiates a new Operation builder.
10095
* @param parameterBuilder the parameter builder
10196
* @param requestBodyService the request body builder
10297
* @param securityParser the security parser
10398
* @param propertyResolverUtils the property resolver utils
104-
* @param javadocProvider the javadoc provider
10599
*/
106100
public OperationService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
107-
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils, Optional<JavadocProvider> javadocProvider) {
101+
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils) {
108102
super();
109103
this.parameterBuilder = parameterBuilder;
110104
this.requestBodyService = requestBodyService;
111105
this.securityParser = securityParser;
112106
this.propertyResolverUtils = propertyResolverUtils;
113-
this.javadocProvider = javadocProvider;
114107
}
115108

116109
/**
@@ -630,6 +623,6 @@ public Operation mergeOperation(Operation operation, Operation operationModel) {
630623
* @return the javadoc provider
631624
*/
632625
public JavadocProvider getJavadocProvider() {
633-
return javadocProvider.orElse(null);
626+
return parameterBuilder.getJavadocProvider();
634627
}
635628
}

springdoc-openapi-common/src/main/java/org/springdoc/core/RequestBodyService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,15 @@ else if (!methodAttributes.isWithResponseBodySchemaDoc()) {
281281
methodAttributes.getJsonViewAnnotationForRequestBody());
282282
mergeContent(requestBody, methodAttributes, schema);
283283
}
284+
285+
// Add requestBody javadoc
286+
if (StringUtils.isBlank(requestBody.getDescription()) && parameterBuilder.getJavadocProvider() != null
287+
&& parameterBuilder.isRequestBodyPresent(parameterInfo)) {
288+
String paramJavadocDescription = parameterBuilder.getParamJavadoc(parameterBuilder.getJavadocProvider(), parameterInfo.getMethodParameter());
289+
if (!StringUtils.isBlank(paramJavadocDescription)) {
290+
requestBodyInfo.getRequestBody().setDescription(paramJavadocDescription);
291+
}
292+
}
284293
return requestBody;
285294
}
286295

springdoc-openapi-common/src/main/java/org/springdoc/core/SpringDocConfiguration.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@
9393
import org.springframework.context.annotation.Configuration;
9494
import org.springframework.context.annotation.Lazy;
9595
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
96-
import org.springframework.core.convert.support.GenericConversionService;
9796
import org.springframework.data.domain.Pageable;
9897
import org.springframework.data.domain.Sort;
9998
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
@@ -267,16 +266,15 @@ ModelConverterRegistrar modelConverterRegistrar(Optional<List<ModelConverter>> m
267266
* @param requestBodyService the request body service
268267
* @param securityParser the security parser
269268
* @param propertyResolverUtils the property resolver utils
270-
* @param javadocProvider the javadoc provider
271269
* @return the operation service
272270
*/
273271
@Bean
274272
@ConditionalOnMissingBean
275273
@Lazy(false)
276274
OperationService operationBuilder(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
277-
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils, Optional<JavadocProvider> javadocProvider) {
275+
SecurityService securityParser, PropertyResolverUtils propertyResolverUtils) {
278276
return new OperationService(parameterBuilder, requestBodyService,
279-
securityParser, propertyResolverUtils, javadocProvider);
277+
securityParser, propertyResolverUtils);
280278
}
281279

282280
/**
@@ -337,16 +335,17 @@ ReturnTypeParser genericReturnTypeParser() {
337335
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
338336
* @param optionalWebConversionServiceProvider the optional web conversion service provider
339337
* @param objectMapperProvider the object mapper provider
338+
* @param javadocProvider the javadoc provider
340339
* @return the generic parameter builder
341340
*/
342341
@Bean
343342
@ConditionalOnMissingBean
344343
@Lazy(false)
345344
GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils,
346345
Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
347-
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider) {
346+
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider,Optional<JavadocProvider> javadocProvider) {
348347
return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizer,
349-
optionalWebConversionServiceProvider, objectMapperProvider);
348+
optionalWebConversionServiceProvider, objectMapperProvider,javadocProvider);
350349
}
351350

352351
/**

0 commit comments

Comments
 (0)