swagger-api / swagger-ui

Swagger UI is a collection of HTML, JavaScript, and CSS assets that dynamically generate beautiful documentation from a Swagger-compliant API.
https://swagger.io
Apache License 2.0
26.48k stars 8.96k forks source link

$ref pointing at the same schema causes a resolver error #5762

Open gavmck opened 4 years ago

gavmck commented 4 years ago

Q&A

Content & configuration

I have a $ref that is a causing a resolver error (#/components/schemas/PartsOrderLine/properties/listPrice/allOf/0). The main difference I can see to other working refs is that it is pointing at the same schema, although it does not throw an error if I omit /allof/0.

I have removed as much of the spec as possible, whilst retaining the error in order to simplify the problem.

For now I have ended up manually copying the information from the $ref instead of using a $ref, but that is not ideal.

Example Swagger/OpenAPI definition:

{
  "openapi": "3.0.0",
  "paths": {
    "/parts-orders": {
      "post": {
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartsOrder"
                }
              }
            }
          }
        }
      }
    },
    "/parts-orders/{partsOrderId}": {
      "get": {
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PartsOrder"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "PartsOrder": {
        "type": "object",
        "properties": {
          "parts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PartsOrderLine"
            }
          }
        }
      },
      "PartsOrderLine": {
        "type": "object",
        "properties": {
          "listPrice": {
            "allOf": [
              {
                "type": "string"
              }
            ]
          },
          "orderPrice": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PartsOrderLine/properties/listPrice/allOf/0"
              }
            ]
          }
        }
      }
    }
  }
}

Swagger-UI configuration options:

import spec from '../../specs/parts-orders.json';
<SwaggerUI spec={spec} />

To reproduce...

Steps to reproduce the behavior:

  1. Load this up in Create React App
  2. See the resolver error

Expected behavior

The $ref resolves correctly.

webron commented 4 years ago

I've tried loading the following in editor.swagger.io:

openapi: 3.0.0
info:
  title: hello
  version: 1.1.1
paths:
  /parts-orders:
    post:
      responses:
        '201':
          description: hello
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartsOrder'
components:
  schemas:
    PartsOrder:
      type: object
      properties:
        parts:
          type: array
          items:
            $ref: '#/components/schemas/PartsOrderLine'
    PartsOrderLine:
      type: object
      properties:
        listPrice:
          allOf:
            - type: string
        orderPrice:
          allOf:
            - $ref: '#/components/schemas/PartsOrderLine/properties/listPrice/allOf/0'

It's a slightly modified version that removes some syntax errors. The current online editor is based on Swagger UI 3.24.3, so the rendering behavior is the same.

When using the definition above, it seems to be rendering it correctly with no errors. This does not mean an issue doesn't exist though. Can you try reproducing it using that method as well?

gavmck commented 4 years ago

Hey @webron thanks for taking a look into this! Sorry for the slow reply, Christmas got in my way...

I've tried out your example spec with both swagger-ui-react and the editor.swagger.io and it works correctly in both.

When I add the second path is where it starts failing in swagger-ui-react, although this works correctly in editor.swagger.io.

I've put together a reproduction on codesandbox: https://codesandbox.io/s/zealous-lederberg-czmn1?fontsize=14&hidenavigation=1&theme=dark

Whilst putting this together I noticed you have to have docExpansion="full" to trigger the error, which is odd.

Example spec:

openapi: 3.0.0
info:
  title: hello
  version: 1.1.1
paths:
  /parts-orders:
    post:
      responses:
        '201':
          description: hello
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartsOrder'
  '/parts-orders/{partsOrderId}':
    get:
      parameters:
        - in: path
          name: partsOrderId
          description: The id
          required: true
          schema:
            type: string
      responses:
        '200':
          description: hello
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PartsOrder'
components:
  schemas:
    PartsOrder:
      type: object
      properties:
        parts:
          type: array
          items:
            $ref: '#/components/schemas/PartsOrderLine'
    PartsOrderLine:
      type: object
      properties:
        listPrice:
          allOf:
            - type: string
        orderPrice:
          allOf:
            - $ref: '#/components/schemas/PartsOrderLine/properties/listPrice/allOf/0'
webron commented 4 years ago

Huh, interesting. This might not be as odd as you might think though. It could be a bug related to the $ref resolution that when not automatically expanded is circumvented by the lazy loading. Unfortunately, there's nobody around right now to dive in to the issue.

I assume the provided example is just to reproduce it and not the actual sample (because if it is, you and I need to have a serious talk). Is there any way you can provide me with more concrete details of what the actual example/use case is? I may be able to offer an alternative solution (fwiw, those kind of refs are very no-standard, yet valid).

gavmck commented 4 years ago

Hey @webron thanks for the reply :)

The provided example is a very minimalist reproduction as I wanted to isolate the issue as much as possible to remove distractions.

I'm not too familiar with Swagger specs (it's not my spec/api), I'm just doing from front-end work on displaying a customised Swagger UI.

I presume the use case is that the spec author has seen duplicate content in the spec and decided to use refs to make management of it more efficient. It's not a huge amount of extra code, so for the moment I've just duplicated it manually rather than using refs and asked them to update the spec.

Could you clarify what you mean by "those kind of refs"? Is it the part of the spec they are referencing? Just for interest, what would the serious talk be about? (in case I've missed something awfully glaring)

webron commented 4 years ago

Of course. The reason why components exist in the spec is to enable (and encourage) reusability. A reference like $ref: '#/components/schemas/PartsOrderLine/properties/listPrice/allOf/0' suggests to me that the content of #/components/schemas/PartsOrderLine/properties/listPrice/allOf/0 should have been set it its own schema under components and referenced from both locations.

Referencing objects directly under their components section makes sense, but digging in deeper with a ref is something I'd consider a... code smell.

gavmck commented 4 years ago

Thanks, that's very helpful!

I have a call with the spec maintainers tomorrow so I'll raise that as an issue.