Skip to content

Commit 7426a96

Browse files
committed
When @get, using @parameter over the method results in duplicate of the same parameter. Fixes #1901.
1 parent 1c63f0f commit 7426a96

File tree

7 files changed

+230
-56
lines changed

7 files changed

+230
-56
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ public void buildCommonParameters(OpenAPI openAPI, RequestMethod requestMethod,
155155
Class<?> domainType = dataRestRepository.getDomainType();
156156
for (MethodParameter methodParameter : parameters) {
157157
final String pName = methodParameter.getParameterName();
158-
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameterBuilder);
158+
io.swagger.v3.oas.annotations.Parameter parameterDoc = AnnotatedElementUtils.findMergedAnnotation(
159+
AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()),
160+
io.swagger.v3.oas.annotations.Parameter.class);
161+
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameterBuilder,parameterDoc);
159162
if (isParamToIgnore(methodParameter)) {
160163
if (PersistentEntityResource.class.equals(methodParameter.getParameterType())) {
161164
Schema<?> schema = SpringDocAnnotationsUtils.resolveSchemaFromType(domainType, openAPI.getComponents(), null, methodParameter.getParameterAnnotations());
@@ -165,9 +168,6 @@ else if (methodParameter.getParameterAnnotation(BackendId.class) != null) {
165168
parameterInfo.setParameterModel(new Parameter().name("id").in(ParameterIn.PATH.toString()).schema(new StringSchema()));
166169
}
167170
Parameter parameter = null;
168-
io.swagger.v3.oas.annotations.Parameter parameterDoc = AnnotatedElementUtils.findMergedAnnotation(
169-
AnnotatedElementUtils.forAnnotations(methodParameter.getParameterAnnotations()),
170-
io.swagger.v3.oas.annotations.Parameter.class);
171171
if (parameterDoc != null) {
172172
if (parameterDoc.hidden() || parameterDoc.schema().hidden())
173173
continue;

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterId.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,19 @@ public ParameterId(Parameter parameter) {
6666
*/
6767
public ParameterId(io.swagger.v3.oas.annotations.Parameter parameter) {
6868
this.pName = parameter.name();
69-
this.paramType = parameter.in().toString();
70-
this.$ref = parameter.ref();
69+
this.paramType = StringUtils.isNotBlank(parameter.in().toString()) ? parameter.in().toString():null;
70+
this.$ref = StringUtils.isNotBlank(parameter.ref()) ? parameter.ref():null;
71+
}
72+
73+
/**
74+
* Instantiates a new Parameter id.
75+
*
76+
* @param pName the p name
77+
* @param paramType the param type
78+
*/
79+
public ParameterId(String pName, String paramType) {
80+
this.pName = pName;
81+
this.paramType =paramType;
7182
}
7283

7384
/**

springdoc-openapi-starter-common/src/main/java/org/springdoc/core/models/ParameterInfo.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
package org.springdoc.core.models;
2626

27+
import io.swagger.v3.oas.annotations.Parameter;
2728
import io.swagger.v3.oas.annotations.enums.ParameterIn;
2829
import org.apache.commons.lang3.StringUtils;
2930
import org.slf4j.Logger;
@@ -79,6 +80,12 @@ public class ParameterInfo {
7980
*/
8081
private boolean requestPart;
8182

83+
84+
/**
85+
* The Parameter id.
86+
*/
87+
private ParameterId parameterId;
88+
8289
/**
8390
* The constant LOGGER.
8491
*/
@@ -89,8 +96,9 @@ public class ParameterInfo {
8996
* @param pName the parameter name
9097
* @param methodParameter the method parameter
9198
* @param genericParameterService the parameter builder
99+
* @param parameterAnnotation the parameter annotation
92100
*/
93-
public ParameterInfo(String pName, MethodParameter methodParameter, GenericParameterService genericParameterService) {
101+
public ParameterInfo(String pName, MethodParameter methodParameter, GenericParameterService genericParameterService, Parameter parameterAnnotation) {
94102
RequestHeader requestHeader = methodParameter.getParameterAnnotation(RequestHeader.class);
95103
RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class);
96104
PathVariable pathVar = methodParameter.getParameterAnnotation(PathVariable.class);
@@ -126,6 +134,16 @@ else if (cookieValue != null)
126134
}
127135

128136
this.required = this.required && !methodParameter.isOptional();
137+
if (parameterAnnotation != null) {
138+
this.parameterId = new ParameterId(parameterAnnotation);
139+
if (StringUtils.isBlank(parameterId.getpName()))
140+
this.parameterId.setpName(this.pName);
141+
if (StringUtils.isBlank(parameterId.getParamType()))
142+
this.parameterId.setParamType(this.paramType);
143+
}
144+
else
145+
this.parameterId = new ParameterId(this.pName, paramType);
146+
129147
}
130148

131149
/**
@@ -297,4 +315,23 @@ public boolean isRequestPart() {
297315
public void setRequestPart(boolean requestPart) {
298316
this.requestPart = requestPart;
299317
}
318+
319+
320+
/**
321+
* Gets parameter id.
322+
*
323+
* @return the parameter id
324+
*/
325+
public ParameterId getParameterId() {
326+
return parameterId;
327+
}
328+
329+
/**
330+
* Sets parameter id.
331+
*
332+
* @param parameterId the parameter id
333+
*/
334+
public void setParameterId(ParameterId parameterId) {
335+
this.parameterId = parameterId;
336+
}
300337
}

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

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
261261
parameters = DelegatingMethodParameter.customize(pNames, parameters, parameterBuilder.getDelegatingMethodParameterCustomizer(), this.defaultFlatParamObject);
262262
RequestBodyInfo requestBodyInfo = new RequestBodyInfo();
263263
List<Parameter> operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>();
264-
Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
264+
Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap = getApiParameters(handlerMethod.getMethod());
265265
Components components = openAPI.getComponents();
266266

267267
JavadocProvider javadocProvider = operationService.getJavadocProvider();
@@ -274,10 +274,10 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod,
274274
io.swagger.v3.oas.annotations.Parameter.class);
275275

276276
final String pName = methodParameter.getParameterName();
277-
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameterBuilder);
277+
ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameterBuilder, parameterDoc);
278278

