Skip to content

Commit 2b45ea4

Browse files
committed
UpperSnakeCaseStrategy is not working with spring boot and ParameterObject. Fixes #2643
1 parent 7bb5fc2 commit 2b45ea4

File tree

13 files changed

+208
-20
lines changed

13 files changed

+208
-20
lines changed

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

+16-3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.springdoc.core.customizers.OpenApiBuilderCustomizer;
6464
import org.springdoc.core.customizers.OpenApiCustomizer;
6565
import org.springdoc.core.customizers.OperationCustomizer;
66+
import org.springdoc.core.customizers.ParameterObjectNamingStrategyCustomizer;
6667
import org.springdoc.core.customizers.PropertyCustomizer;
6768
import org.springdoc.core.customizers.QuerydslPredicateOperationCustomizer;
6869
import org.springdoc.core.customizers.RouterOperationCustomizer;
@@ -375,7 +376,7 @@ ReturnTypeParser genericReturnTypeParser() {
375376
* Parameter builder generic parameter builder.
376377
*
377378
* @param propertyResolverUtils the property resolver utils
378-
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
379+
* @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
379380
* @param optionalWebConversionServiceProvider the optional web conversion service provider
380381
* @param objectMapperProvider the object mapper provider
381382
* @param javadocProvider the javadoc provider
@@ -385,9 +386,9 @@ ReturnTypeParser genericReturnTypeParser() {
385386
@ConditionalOnMissingBean
386387
@Lazy(false)
387388
GenericParameterService parameterBuilder(PropertyResolverUtils propertyResolverUtils,
388-
Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
389+
Optional<List<DelegatingMethodParameterCustomizer>> optionalDelegatingMethodParameterCustomizers,
389390
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional<JavadocProvider> javadocProvider) {
390-
return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizer,
391+
return new GenericParameterService(propertyResolverUtils, optionalDelegatingMethodParameterCustomizers,
391392
optionalWebConversionServiceProvider, objectMapperProvider, javadocProvider);
392393
}
393394

@@ -649,4 +650,16 @@ QuerydslPredicateOperationCustomizer queryDslQuerydslPredicateOperationCustomize
649650
return null;
650651
}
651652
}
653+
654+
/**
655+
* Parameter object naming strategy customizer delegating method parameter customizer.
656+
*
657+
* @return the delegating method parameter customizer
658+
*/
659+
@Bean
660+
@ConditionalOnMissingBean
661+
@Lazy(false)
662+
ParameterObjectNamingStrategyCustomizer parameterObjectNamingStrategyCustomizer() {
663+
return new ParameterObjectNamingStrategyCustomizer();
664+
}
652665
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocPageableConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ PageOpenAPIConverter pageOpenAPIConverter(Optional<SpringDataWebSettings> settin
106106
@Bean
107107
@ConditionalOnMissingBean
108108
@Lazy(false)
109-
DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
109+
DataRestDelegatingMethodParameterCustomizer dataRestDelegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
110110
return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider, optionalRepositoryRestConfiguration);
111111
}
112112
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/configuration/SpringDocSortConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ SortOpenAPIConverter sortOpenAPIConverter(ObjectMapperProvider objectMapperProvi
8686
@Bean
8787
@ConditionalOnMissingBean
8888
@Lazy(false)
89-
DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
89+
DataRestDelegatingMethodParameterCustomizer dataRestDelegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
9090
return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider, optionalRepositoryRestConfiguration);
9191
}
9292
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2024 the original author or authors.
6+
* * * *
7+
* * * * Licensed under the Apache License, Version 2.0 (the "License");
8+
* * * * you may not use this file except in compliance with the License.
9+
* * * * You may obtain a copy of the License at
10+
* * * *
11+
* * * * https://www.apache.org/licenses/LICENSE-2.0
12+
* * * *
13+
* * * * Unless required by applicable law or agreed to in writing, software
14+
* * * * distributed under the License is distributed on an "AS IS" BASIS,
15+
* * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* * * * See the License for the specific language governing permissions and
17+
* * * * limitations under the License.
18+
* * *
19+
* *
20+
*
21+
*/
22+
23+
package org.springdoc.core.customizers;
24+
25+
import java.lang.reflect.Field;
26+
27+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
28+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
29+
import org.apache.commons.lang3.reflect.FieldUtils;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
import org.springframework.core.MethodParameter;
34+
import org.springframework.core.annotation.AnnotatedElementUtils;
35+
36+
/**
37+
* The type Parameter object naming strategy customizer.
38+
*
39+
* @author bnasslahsen
40+
*/
41+
public class ParameterObjectNamingStrategyCustomizer implements DelegatingMethodParameterCustomizer {
42+
43+
/**
44+
* The constant LOGGER.
45+
*/
46+
private static final Logger LOGGER = LoggerFactory.getLogger(ParameterObjectNamingStrategyCustomizer.class);
47+
48+
@Override
49+
public void customize(MethodParameter originalParameter, MethodParameter methodParameter) {
50+
if (AnnotatedElementUtils.isAnnotated(methodParameter.getContainingClass(), JsonNaming.class)) {
51+
JsonNaming jsonNaming = methodParameter.getContainingClass().getAnnotation(JsonNaming.class);
52+
if (jsonNaming.value().equals(PropertyNamingStrategies.UpperSnakeCaseStrategy.class)) {
53+
try {
54+
Field parameterNameField = FieldUtils.getDeclaredField(methodParameter.getClass(), "parameterName",
55+
true);
56+
parameterNameField.set(methodParameter,
57+
PropertyNamingStrategies.UpperSnakeCaseStrategy.INSTANCE.translate(
58+
methodParameter.getParameterName()));
59+
}
60+
catch (IllegalAccessException e) {
61+
LOGGER.warn(e.getMessage());
62+
}
63+
}
64+
}
65+
}
66+
}

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/data/DataRestRequestService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public void buildParameters(OpenAPI openAPI, HandlerMethod handlerMethod, Reques
151151
*/
152152
public void buildCommonParameters(OpenAPI openAPI, RequestMethod requestMethod, MethodAttributes methodAttributes, Operation operation, String[] pNames, MethodParameter[] parameters,
153153
DataRestRepository dataRestRepository) {
154-
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer(), requestBuilder.isDefaultFlatParamObject());
154+
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getOptionalDelegatingMethodParameterCustomizers(), requestBuilder.isDefaultFlatParamObject());
155155
Class<?> domainType = dataRestRepository.getDomainType();
156156
for (MethodParameter methodParameter : parameters) {
157157
final String pName = methodParameter.getParameterName();

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/extractor/DelegatingMethodParameter.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ public class DelegatingMethodParameter extends MethodParameter {
113113
*
114114
* @param pNames the p names
115115
* @param parameters the parameters
116-
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
116+
* @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
117117
* @param defaultFlatParamObject the default flat param object
118118
* @return the method parameter [ ]
119119
*/
120120
public static MethodParameter[] customize(String[] pNames, MethodParameter[] parameters,
121-
Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer, boolean defaultFlatParamObject) {
121+
Optional<List<DelegatingMethodParameterCustomizer>> optionalDelegatingMethodParameterCustomizers, boolean defaultFlatParamObject) {
122122
List<MethodParameter> explodedParameters = new ArrayList<>();
123123
for (int i = 0; i < parameters.length; ++i) {
124124
MethodParameter p = parameters[i];
@@ -130,7 +130,7 @@ public static MethodParameter[] customize(String[] pNames, MethodParameter[] par
130130
if (!MethodParameterPojoExtractor.isSimpleType(paramClass)
131131
&& (hasFlatAnnotation || (defaultFlatParamObject && !hasNotFlatAnnotation && !AbstractRequestService.isRequestTypeToIgnore(paramClass)))) {
132132
MethodParameterPojoExtractor.extractFrom(paramClass).forEach(methodParameter -> {
133-
optionalDelegatingMethodParameterCustomizer.ifPresent(customizer -> customizer.customize(p, methodParameter));
133+
optionalDelegatingMethodParameterCustomizers.ifPresent(delegatingMethodParameterCustomizers -> delegatingMethodParameterCustomizers.forEach(customizer -> customizer.customize(p, methodParameter)));
134134
explodedParameters.add(methodParameter);
135135
});
136136
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
287287
String[] reflectionParametersNames = Arrays.stream(handlerMethod.getMethod().getParameters()).map(java.lang.reflect.Parameter::getName).toArray(String[]::new);
288288
if (pNames == null || Arrays.stream(pNames).anyMatch(Objects::isNull))
289289
pNames = reflectionParametersNames;
290-
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer(), this.defaultFlatParamObject);
290+
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getOptionalDelegatingMethodParameterCustomizers(), this.defaultFlatParamObject);
291291
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
292292
List<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
293293
Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());

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

+8-8
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public class GenericParameterService {
113113
/**
114114
* The Optional delegating method parameter customizer.
115115
*/
116-
private final Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer;
116+
private final Optional<List<DelegatingMethodParameterCustomizer>> optionalDelegatingMethodParameterCustomizers;
117117

118118
/**
119119
* The Web conversion service.
@@ -148,15 +148,15 @@ public class GenericParameterService {
148148
/**
149149
* Instantiates a new Generic parameter builder.
150150
* @param propertyResolverUtils the property resolver utils
151-
* @param optionalDelegatingMethodParameterCustomizer the optional delegating method parameter customizer
151+
* @param optionalDelegatingMethodParameterCustomizers the optional list delegating method parameter customizer
152152
* @param optionalWebConversionServiceProvider the optional web conversion service provider
153153
* @param objectMapperProvider the object mapper provider
154154
* @param javadocProviderOptional the javadoc provider
155155
*/
156-
public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional<DelegatingMethodParameterCustomizer> optionalDelegatingMethodParameterCustomizer,
156+
public GenericParameterService(PropertyResolverUtils propertyResolverUtils, Optional<List<DelegatingMethodParameterCustomizer>> optionalDelegatingMethodParameterCustomizers,
157157
Optional<WebConversionServiceProvider> optionalWebConversionServiceProvider, ObjectMapperProvider objectMapperProvider, Optional<JavadocProvider> javadocProviderOptional) {
158158
this.propertyResolverUtils = propertyResolverUtils;
159-
this.optionalDelegatingMethodParameterCustomizer = optionalDelegatingMethodParameterCustomizer;
159+
this.optionalDelegatingMethodParameterCustomizers = optionalDelegatingMethodParameterCustomizers;
160160
this.optionalWebConversionServiceProvider = optionalWebConversionServiceProvider;
161161
this.configurableBeanFactory = propertyResolverUtils.getFactory();
162162
this.expressionContext = (configurableBeanFactory != null ? new BeanExpressionContext(configurableBeanFactory, new RequestScope()) : null);
@@ -543,12 +543,12 @@ public boolean isFile(MethodParameter methodParameter) {
543543
}
544544

545545
/**
546-
* Gets delegating method parameter customizer.
546+
* Gets optional delegating method parameter customizers.
547547
*
548-
* @return the delegating method parameter customizer
548+
* @return the optional delegating method parameter customizers
549549
*/
550-
public Optional<DelegatingMethodParameterCustomizer> getDelegatingMethodParameterCustomizer() {
551-
return optionalDelegatingMethodParameterCustomizer;
550+
public Optional<List<DelegatingMethodParameterCustomizer>> getOptionalDelegatingMethodParameterCustomizers() {
551+
return optionalDelegatingMethodParameterCustomizers;
552552
}
553553

554554
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package test.org.springdoc.api.v30.app225;
2+
3+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
4+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
5+
import org.springdoc.core.annotations.ParameterObject;
6+
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestMapping;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
/**
12+
* @author bnasslahsen
13+
*/
14+
@RestController
15+
@RequestMapping
16+
public class HelloController {
17+
18+
@PostMapping("/testBoolean")
19+
public void HelloController(@ParameterObject RequestDto requestDto) {
20+
}
21+
}
22+
23+
@JsonNaming(PropertyNamingStrategies.UpperSnakeCaseStrategy.class)
24+
class RequestDto {
25+
private String personalNumber;
26+
27+
public String getPersonalNumber() {
28+
return personalNumber;
29+
}
30+
31+
public void setPersonalNumber(String personalNumber) {
32+
this.personalNumber = personalNumber;
33+
}
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * *
6+
* * * * * Copyright 2019-2022 the original author or authors.
7+
* * * * *
8+
* * * * * Licensed under the Apache License, Version 2.0 (the "License");
9+
* * * * * you may not use this file except in compliance with the License.
10+
* * * * * You may obtain a copy of the License at
11+
* * * * *
12+
* * * * * https://www.apache.org/licenses/LICENSE-2.0
13+
* * * * *
14+
* * * * * Unless required by applicable law or agreed to in writing, software
15+
* * * * * distributed under the License is distributed on an "AS IS" BASIS,
16+
* * * * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
* * * * * See the License for the specific language governing permissions and
18+
* * * * * limitations under the License.
19+
* * * *
20+
* * *
21+
* *
22+
*
23+
*/
24+
25+
package test.org.springdoc.api.v30.app225;
26+
27+
import test.org.springdoc.api.v30.AbstractSpringDocV30Test;
28+
29+
import org.springframework.boot.autoconfigure.SpringBootApplication;
30+
31+
public class SpringDocApp225Test extends AbstractSpringDocV30Test {
32+
33+
@SpringBootApplication
34+
static class SpringDocTestApp {}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "OpenAPI definition",
5+
"version": "v0"
6+
},
7+
"servers": [
8+
{
9+
"url": "http://localhost",
10+
"description": "Generated server url"
11+
}
12+
],
13+
"paths": {
14+
"/testBoolean": {
15+
"post": {
16+
"tags": [
17+
"hello-controller"
18+
],
19+
"operationId": "HelloController",
20+
"parameters": [
21+
{
22+
"name": "PERSONAL_NUMBER",
23+
"in": "query",
24+
"required": false,
25+
"schema": {
26+
"type": "string"
27+
}
28+
}
29+
],
30+
"responses": {
31+
"200": {
32+
"description": "OK"
33+
}
34+
}
35+
}
36+
}
37+
},
38+
"components": {}
39+
}

springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/app14/SpringDocApp14Test.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ static class SpringDocTestApp {
5757
@Bean
5858
@ConditionalOnMissingBean
5959
@Lazy(false)
60-
DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
60+
DataRestDelegatingMethodParameterCustomizer dataRestDelegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
6161
return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider, optionalRepositoryRestConfiguration);
6262
}
6363
}

springdoc-openapi-tests/springdoc-openapi-data-rest-tests/src/test/java/test/org/springdoc/api/app32/SpringDocApp32Test.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ static class SpringDocTestApp {
5252
@Bean
5353
@ConditionalOnMissingBean
5454
@Lazy(false)
55-
DelegatingMethodParameterCustomizer delegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
55+
DataRestDelegatingMethodParameterCustomizer dataRestDelegatingMethodParameterCustomizer(Optional<SpringDataWebPropertiesProvider> optionalSpringDataWebPropertiesProvider, Optional<RepositoryRestConfigurationProvider> optionalRepositoryRestConfiguration) {
5656
return new DataRestDelegatingMethodParameterCustomizer(optionalSpringDataWebPropertiesProvider, optionalRepositoryRestConfiguration);
5757
}
5858
}

0 commit comments

Comments
 (0)