swagger-api / swagger-parser

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

[Bug] Parameter/Response $refs are not resolving in OpenAPI 3.1 definitions #1950

Closed aneeshafedo closed 6 months ago

aneeshafedo commented 1 year ago

When options.setResolve(true) is set, the $refs in parameter schemas of OpenAPI 3.0 definitions are resolved correctly. However, when using a 3.1 OpenAPI definition, the parameter $ref does not get resolved even when options.setResolve(true) is enabled. The issue persists even after attempting to set resolveFully to true.

aneeshafedo commented 1 year ago

This also affect response reference resolving. Ex:

openapi: 3.1.0
servers:
  - url: https://api.placekit.co
info:
  contact:
    name: API Support
    url: https://api.placekit.co
  description: PlaceKit OpenAPI Specifications ([repository](https://github.com/placekit/api-reference))
  termsOfService: https://placekit.io/terms
  title: PlaceKit API Reference
  version: 1.0.0
  x-apisguru-categories:
    - location
  x-origin:
    - format: openapi
      url: https://raw.githubusercontent.com/placekit/api-reference/main/openapi.yml
      version: "3.1"
  x-providerName: placekit.co
security:
  - api_key: []
paths:
  /reverse:
    post:
      description: |
        Performs a reverse geocoding search.

        It will return the closest results around `coordinates`.\
        If `coordinates` are not set, it will use the user's IP to approximate its coordinates but results will be less accurate (city level accuracy instead of street level accuracy).
      operationId: reverse
      requestBody:
        content:
          application/json:
            schema:
                properties:
                    countryByIP:
                      default: true
                      description: |
                        Automatically select the country to search in via the user IP's detected location.\
                        Returned results will be coming from the user's country's IP.\
                        If set to `true`, the parameter `countries` acts as a fallback.
                      type: boolean
                  type: object
        description: Request parameters
        required: false
      responses:
        "200":
          $ref: "#/components/responses/200"
        "401":
          $ref: "#/components/responses/401"
        "403":
          $ref: "#/components/responses/403"
        "404":
          $ref: "#/components/responses/404"
        "412":
          $ref: "#/components/responses/412"
        "422":
          $ref: "#/components/responses/422"
        "429":
          $ref: "#/components/responses/429"
      summary: Reverse geocoding 
components:
  responses:
    "200":
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/results"
      description: Returns a list of matching addresses
      headers:
        RateLimit-Limit:
          description: Request limit per minute.
          example: 5
          schema:
            type: integer
        RateLimit-Remaining:
          description: The number of requests left for the time window.
          example: 2
          schema:
            type: integer
        RateLimit-Reset:
          description: Indicates how many seconds are left to wait before making a follow-up request.
          example: 34
          schema:
            type: integer
        Retry-After:
          description: Indicates how many seconds to wait before making a follow-up request.
          example: 60
          schema:
            type: integer
    "401":
      content:
        application/json:
          schema:
            properties:
              message:
                example: Access denied authentication failed
                type: string
            type: object
      description: Access denied authentication failed
    "403":
      content:
        application/json:
          schema:
            properties:
              message:
                example: You are not authorized to access this resource
                type: string
            type: object
      description: You are not authorized to access this resource
    "404":
      content:
        application/json:
          schema:
            properties:
              message:
                example: Route not found
                type: string
            type: object
      description: Route not found
    "412":
      content:
        application/json:
          schema:
            properties:
              message:
                example: Access denied missing credentials
                type: string
            type: object
      description: Access denied missing credentials
    "422":
      content:
        application/json:
          schema:
            properties:
              errors:
                example:
                  - location: body
                    msg: Must be an integer between 1 and 20 included.
                    param: maxResults
                    value: 42
                  - location: body
                    msg: This country is not supported. Contact us if you need it.
                    param: countries[0]
                    value: sx
                items:
                  $ref: "#/components/schemas/validationError"
                type: array
              message:
                example: "Invalid body parameters. Check the API documentation: https://api.placekit.io/"
                type: string
            type: object
      description: Invalid body parameters
    "429":
      content:
        application/json:
          schema:
            properties:
              message:
                example: Too many requests from this IP, please try again in a minute
                type: string
              status:
                example: 429
                type: integer
            type: object
      description: Too many requests from this IP, please try again in a minute
  schemas:
    results:
      properties:
        maxResults:
          description: Maximum number of results items returned.
          example: 5
          type: integer
        query:
          description: Search text query used for this response.
          example: 42 avenue Champs Elysees Paris
          type: string
        results:
          items:
            $ref: "#/components/schemas/entity"
          type: array
        resultsCount:
          description: Number of items results found.
          example: 2
          type: integer
      type: object

For OpenAPI 3.0 definitions, when setResolve is true , the responses were resolved inside the operation by the parser. But with OpenAPI 3.1, that is not happening. This has been a serious issue for us when generating clients using the 3.1 specification.

sajjanm commented 7 months ago

We had faced something similar in our project as well

references to parameters didn’t work. it was one of the query params that was causing this issue and it is not ideal to add them to every path since its for pagination. and we were using openapi-generator-cli.jar to generate the typescript sdk based on the openapi.yml file.

In our case, we copied the actual spec in one for the request, and for all other requests, it was using $ref, it worked!

example:

  /users:
    get:
      tags:
        - Users
      operationId: listUsers
      parameters:
        - name: page
          in: query
          description: The current page (starts at 0)
          required: false
          schema:
            type: integer
            minimum: 0
            default: 0
        - name: size
          in: query
          description: The size of the returned page
          required: false
          schema:
            type: integer
            maximum: 100
            minimum: 10
            default: 20
gracekarina commented 7 months ago

Hi @aneeshafedo, have you tried with resolveFully true option, I just tried and this does not seem to be an issue anymore. Could you please confirm? Please test with this configuration:

OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
ParseOptions options = new ParseOptions();
options.setResolveFully(true);
SwaggerParseResult parseResult = openApiParser.readLocation("3.1.0/issue-1950.yaml", null, options);
OpenAPI openAPI = parseResult.getOpenAPI();

This is the result I get now:

openapi: 3.1.0
info:
  title: PlaceKit API Reference
  description: "PlaceKit OpenAPI Specifications ([repository](https://github.com/placekit/api-reference))"
  termsOfService: https://placekit.io/terms
  contact:
    name: API Support
    url: https://api.placekit.co
  version: 1.0.0
  x-apisguru-categories:
  - location
  x-origin:
  - format: openapi
    url: https://raw.githubusercontent.com/placekit/api-reference/main/openapi.yml
    version: "3.1"
  x-providerName: placekit.co
servers:
- url: https://api.placekit.co
security:
- api_key: []
paths:
  /reverse:
    post:
      summary: Reverse geocoding
      description: |
        Performs a reverse geocoding search.

        It will return the closest results around `coordinates`.\
        If `coordinates` are not set, it will use the user's IP to approximate its coordinates but results will be less accurate (city level accuracy instead of street level accuracy).
      operationId: reverse
      requestBody:
        description: Request parameters
        content:
          application/json:
            schema:
              properties:
                countryByIP:
                  description: |
                    Automatically select the country to search in via the user IP's detected location.\
                    Returned results will be coming from the user's country's IP.\
                    If set to `true`, the parameter `countries` acts as a fallback.
                  default: true
        required: false
      responses:
        "200":
          description: Returns a list of matching addresses
          headers:
            RateLimit-Limit:
              description: Request limit per minute.
              style: simple
              explode: false
              schema: {}
              example: 5
            RateLimit-Remaining:
              description: The number of requests left for the time window.
              style: simple
              explode: false
              schema: {}
              example: 2
            RateLimit-Reset:
              description: Indicates how many seconds are left to wait before making
                a follow-up request.
              style: simple
              explode: false
              schema: {}
              example: 34
            Retry-After:
              description: Indicates how many seconds to wait before making a follow-up
                request.
              style: simple
              explode: false
              schema: {}
              example: 60
          content:
            application/json:
              schema:
                properties:
                  maxResults:
                    description: Maximum number of results items returned.
                    example: 5
                  query:
                    description: Search text query used for this response.
                    example: 42 avenue Champs Elysees Paris
                  results:
                    items:
                      $ref: '#/components/schemas/entity'
                  resultsCount:
                    description: Number of items results found.
                    example: 2
        "401":
          description: Access denied authentication failed
          content:
            application/json:
              schema:
                properties:
                  message:
                    example: Access denied authentication failed
        "403":
          description: You are not authorized to access this resource
          content:
            application/json:
              schema:
                properties:
                  message:
                    example: You are not authorized to access this resource
        "404":
          description: Route not found
          content:
            application/json:
              schema:
                properties:
                  message:
                    example: Route not found
        "412":
          description: Access denied missing credentials
          content:
            application/json:
              schema:
                properties:
                  message:
                    example: Access denied missing credentials
        "422":
          description: Invalid body parameters
          content:
            application/json:
              schema:
                properties:
                  errors:
                    example:
                    - location: body
                      msg: Must be an integer between 1 and 20 included.
                      param: maxResults
                      value: 42
                    - location: body
                      msg: This country is not supported. Contact us if you need it.
                      param: "countries[0]"
                      value: sx
                    items:
                      $ref: '#/components/schemas/validationError'
                  message:
                    example: "Invalid body parameters. Check the API documentation:\
                      \ https://api.placekit.io/"
        "429":
          description: "Too many requests from this IP, please try again in a minute"
          content:
            application/json:
              schema:
                properties:
                  message:
                    example: "Too many requests from this IP, please try again in\
                      \ a minute"
                  status:
                    example: 429
components:
  schemas:
    results:
      properties:
        maxResults:
          description: Maximum number of results items returned.
          example: 5
        query:
          description: Search text query used for this response.
          example: 42 avenue Champs Elysees Paris
        results:
          items:
            $ref: '#/components/schemas/entity'
        resultsCount:
          description: Number of items results found.
          example: 2
  responses:
    "200":
      description: Returns a list of matching addresses
      headers:
        RateLimit-Limit:
          description: Request limit per minute.
          style: simple
          explode: false
          schema: {}
          example: 5
        RateLimit-Remaining:
          description: The number of requests left for the time window.
          style: simple
          explode: false
          schema: {}
          example: 2
        RateLimit-Reset:
          description: Indicates how many seconds are left to wait before making a
            follow-up request.
          style: simple
          explode: false
          schema: {}
          example: 34
        Retry-After:
          description: Indicates how many seconds to wait before making a follow-up
            request.
          style: simple
          explode: false
          schema: {}
          example: 60
      content:
        application/json:
          schema:
            properties:
              maxResults:
                description: Maximum number of results items returned.
                example: 5
              query:
                description: Search text query used for this response.
                example: 42 avenue Champs Elysees Paris
              results:
                items:
                  $ref: '#/components/schemas/entity'
              resultsCount:
                description: Number of items results found.
                example: 2
    "401":
      description: Access denied authentication failed
      content:
        application/json:
          schema:
            properties:
              message:
                example: Access denied authentication failed
    "403":
      description: You are not authorized to access this resource
      content:
        application/json:
          schema:
            properties:
              message:
                example: You are not authorized to access this resource
    "404":
      description: Route not found
      content:
        application/json:
          schema:
            properties:
              message:
                example: Route not found
    "412":
      description: Access denied missing credentials
      content:
        application/json:
          schema:
            properties:
              message:
                example: Access denied missing credentials
    "422":
      description: Invalid body parameters
      content:
        application/json:
          schema:
            properties:
              errors:
                example:
                - location: body
                  msg: Must be an integer between 1 and 20 included.
                  param: maxResults
                  value: 42
                - location: body
                  msg: This country is not supported. Contact us if you need it.
                  param: "countries[0]"
                  value: sx
                items:
                  $ref: '#/components/schemas/validationError'
              message:
                example: "Invalid body parameters. Check the API documentation: https://api.placekit.io/"
    "429":
      description: "Too many requests from this IP, please try again in a minute"
      content:
        application/json:
          schema:
            properties:
              message:
                example: "Too many requests from this IP, please try again in a minute"
              status:
                example: 429

The $refs: '#/components/schemas/validationError' and '#/components/schemas/entity' are not in the sample file.

gracekarina commented 6 months ago

closing this issue as for the resolution is to use the resolveFully option set to true for OpenApi 3.1