279279
if (parameterDoc == null)
280-
parameterDoc = parametersDocMap.get(parameterInfo.getpName());
280+
parameterDoc = parametersDocMap.get(parameterInfo.getParameterId());
281281

282282
if (parameterDoc == null) {
283283
io.swagger.v3.oas.annotations.media.Schema schema = AnnotatedElementUtils.findMergedAnnotation(
@@ -364,35 +364,43 @@ else if (!RequestMethod.GET.equals(requestMethod) || OpenApiVersion.OPENAPI_3_1.
364364
* @param parametersDocMap the parameters doc map
365365
* @return the parameter linked hash map
366366
*/
367-
private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
368-
LinkedHashMap<ParameterId, Parameter> map = operationParameters.stream()
369-
.collect(Collectors.toMap(
370-
ParameterId::new,
371-
parameter -> parameter,
372-
(u, v) -> {
373-
throw new IllegalStateException(String.format("Duplicate key %s", u));
374-
},
375-
LinkedHashMap::new
376-
));
377-
378-
for (Map.Entry<String, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) {
379-
ParameterId parameterId = new ParameterId(entry.getValue());
380-
if (entry.getKey() != null && !map.containsKey(parameterId) && !entry.getValue().hidden()) {
381-
//Convert
382-
Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components,
383-
methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
384-
map.put(parameterId, parameter);
367+
private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
368+
LinkedHashMap<ParameterId, Parameter> map = operationParameters.stream().collect(Collectors.toMap(ParameterId::new, parameter -> parameter, (u, v) -> {
369+
throw new IllegalStateException(String.format("Duplicate key %s", u));
370+
}, LinkedHashMap::new));
371+
372+
for (Map.Entry<ParameterId, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) {
373+
ParameterId parameterId = entry.getKey();
374+
if (parameterId != null && !map.containsKey(parameterId) && !entry.getValue().hidden()) {
375+
Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components, methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
376+
//proceed with the merge if possible
377+
if (map.containsKey(parameterId)) {
378+
GenericParameterService.mergeParameter(map.get(parameterId), parameter);
379+
map.put(parameterId, parameter);
380+
}
381+
else {
382+
long mumParamsWithName = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count();
383+
long mumParamsDocWithName = parametersDocMap.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).count();
384+
if (mumParamsWithName == 1 && mumParamsDocWithName == 1) {
385+
Optional<ParameterId> parameterIdWithSameNameOptional = map.keySet().stream().filter(parameterId1 -> parameterId.getpName().equals(parameterId1.getpName())).findAny();
386+
parameterIdWithSameNameOptional.ifPresent(parameterIdWithSameName -> {
387+
GenericParameterService.mergeParameter(map.get(parameterIdWithSameName), parameter);
388+
map.put(parameterIdWithSameName, parameter);
389+
});
390+
}
391+
else
392+
map.put(parameterId, parameter);
393+
}
385394
}
386395
}
387396

388-
getHeaders(methodAttributes, map);
389-
map.forEach((parameterId, parameter) -> {
390-
if(StringUtils.isBlank(parameter.getIn()) && StringUtils.isBlank(parameter.get$ref()))
397+
getHeaders(methodAttributes, map); map.forEach((parameterId, parameter) -> {
398+
if (StringUtils.isBlank(parameter.getIn()) && StringUtils.isBlank(parameter.get$ref()))
391399
parameter.setIn(ParameterIn.QUERY.toString());
392-
});
393-
return map;
400+
}); return map;
394401
}
395402

403+
396404
/**
397405
* Gets headers.
398406
*
@@ -650,34 +658,22 @@ public RequestBodyService getRequestBodyBuilder() {
650658
* @param method the method
651659
* @return the api parameters
652660
*/
653-
private Map<String, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Method method) {
661+
private Map<ParameterId, io.swagger.v3.oas.annotations.Parameter> getApiParameters(Method method) {
654662
Class<?> declaringClass = method.getDeclaringClass();
655663

656-
Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDoc = AnnotatedElementUtils
657-
.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameters.class);
658-
LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParametersMap = apiParametersDoc.stream()
659-
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2,
660-
LinkedHashMap::new));
661-
662-
Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDocDeclaringClass = AnnotatedElementUtils
663-
.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameters.class);
664-
LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream()
665-
.flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2,
666-
LinkedHashMap::new));
664+
Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameters.class);
665+
LinkedHashMap<ParameterId, io.swagger.v3.oas.annotations.Parameter> apiParametersMap = apiParametersDoc.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
666+
667+
Set<io.swagger.v3.oas.annotations.Parameters> apiParametersDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameters.class);
668+
LinkedHashMap<ParameterId, io.swagger.v3.oas.annotations.Parameter> apiParametersDocDeclaringClassMap = apiParametersDocDeclaringClass.stream().flatMap(x -> Stream.of(x.value())).collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
667669
apiParametersMap.putAll(apiParametersDocDeclaringClassMap);
668670

