Skip to content

Commit 65e9a3c

Browse files
committed
Request parameter parsing error after using @notblank in the from type interface field. Fixes #2519
1 parent 9aa78d0 commit 65e9a3c

File tree

3 files changed

+115
-71
lines changed
  • springdoc-openapi-starter-common/src/main/java/org/springdoc/core/service
  • springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1
  • springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results

3 files changed

+115
-71
lines changed

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

+88-45
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import jakarta.validation.constraints.Min;
6262
import jakarta.validation.constraints.Pattern;
6363
import jakarta.validation.constraints.Size;
64-
import org.apache.commons.lang3.ArrayUtils;
6564
import org.apache.commons.lang3.StringUtils;
6665
import org.springdoc.core.customizers.ParameterCustomizer;
6766
import org.springdoc.core.discoverer.SpringDocParameterNameDiscoverer;
@@ -77,7 +76,6 @@
7776
import org.springframework.core.MethodParameter;
7877
import org.springframework.core.annotation.AnnotatedElementUtils;
7978
import org.springframework.http.HttpMethod;
80-
import org.springframework.util.ClassUtils;
8179
import org.springframework.util.CollectionUtils;
8280
import org.springframework.validation.BindingResult;
8381
import org.springframework.validation.Errors;
@@ -93,11 +91,13 @@
9391
import org.springframework.web.util.UriComponentsBuilder;
9492

9593
import static org.springdoc.core.converters.SchemaPropertyDeprecatingConverter.containsDeprecatedAnnotation;
94+
import static org.springdoc.core.service.GenericParameterService.isFile;
9695
import static org.springdoc.core.utils.Constants.OPENAPI_ARRAY_TYPE;
9796
import static org.springdoc.core.utils.Constants.OPENAPI_STRING_TYPE;
9897

