swagger-parser
swagger-parser copied to clipboard
Parameters with the same name but different locations are incorrectly returned when using JSON references ($ref) with openapi 3.0.x.
Multiple parameters with the same name but different locations (for instance, in: query
vs in: header
) are wrongly handled by the parser if:
- the parameters are described using JSON references (
$ref
). - and the OpenAPI file declares its version as v3.0.x (
"openapi": "3.0.0"
or"openapi": "3.0.3"
).
The issue does NOT occur when either not using $ref
, or declaring "openapi": "3.1.0"
.
This looks like a bug because:
a) It violates the OpenAPI spec (both v3.0 and v3.1):
A unique parameter is defined by a combination of a name and location.
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.0.md#parameter-object
This should be respected regardless of whether the parameter is defined with, or without $ref
.
b) I don't see a mention of a change in this area between v3.0 and v3.1 (for instance here or here) which would justify the difference of behaviour.
How to reproduce (holds at least for swagger-parser 2.1.22 and 2.1.19):
package parsertest;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.parser.OpenAPIV3Parser;
import io.swagger.v3.parser.core.models.ParseOptions;
import java.io.IOException;
import java.util.List;
public class ParserTest {
public static void main(String[] args) throws IOException {
test("src/test/resources/parsertest/openapi-ok.json");
test("src/test/resources/jparsertest/openapi-ko.json");
}
private static void test(String openapiFile) throws IOException {
System.out.println("=== OpenAPI file:" + openapiFile);
final ParseOptions options = new ParseOptions();
options.setResolve(true);
options.setResolveCombinators(true);
options.setResolveFully(true);
final OpenAPI openAPI = new OpenAPIV3Parser().read(openapiFile, null, options);
final List<Parameter> parameters = openAPI.getPaths().get("/myoperation").getGet().getParameters();
parameters.forEach(parameter -> {
if (parameter != null) {
System.out.println("Parameter name: " + parameter.getName() + " in: " + parameter.getIn());
}
});
}
}
openapi-ko.json:
{
"openapi": "3.0.0",
"paths": {
"/myoperation": {
"get": {
"parameters": [
{"$ref": "#/components/parameters/schemaParam1"},
{"$ref": "#/components/parameters/schemaParam2"}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}}}}}}},
"components": {
"parameters": {
"schemaParam1": {
"name": "myParam",
"in": "query",
"schema": {
"type": "string"
}
},
"schemaParam2": {
"name": "myParam",
"in": "header",
"schema": {
"type": "string"
}
}}}
}
openapi-ok.json:
{
"openapi": "3.0.0",
"paths": {
"/myoperation": {
"get": {
"parameters": [
{
"in": "query",
"name": "myParam",
"schema": {
"type": "string"
}
},
{
"in": "header",
"name": "myParam",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK",
"content": {
"text/plain": {
"schema": {
"type": "string"
}
}}}}}}}
}
Output:
=== OpenAPI file:src/test/resources/parsertest/openapi-ok.json
Parameter name: myParam in: query
Parameter name: myParam in: header
=== OpenAPI file:src/test/resources/parsertest/openapi-ko.json
Parameter name: myParam in: query
That is, for the OpenAPI using $ref
, only one of the two parameters is returned by getParameters()
.
For comparison, if modifying the OpenAPI files to declare "openapi": 3.1.0
, both the header and query parameter are returned:
=== OpenAPI file:src/test/resources/parsertest/openapi-ok.json
Parameter name: myParam in: query
Parameter name: myParam in: header
=== OpenAPI file:src/test/resources/parsertest/openapi-ko.json
Parameter name: myParam in: query
Parameter name: myParam in: header