swagger-api / swagger-parser

Swagger Spec to Java POJOs
http://swagger.io
Apache License 2.0
773 stars 525 forks source link

Parameters with the same name but different locations are incorrectly returned when using JSON references ($ref) with openapi 3.0.x. #2102

Open AdrianVasiliu opened 1 month ago

AdrianVasiliu commented 1 month ago

Multiple parameters with the same name but different locations (for instance, in: query vs in: header) are wrongly handled by the parser if:

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