669-
Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDoc = AnnotatedElementUtils
670-
.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameter.class);
671-
LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParameterDocMap = apiParameterDoc.stream()
672-
.collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2,
673-
LinkedHashMap::new));
671+
Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDoc = AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.Parameter.class);
672+
LinkedHashMap<ParameterId, io.swagger.v3.oas.annotations.Parameter> apiParameterDocMap = apiParameterDoc.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
674673
apiParametersMap.putAll(apiParameterDocMap);
675674

676-
Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClass = AnnotatedElementUtils
677-
.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameter.class);
678-
LinkedHashMap<String, io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream()
679-
.collect(Collectors.toMap(io.swagger.v3.oas.annotations.Parameter::name, x -> x, (e1, e2) -> e2,
680-
LinkedHashMap::new));
675+
Set<io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClass = AnnotatedElementUtils.findAllMergedAnnotations(declaringClass, io.swagger.v3.oas.annotations.Parameter.class);
676+
LinkedHashMap<ParameterId, io.swagger.v3.oas.annotations.Parameter> apiParameterDocDeclaringClassMap = apiParameterDocDeclaringClass.stream().collect(Collectors.toMap(ParameterId::new, x -> x, (e1, e2) -> e2, LinkedHashMap::new));
681677
apiParametersMap.putAll(apiParameterDocDeclaringClassMap);
682678

683679
return apiParametersMap;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public static Parameter mergeParameter(List<Parameter> existingParamDoc, Paramet
200200
* @param paramCalcul the param calcul
201201
* @param paramDoc the param doc
202202
*/
203-
private static void mergeParameter(Parameter paramCalcul, Parameter paramDoc) {
203+
public static void mergeParameter(Parameter paramCalcul, Parameter paramDoc) {
204204
if (StringUtils.isBlank(paramDoc.getDescription()))
205205
paramDoc.setDescription(paramCalcul.getDescription());
206206

springdoc-openapi-starter-webmvc-api/src/test/java/test/org/springdoc/api/v30/app87/HelloController.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.web.bind.annotation.PathVariable;
3737
import org.springframework.web.bind.annotation.PutMapping;
3838
import org.springframework.web.bind.annotation.RequestHeader;
39+
import org.springframework.web.bind.annotation.RequestParam;
3940
import org.springframework.web.bind.annotation.RestController;
4041

4142

@@ -69,4 +70,28 @@ void list(@RequestHeader(value = "access_token", required = false)
6970
}
7071
public static class Item {
7172
}
73+
74+
75+
@GetMapping("/duplicate_param")
76+
@Operation(summary = "Duplicate param")
77+
@Parameter(name = "sample", required = true, in = ParameterIn.HEADER, description = "sample Header")
78+
@Parameter(name = "sample", required = true, in = ParameterIn.QUERY, description = "sample query")
79+
public String duplicateParam(@RequestParam String sample, @RequestHeader("sample") String sampleHeader) {
80+
return "duplicateParam";
81+
}
82+
83+
@GetMapping("/duplicate_param2")
84+
@Operation(summary = "Duplicate param")
85+
@Parameter(name = "sample", required = true, description = "sample")
86+
public String duplicateParam2(@RequestParam String sample) {
87+
return "duplicateParam";
88+
}
89+
90+
91+
@GetMapping("/duplicate_param3")
92+
@Operation(summary = "Duplicate param")
93+
@Parameter(name = "sample", required = true, description = "sample")
94+
public String duplicateParam3(@RequestHeader String sample) {
95+
return "duplicateParam";
96+
}
7297
}

0 commit comments

Comments
 (0)