Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When @Get, using @Parameter over the method results in duplicate of the same parameter #1901

Closed
mskangg opened this issue Oct 21, 2022 · 1 comment
Labels
enhancement New feature or request

Comments

@mskangg
Copy link

mskangg commented Oct 21, 2022

Describe the bug

When @Get, using @Parameter over the method results in duplicate of the same parameter.

To Reproduce

What version of spring-boot you are using?

id 'org.springframework.boot' version '2.7.5'

What modules and versions of springdoc-openapi are you using?

implementation 'org.springdoc:springdoc-openapi-ui:1.6.12'

What is the actual and the expected result using OpenAPI Description (yml or json)?

actual

{
    "openapi": "3.0.1",
    "info": {
        "title": "OpenAPI definition",
        "version": "v0"
    },
    "servers": [
        {
            "url": "http://localhost:8081",
            "description": "Generated server url"
        }
    ],
    "paths": {
        "/not_duplicate_param": {
            "get": {
                "tags": [
                    "sample-controller"
                ],
                "summary": "Not Duplicate param",
                "operationId": "notDuplicateParam",
                "parameters": [
                    {
                        "name": "sample",
                        "in": "query",
                        "description": "sample",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "*/*": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/duplicate_param": {
            "get": {
                "tags": [
                    "sample-controller"
                ],
                "summary": "Duplicate param",
                "operationId": "duplicateParam",
                "parameters": [
                    {
                        "name": "sample",
                        "in": "query",
                        "description": "sample",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "sample",
                        "in": "query",
                        "description": "sample",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "*/*": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {}
}

expected

{
    "openapi": "3.0.1",
    "info": {
        "title": "OpenAPI definition",
        "version": "v0"
    },
    "servers": [
        {
            "url": "http://localhost:8081",
            "description": "Generated server url"
        }
    ],
    "paths": {
        "/not_duplicate_param": {
            "get": {
                "tags": [
                    "sample-controller"
                ],
                "summary": "Not Duplicate param",
                "operationId": "notDuplicateParam",
                "parameters": [
                    {
                        "name": "sample",
                        "in": "query",
                        "description": "sample",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "*/*": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/duplicate_param": {
            "get": {
                "tags": [
                    "sample-controller"
                ],
                "summary": "Duplicate param",
                "operationId": "duplicateParam",
                "parameters": [
                    {
                        "name": "sample",
                        "in": "query",
                        "description": "sample",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "*/*": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {}
}

Provide with a sample code (HelloController) or Test that reproduces the problem

Controller source code

@RestController
public class SampleController {

    @GetMapping("/not_duplicate_param")
    @Operation(summary = "Not Duplicate param")
    public String notDuplicateParam(
            @Parameter(name = "sample", required = true, description = "sample")
            @RequestParam String sample) {
        return "notDuplicateParam";
    }

    @GetMapping("/duplicate_param")
    @Operation(summary = "Duplicate param")
    @Parameter(name = "sample", required = true, description = "sample")
    public String duplicateParam(@RequestParam String sample) {
        return "duplicateParam";
    }
}

Source code 1 for expected problems
Source code 2 for expected problems

private LinkedHashMap<ParameterId, Parameter> getParameterLinkedHashMap(Components components, MethodAttributes methodAttributes, List<Parameter> operationParameters, Map<String, io.swagger.v3.oas.annotations.Parameter> parametersDocMap) {
	LinkedHashMap<ParameterId, Parameter> map = operationParameters.stream()
			.collect(Collectors.toMap(
					ParameterId::new,
					parameter ->  parameter,
					(u, v) -> {
						throw new IllegalStateException(String.format("Duplicate key %s", u));
					},
					LinkedHashMap::new
			));

	for (Map.Entry<String, io.swagger.v3.oas.annotations.Parameter> entry : parametersDocMap.entrySet()) {
		ParameterId parameterId = new ParameterId(entry.getValue());
		if (entry.getKey() != null && !map.containsKey(parameterId) && !entry.getValue().hidden()) {
			//Convert
			Parameter parameter = parameterBuilder.buildParameterFromDoc(entry.getValue(), components,
					methodAttributes.getJsonViewAnnotation(), methodAttributes.getLocale());
			map.put(parameterId, parameter);
		}
	}

	getHeaders(methodAttributes, map);
	map.forEach((parameterId, parameter) -> {
		if(StringUtils.isBlank(parameter.getIn()) && StringUtils.isBlank(parameter.get$ref()))
			parameter.setIn(ParameterIn.QUERY.toString());
	});
	return map;
}

Expected behavior

When using @Parameter over the method, io.swagger.v3.oas.annotations.parameter is entered in the argument parametersDocMap.
After that, when comparing the values of condition if(!map.containsKey(entry.getKey())), it is compared with hashCode of ParameterId, and even though it is the same ParameterId, it is classified as different ParameterId.

Hash code for "ParameterId.java"

Screenshots

@RestController
public class SampleController {

    @GetMapping("/not_duplicate_param")
    @Operation(summary = "Not Duplicate param")
    public String notDuplicateParam(
            @Parameter(name = "sample", required = true, description = "sample")
            @RequestParam String sample) {
        return "notDuplicateParam";
    }

    @GetMapping("/duplicate_param")
    @Operation(summary = "Duplicate param")
    @Parameter(name = "sample", required = true, description = "sample")
    public String duplicateParam(@RequestParam String sample) {
        return "duplicateParam";
    }
}

image

image

@bnasslahsen bnasslahsen added the invalid This doesn't seem right label Oct 22, 2022
@bnasslahsen
Copy link
Contributor

bnasslahsen commented Oct 22, 2022

@mskangg,

This not a bug. It's expected since #1816:
-Your @Parameter annotation is not precisely attached to your method @Parameter. There is no way to determine it's uniqueness.

To make it work you need:

	@GetMapping("/duplicate_param")
	@Operation(summary = "Duplicate param")
	@Parameter(name = "sample", required = true, in = ParameterIn.QUERY, description = "sample query")
	public String duplicateParam(@RequestParam String sample) {
		return "duplicateParam";
	}

I am adding an enhancement to cover some cases out of the box. We will presume, that if a single parameter is present in the method definition and in the @Parameter annotation, it will be matched!
You will be able to get what you were expecting in the next release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants