Skip to content

Commit f24cdab

Browse files
committed
Parameters with same name but different locations can't render in UI properly. Fixes #1816
1 parent fd2f9a4 commit f24cdab

File tree

5 files changed

+226
-18
lines changed

5 files changed

+226
-18
lines changed

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

+23-17
Original file line numberDiff line numberDiff line change
@@ -323,24 +323,24 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
323323
}
324324
}
325325

326-
LinkedHashMap<String, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
326+
LinkedHashMap<ParameterId, Parameter> map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap);
327327
RequestBody requestBody = requestBodyInfo.getRequestBody();
328328
// support form-data
329329
if (defaultSupportFormData && requestBody != null
330330
&& requestBody.getContent() != null
331331
&& requestBody.getContent().containsKey(org.springframework.http.MediaType.MULTIPART_FORM_DATA_VALUE)) {
332-
Iterator<Entry<String, Parameter>> it = map.entrySet().iterator();
332+
Iterator<Entry<ParameterId, Parameter>> it = map.entrySet().iterator();
333333
while (it.hasNext()) {
334-
Entry<String, Parameter> entry = it.next();
334+
Entry<ParameterId, Parameter> entry = it.next();
335335
Parameter parameter = entry.getValue();
336336
if (!ParameterIn.PATH.toString().equals(parameter.getIn())) {
337337
io.swagger.v3.oas.models.media.Schema<?> itemSchema = new io.swagger.v3.oas.models.media.Schema<>();
338-
itemSchema.setName(entry.getKey());
338+
itemSchema.setName(entry.getKey().getpName());
339339
itemSchema.setDescription(parameter.getDescription());
340340
itemSchema.setDeprecated(parameter.getDeprecated());
341341
if (parameter.getExample() != null)
342342
itemSchema.setExample(parameter.getExample());
343-
requestBodyInfo.addProperties(entry.getKey(), itemSchema);
343+
requestBodyInfo.addProperties(entry.getKey().getpName(), itemSchema);
344344
it.remove();
345345
}
346346
}
@@ -358,27 +358,32 @@ else if (!RequestMethod.GET.equals(requestMethod)) {
358358
* @param parametersDocMap the parameters doc map
359359
* @return the parameter linked hash map
360360
*/
361-
private LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
362-
LinkedHashMap<String, Parameter> map = operationParameters.stream()
361+
private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
362+
LinkedHashMap<ParameterId, Parameter> map = operationParameters.stream()
363363
.collect(Collectors.toMap(
364-
parameter -> parameter.getName() != null ? parameter.getName() : Integer.toString(parameter.hashCode()),
365-
parameter -> parameter,
364+
ParameterId::new,
365+
parameter -> parameter,
366366
(u, v) -> {
367367
throw new IllegalStateException(String.format("Duplicate key %s", u));
368368
},
369369
LinkedHashMap::new
370370
));
371371

372372
for (Map.Entry<String, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) {
373-
if (entry.getKey() != null && !map.containsKey(entry.getKey()) && !entry.getValue().hidden()) {
373+
ParameterId parameterId = new ParameterId(entry.getValue());
374+
if (entry.getKey() != null && !map.containsKey(parameterId) && !entry.getValue().hidden()) {
374375
//Convert
375376
Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components,
376377
methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
377-
map.put(entry.getKey(), parameter);
378+
map.put(parameterId, parameter);
378379
}
379380
}
380381

381382
getHeaders(methodAttributes, map);
383+
map.forEach((parameterId, parameter) -> {
384+
if(StringUtils.isBlank(parameter.getIn()) && StringUtils.isBlank(parameter.get$ref()))
385+
parameter.setIn(ParameterIn.QUERY.toString());
386+
});
382387
return map;
383388
}
384389

@@ -390,22 +395,23 @@ private LinkedHashMap<String, Parameter> getParameterLinkedHashMap(Components co
390395
* @return the headers
391396
*/
392397
@SuppressWarnings("unchecked")
393-
public static Collection<Parameter> getHeaders(MethodAttributes methodAttributes, Map<String, Parameter> map) {
398+
public static Collection<Parameter> getHeaders(MethodAttributes methodAttributes, Map<ParameterId, Parameter> map) {
394399
for (Map.Entry<String, String> entry : methodAttributes.getHeaders().entrySet()) {
395400
StringSchema schema = new StringSchema();
396401
if (StringUtils.isNotEmpty(entry.getValue()))
397402
schema.addEnumItem(entry.getValue());
398403
Parameter parameter = new Parameter().in(ParameterIn.HEADER.toString()).name(entry.getKey()).schema(schema);
399-
if (map.containsKey(entry.getKey())) {
400-
parameter = map.get(entry.getKey());
404+
ParameterId parameterId = new ParameterId(parameter);
405+
if (map.containsKey(parameterId)) {
406+
parameter = map.get(parameterId);
401407
List existingEnum = null;
402408
if (parameter.getSchema() != null && !CollectionUtils.isEmpty(parameter.getSchema().getEnum()))
403409
existingEnum = parameter.getSchema().getEnum();
404-
if (StringUtils.isNotEmpty(entry.getValue()) && (existingEnum==null || !existingEnum.contains(entry.getValue())))
410+
if (StringUtils.isNotEmpty(entry.getValue()) && (existingEnum == null || !existingEnum.contains(entry.getValue())))
405411
parameter.getSchema().addEnumItemObject(entry.getValue());
406412
parameter.setSchema(parameter.getSchema());
407413
}
408-
map.put(entry.getKey(), parameter);
414+
map.put(parameterId, parameter);
409415
}
410416
return map.values();
411417
}
@@ -503,7 +509,7 @@ public Parameter buildParams(ParameterInfo parameterInfo, Components components,
503509
// By default
504510
if (!isRequestBodyParam(requestMethod, parameterInfo)) {
505511
parameterInfo.setRequired(!((DelegatingMethodParameter) methodParameter).isNotRequired() && !methodParameter.isOptional());
506-
parameterInfo.setParamType(QUERY_PARAM);
512+
//parameterInfo.setParamType(QUERY_PARAM);
507513
parameterInfo.setDefaultValue(null);
508514
return this.buildParam(parameterInfo, components, jsonView);
509515
}

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

+6-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,12 @@ public static Parameter mergeParameter(List<Parameter> existingParamDoc, Paramet
169169
Parameter result = paramCalcul;
170170
if (paramCalcul != null && paramCalcul.getName() != null) {
171171
final String name = paramCalcul.getName();
172-
Parameter paramDoc = existingParamDoc.stream().filter(p -> name.equals(p.getName())).findAny().orElse(null);
172+
final String in = paramCalcul.getIn();
173+
Parameter paramDoc = existingParamDoc.stream().filter(p ->
174+
name.equals(p.getName())
175+
&& (StringUtils.isEmpty(in) || StringUtils.isEmpty(p.getIn()) || in.equals(p.getIn()))
176+
).findAny()
177+
.orElse(null);
173178
if (paramDoc != null) {
174179
mergeParameter(paramCalcul, paramDoc);
175180
result = paramDoc;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*
2+
*
3+
* *
4+
* * *
5+
* * * * Copyright 2019-2022 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;
24+
25+
import java.util.Objects;
26+
27+
import io.swagger.v3.oas.annotations.enums.ParameterIn;
28+
import io.swagger.v3.oas.models.parameters.Parameter;
29+
import org.apache.commons.lang3.StringUtils;
30+
31+
/**
32+
* The type Parameter Id.
33+
* @author bnasslahsen
34+
*/
35+
public class ParameterId {
36+
37+
/**
38+
* The P name.
39+
*/
40+
private String pName;
41+
42+
/**
43+
* The Param type.
44+
*/
45+
private String paramType;
46+
47+
/**
48+
* The Ref.
49+
*/
50+
private String $ref;
51+
52+
/**
53+
* Instantiates a new Parameter id.
54+
*
55+
* @param parameter the parameter
56+
*/
57+
public ParameterId(Parameter parameter) {
58+
this.pName = parameter.getName();
59+
this.paramType = parameter.getIn();
60+
this.$ref = parameter.get$ref();
61+
}
62+
63+
/**
64+
* Instantiates a new Parameter id.
65+
*
66+
* @param parameter the parameter
67+
*/
68+
public ParameterId(io.swagger.v3.oas.annotations.Parameter parameter) {
69+
this.pName = parameter.name();
70+
this.paramType = parameter.in().toString();
71+
this.$ref = parameter.ref();
72+
}
73+
74+
/**
75+
* Gets name.
76+
*
77+
* @return the name
78+
*/
79+
public String getpName() {
80+
return pName;
81+
}
82+
83+
/**
84+
* Sets name.
85+
*
86+
* @param pName the p name
87+
*/
88+
public void setpName(String pName) {
89+
this.pName = pName;
90+
}
91+
92+
/**
93+
* Gets param type.
94+
*
95+
* @return the param type
96+
*/
97+
public String getParamType() {
98+
return paramType;
99+
}
100+
101+
/**
102+
* Sets param type.
103+
*
104+
* @param paramType the param type
105+
*/
106+
public void setParamType(String paramType) {
107+
this.paramType = paramType;
108+
}
109+
110+
/**
111+
* Get ref string.
112+
*
113+
* @return the string
114+
*/
115+
public String get$ref() {
116+
return $ref;
117+
}
118+
119+
/**
120+
* Set ref.
121+
*
122+
* @param $ref the ref
123+
*/
124+
public void set$ref(String $ref) {
125+
this.$ref = $ref;
126+
}
127+
128+
@Override
129+
public boolean equals(Object o) {
130+
if (this == o) return true;
131+
if (o == null || getClass() != o.getClass()) return false;
132+
ParameterId that = (ParameterId) o;
133+
if (this.pName == null && StringUtils.isBlank(this.paramType))
134+
return Objects.equals($ref, that.$ref);
135+
if (this.pName != null && StringUtils.isBlank(this.paramType))
136+
return Objects.equals(pName, that.pName);
137+
138+
return Objects.equals(pName, that.pName) && Objects.equals(paramType, that.paramType);
139+
140+
}
141+
142+
@Override
143+
public int hashCode() {
144+
return Objects.hash(pName, paramType, $ref);
145+
}
146+
}

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

+18
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,17 @@
2525
import java.util.UUID;
2626

2727
import io.swagger.v3.oas.annotations.Operation;
28+
import io.swagger.v3.oas.annotations.Parameter;
29+
import io.swagger.v3.oas.annotations.enums.ParameterIn;
30+
import io.swagger.v3.oas.annotations.media.Schema;
2831
import io.swagger.v3.oas.annotations.parameters.RequestBody;
2932

3033
import org.springframework.http.ResponseEntity;
3134
import org.springframework.web.bind.annotation.CookieValue;
35+
import org.springframework.web.bind.annotation.GetMapping;
3236
import org.springframework.web.bind.annotation.PathVariable;
3337
import org.springframework.web.bind.annotation.PutMapping;
38+
import org.springframework.web.bind.annotation.RequestHeader;
3439
import org.springframework.web.bind.annotation.RestController;
3540

3641

@@ -49,6 +54,19 @@ public ResponseEntity<Item> putItem(
4954
return ResponseEntity.ok(item);
5055
}
5156

57+
/**
58+
* List tracker data.
59+
*
60+
* @return the tracker data
61+
*/
62+
@GetMapping(value = "/values/data")
63+
void list(@RequestHeader(value = "access_token", required = false)
64+
@Parameter(name = "access_token", in = ParameterIn.HEADER, description = "token in header", schema = @Schema(implementation = String.class))
65+
String tokenInHeader,@CookieValue(value = "access_token", required = false)
66+
@Parameter(name = "access_token", in = ParameterIn.COOKIE, description = "token in cookie", schema = @Schema(implementation = String.class))
67+
String tokenInCookie) {
68+
69+
}
5270
public static class Item {
5371
}
5472
}

springdoc-openapi-webmvc-core/src/test/resources/results/3.0.1/app87.json

+33
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,39 @@
5858
}
5959
}
6060
}
61+
},
62+
"/values/data": {
63+
"get": {
64+
"tags": [
65+
"hello-controller"
66+
],
67+
"operationId": "list",
68+
"parameters": [
69+
{
70+
"name": "access_token",
71+
"in": "header",
72+
"description": "token in header",
73+
"required": false,
74+
"schema": {
75+
"type": "string"
76+
}
77+
},
78+
{
79+
"name": "access_token",
80+
"in": "cookie",
81+
"description": "token in cookie",
82+
"required": false,
83+
"schema": {
84+
"type": "string"
85+
}
86+
}
87+
],
88+
"responses": {
89+
"200": {
90+
"description": "OK"
91+
}
92+
}
93+
}
6194
}
6295
},
6396
"components": {

0 commit comments

Comments
 (0)