9998
/**
10099
* The type Abstract request builder.
100+
*
101101
* @author bnasslahsen
102102
*/
103103
public abstract class AbstractRequestService {
@@ -183,10 +183,10 @@ public abstract class AbstractRequestService {
183183
/**
184184
* Instantiates a new Abstract request builder.
185185
*
186-
* @param parameterBuilder the parameter builder
187-
* @param requestBodyService the request body builder
188-
* @param operationService the operation builder
189-
* @param parameterCustomizers the parameter customizers
186+
* @param parameterBuilder the parameter builder
187+
* @param requestBodyService the request body builder
188+
* @param operationService the operation builder
189+
* @param parameterCustomizers the parameter customizers
190190
* @param localSpringDocParameterNameDiscoverer the local spring doc parameter name discoverer
191191
*/
192192
protected AbstractRequestService(GenericParameterService parameterBuilder, RequestBodyService requestBodyService,
@@ -237,7 +237,7 @@ public static boolean isRequestTypeToIgnore(Class<?> rawClass) {
237237
* Gets headers.
238238
*
239239
* @param methodAttributes the method attributes
240-
* @param map the map
240+
* @param map the map
241241
* @return the headers
242242
*/
243243
@SuppressWarnings("unchecked")
@@ -265,11 +265,11 @@ public static Collection<Parameter> getHeaders(MethodAttributes methodAttributes
265265
/**
266266
* Build operation.
267267
*
268-
* @param handlerMethod the handler method
269-
* @param requestMethod the request method
270-
* @param operation the operation
268+
* @param handlerMethod the handler method
269+
* @param requestMethod the request method
270+
* @param operation the operation
271271
* @param methodAttributes the method attributes
272-
* @param openAPI the open api
272+
* @param openAPI the open api
273273
* @return the operation
274274
*/
275275
public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
@@ -378,10 +378,10 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
378378
/**
379379
* Gets parameter linked hash map.
380380
*
381-
* @param components the components
382-
* @param methodAttributes the method attributes
381+
* @param components the components
382+
* @param methodAttributes the method attributes
383383
* @param operationParameters the operation parameters
384-
* @param parametersDocMap the parameters doc map
384+
* @param parametersDocMap the parameters doc map
385385
* @return the parameter linked hash map
386386
*/
387387
private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
@@ -425,8 +425,8 @@ private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Componen
425425
/**
426426
* Customise parameter parameter.
427427
*
428-
* @param parameter the parameter
429-
* @param parameterInfo the parameter info
428+
* @param parameter the parameter
429+
* @param parameterInfo the parameter info
430430
* @param operationParameters the operation parameters
431431
*/
432432
protected void customiseParameter(Parameter parameter, ParameterInfo parameterInfo, List<Parameter> operationParameters) {
@@ -472,9 +472,9 @@ private boolean isRequiredAnnotation(MethodParameter parameter) {
472472
/**
473473
* Sets params.
474474
*
475-
* @param operation the operation
475+
* @param operation the operation
476476
* @param operationParameters the operation parameters
477-
* @param requestBodyInfo the request body info
477+
* @param requestBodyInfo the request body info
478478
*/
479479
private void setParams(Operation operation, List<Parameter> operationParameters, RequestBodyInfo requestBodyInfo) {
480480
if (!CollectionUtils.isEmpty(operationParameters))
@@ -496,10 +496,10 @@ public boolean isValidParameter(Parameter parameter) {
496496
/**
497497
* Build params parameter.
498498
*
499-
* @param parameterInfo the parameter info
500-
* @param components the components
501-
* @param requestMethod the request method
502-
* @param jsonView the json view
499+
* @param parameterInfo the parameter info
500+
* @param components the components
501+
* @param requestMethod the request method
502+
* @param jsonView the json view
503503
* @param openApiVersion the open api version
504504
* @return the parameter
505505
*/
@@ -526,8 +526,8 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
526526
* Build param parameter.
527527
*
528528
* @param parameterInfo the parameter info
529-
* @param components the components
530-
* @param jsonView the json view
529+
* @param components the components
530+
* @param jsonView the json view
531531
* @return the parameter
532532
*/
533533
public Parameter buildParam(ParameterInfo parameterInfo, Components components, JsonView jsonView) {
@@ -573,7 +573,7 @@ public Parameter buildParam(ParameterInfo parameterInfo, Components components,
573573
/**
574574
* Apply bean validator annotations.
575575
*
576-
* @param parameter the parameter
576+
* @param parameter the parameter
577577
* @param annotations the annotations
578578
*/
579579
public void applyBeanValidatorAnnotations(final Parameter parameter, final List<Annotation> annotations) {
@@ -592,7 +592,7 @@ public void applyBeanValidatorAnnotations(final Parameter parameter, final List<
592592
*
593593
* @param requestBody the request body
594594
* @param annotations the annotations
595-
* @param isOptional the is optional
595+
* @param isOptional the is optional
596596
*/
597597
public void applyBeanValidatorAnnotations(final RequestBody requestBody, final List<Annotation> annotations, boolean isOptional) {
598598
Map<String, Annotation> annos = new HashMap<>();
@@ -614,10 +614,28 @@ public void applyBeanValidatorAnnotations(final RequestBody requestBody, final L
614614
}
615615
}
616616

617+
/**
618+
* Gets request body builder.
619+
*
620+
* @return the request body builder
621+
*/
622+
public RequestBodyService getRequestBodyBuilder() {
623+
return requestBodyService;
624+
}
625+
626+
/**
627+
* Is default flat param object boolean.
628+
*
629+
* @return the boolean
630+
*/
631+
public boolean isDefaultFlatParamObject() {
632+
return defaultFlatParamObject;
633+
}
634+
617635
/**
618636
* Calculate size.
619637
*
620-
* @param annos the annos
638+
* @param annos the annos
621639
* @param schema the schema
622640
*/
623641
private void calculateSize(Map<String, Annotation> annos, Schema<?> schema) {
@@ -634,15 +652,6 @@ else if (OPENAPI_STRING_TYPE.equals(schema.getType())) {
634652
}
635653
}
636654

637-
/**
638-
* Gets request body builder.
639-
*
640-
* @return the request body builder
641-
*/
642-
public RequestBodyService getRequestBodyBuilder() {
643-
return requestBodyService;
644-
}
645-
646655
/**
647656
* Gets api parameters.
648657
*
@@ -673,7 +682,7 @@ private Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> getApiParamete
673682
/**
674683
* Apply validations to schema.
675684
*
676-
* @param annos the annos
685+
* @param annos the annos
677686
* @param schema the schema
678687
*/
679688
private void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> schema) {
@@ -713,31 +722,65 @@ private void applyValidationsToSchema(Map<String, Annotation> annos, Schema<?> s
713722
/**
714723
* Is RequestBody param boolean.
715724
*
716-
* @param requestMethod the request method
717-
* @param parameterInfo the parameter info
725+
* @param requestMethod the request method
726+
* @param parameterInfo the parameter info
718727
* @param openApiVersion the open api version
719728
* @return the boolean
720729
*/
721730
private boolean isRequestBodyParam(RequestMethod requestMethod, ParameterInfo parameterInfo, String openApiVersion) {
722731
MethodParameter methodParameter = parameterInfo.getMethodParameter();
723732
DelegatingMethodParameter delegatingMethodParameter = (DelegatingMethodParameter) methodParameter;
724-
Boolean isBodyAllowed = !RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openApiVersion);
733+
boolean isBodyAllowed = !RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.getVersion().equals(openApiVersion);
725734

726735
return (isBodyAllowed && (parameterInfo.getParameterModel() == null || parameterInfo.getParameterModel().getIn() == null) && !delegatingMethodParameter.isParameterObject())
727736
&&
728737
((methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null
729738
|| methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestBody.class) != null
730-
|| methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestPart.class) != null
731739
|| AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.parameters.RequestBody.class) != null)
732-
|| (!ClassUtils.isPrimitiveOrWrapper(methodParameter.getParameterType()) && (!ArrayUtils.isEmpty(methodParameter.getParameterAnnotations()))));
740+
|| checkOperationRequestBody(methodParameter)
741+
|| checkFile(methodParameter)
742+
743+
);
733744
}
734745

735746
/**
736-
* Is default flat param object boolean.
747+
* Check file boolean.
737748
*
749+
* @param methodParameter the method parameter
738750
* @return the boolean
739751
*/
740-
public boolean isDefaultFlatParamObject() {
741-
return defaultFlatParamObject;
752+
private boolean checkFile(MethodParameter methodParameter) {
753+
if (methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestPart.class) != null)
754+
return true;
755+
else if (methodParameter.getParameterAnnotation(org.springframework.web.bind.annotation.RequestParam.class) != null) {
756+
return isFile(methodParameter.getParameterType());
757+
}
758+
return false;
759+
}
760+
761+
/**
762+
* Check operation request body boolean.
763+
*
764+
* @param methodParameter the method parameter
765+
* @return the boolean
766+
*/
767+
private boolean checkOperationRequestBody(MethodParameter methodParameter) {
768+
if (AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.Operation.class) != null) {
769+
io.swagger.v3.oas.annotations.Operation operation = AnnotatedElementUtils.findMergedAnnotation(Objects.requireNonNull(methodParameter.getMethod()), io.swagger.v3.oas.annotations.Operation.class);
770+
io.swagger.v3.oas.annotations.parameters.RequestBody requestBody = operation.requestBody();
771+
if (StringUtils.isNotBlank(requestBody.description()))
772+
return true;
773+
else if (StringUtils.isNotBlank(requestBody.ref()))
774+
return true;
775+
else if (requestBody.required())
776+
return true;
777+
else if (requestBody.useParameterTypeSchema())
778+
return true;
779+
else if (requestBody.content().length > 0)
780+
return true;
781+
else
782+
return requestBody.extensions().length > 0;
783+
}
784+
return false;
742785
}
743786
}

springdoc-openapi-starter-webmvc-api/src/test/resources/results/3.0.1/app96.json

+17-17
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,16 @@
1717
"hello-controller"
1818
],
1919
"operationId": "test3",
20-
"requestBody": {
21-
"content": {
22-
"application/json": {
23-
"schema": {
24-
"type": "string"
25-
}
20+
"parameters": [
21+
{
22+
"name": "test",
23+
"in": "query",
24+
"required": true,
25+
"schema": {
26+
"type": "string"
2627
}
27-
},
28-
"required": true
29-
},
28+
}
29+
],
3030
"responses": {
3131
"200": {
3232
"description": "OK",
@@ -41,19 +41,17 @@
4141
}
4242
}
4343
},
44-
"/api1": {
44+
"/api2": {
4545
"post": {
4646
"tags": [
4747
"hello-controller"
4848
],
49-
"operationId": "test1",
49+
"operationId": "test2",
5050
"requestBody": {
5151
"content": {
5252
"application/json": {
5353
"schema": {
54-
"minimum": 2,
55-
"type": "integer",
56-
"format": "int32"
54+
"type": "string"
5755
}
5856
}
5957
},
@@ -73,17 +71,19 @@
7371
}
7472
}
7573
},
76-
"/api2": {
74+
"/api1": {
7775
"post": {
7876
"tags": [
7977
"hello-controller"
8078
],
81-
"operationId": "test2",
79+
"operationId": "test1",
8280
"requestBody": {
8381
"content": {
8482
"application/json": {
8583
"schema": {
86-
"type": "string"
84+
"minimum": 2,
85+
"type": "integer",
86+
"format": "int32"
8787
}
8888
}
8989
},

springdoc-openapi-tests/springdoc-openapi-javadoc-tests/src/test/resources/results/app96.json

+10-9
Original file line numberDiff line numberDiff line change
@@ -93,16 +93,17 @@
9393
"summary": "Test 3 string.",
9494
"description": "Test 3 string.",
9595
"operationId": "test3",
96-
"requestBody": {
97-
"content": {
98-
"application/json": {
99-
"schema": {
100-
"type": "string"
101-
}
96+
"parameters": [
97+
{
98+
"name": "test",
99+
"in": "query",
100+
"description": "the test",
101+
"required": true,
102+
"schema": {
103+
"type": "string"
102104
}
103-
},
104-
"required": true
105-
},
105+
}
106+
],
106107
"responses": {
107108
"200": {
108109
"description": "the string",

0 commit comments

Comments
 (0)