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

Recursive rendering needs work #3325

Open
julienkosinski opened this issue Jul 2, 2017 · 56 comments
Open

Recursive rendering needs work #3325

julienkosinski opened this issue Jul 2, 2017 · 56 comments
Assignees

Comments

@julienkosinski
Copy link

julienkosinski commented Jul 2, 2017

Hello,

I have the following model definitions spec:

    "definitions": {
        "models.Equipment": {
            "title": "Equipment",
            "type": "object",
            "properties": {
                "Features": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.Feature"
                    }
                },
                "Id": {
                    "type": "integer",
                    "format": "int64"
                },
                "IdType": {
                    "type": "string"
                },
                "Name": {
                    "type": "string"
                },
                "Price": {
                    "type": "integer",
                    "format": "int32"
                }
            }
        },
        "models.Feature": {
            "title": "Feature",
            "type": "object",
            "properties": {
                "Equipments": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.Equipment"
                    }
                },
                "Id": {
                    "type": "integer",
                    "format": "int64"
                },
                "IdFeature": {
                    "$ref": "#/definitions/models.Feature"
                },
                "Name": {
                    "type": "string"
                }
            }
        }
    }

In the Feature model, the Equipments property is defined as an array of Equipment models, but Swagger UI 3.x renders it as an empty array []. Everywhere Feature model is used, like as examples for POST method in Feature I have this kind of display.

swagger-ui bug

We think it may be a bug caused by circular references. Thanks to Helen.

Thank you very much!

@julienkosinski julienkosinski changed the title Empty brackets in definitions Object array rendered as empty array Jul 4, 2017
@julienkosinski
Copy link
Author

@shockey @webron I mention you just to make sure this has been noticed, don't be offended :).

@shockey shockey self-assigned this Jul 15, 2017
@shockey
Copy link
Contributor

shockey commented Jul 15, 2017

@julienkosinski thanks, I'll get this triaged on Monday 😄

@shockey
Copy link
Contributor

shockey commented Jul 18, 2017

I'm seeing two things here:

  1. Feature -> Equipments is being passed a $ref to render... this shouldn't be happening.
  2. The nested model is showing a very strange generated name:

image

This isn't quite right - we'll look into it 😄

@KevVerF
Copy link

KevVerF commented Jul 25, 2017

In case it helps to find the issue => This indeed seems to be "Circular references" ... it also seems to appear without array wrapping.

"definitions": {
   "Classification": {
            "title": "Classification",
            "properties": {
                "ID": {
                    "type": "integer",
                    "default": 126132
                },
                "rank": {
                    "type": "string"
                },
                "scientificname": {
                    "type": "string"
                },
                "child": {
                    "$ref": "#/definitions/Classification"
                }
            },
            "xml": {
                "name": "Classification",
                "wrapped": false
            }
        }
}

Results in:
image

In the 2.x versions this was handled as follows:
image

Further the Model example values (JSON and XML) don't handle these nested definitions. The property is just removed from the list. But this could be another issue.

image

@tobymurray
Copy link

Just ran into this as well using the OpenAPI 3 spec:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Circular reference example",
    "version": "0.1"
  },
  "paths": {},
  "components": {
    "schemas": {
      "SelfReferencingSchema": {
        "type": "object",
        "properties": {
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SelfReferencingSchema"
            }
          }
        }
      }
    }
  }
}

Yields the following:
image

I'm not familiar with the history or previous implementations of this or what's possible/hard, but personally I think it'd be nice to show the title for a circular reference but not expand it by default. E.g.:
image

@Rinzwind
Copy link

Rinzwind commented Oct 4, 2017

Here's a screenshot of another example I was testing (screenshot was made using v3.3.1). The structure of the schema object for the “containingFolder” attribute for “Folder” is similar to the one for “resourceFork” for “File”; I’m guessing the reason that it’s not shown that “containingFolder” is another “Folder” is due to the circularity.

swagger_ui

Here’s the OpenAPI object:

{
  "openapi": "3.0.0",
  "info": {
    "title": "Test",
    "version": "0.1"
  },
  "components": {
    "schemas": {
      "Fork": {
        "type": "object",
        "properties": {
          "data": {
            "type": "string",
            "format": "base64"
          }
        }
      },
      "File": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "dataFork": {
            "$ref": "#/components/schemas/Fork"
          },
          "resourceFork": {
            "anyOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Fork"
              }
            ]
          },
          "containingFolder": {
            "$ref": "#/components/schemas/Folder"
          }
        }
      },
      "Folder": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "containingFolder": {
            "anyOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Folder"
              }
            ]
          },
          "containedItems": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "$ref": "#/components/schemas/File"
                },
                {
                  "$ref": "#/components/schemas/Folder"
                }
              ]
            }
          }
        }
      }
    }
  }
}

@hkosova
Copy link
Contributor

hkosova commented Oct 4, 2017

@Rinzwind

          "resourceFork": {
            "anyOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/Fork"
              }
            ]

"type": "null" is not valid in OpenAPI, because there's no null type - that's one of the differences from JSON Schema. OpenAPI uses the nullable attribute instead.

I'm not sure if there's a way to combine $ref with nullable. You'll probably need to add nullable: true directly to the referenced schemas (Fork and Folder).

@Rinzwind
Copy link

Rinzwind commented Oct 4, 2017

"type": "null" is not valid in OpenAPI, because there's no null type - that's one of the differences from JSON Schema. OpenAPI uses the nullable attribute instead.

@hkosova, I had missed that, thanks for pointing it out!

I'm not sure if there's a way to combine $ref with nullable. You'll probably need to add nullable: true directly to the referenced schemas (Fork and Folder).

I’m not sure that adding nullable directly to the referenced schemas is going to match with what I was trying to express: for the references to Fork from File, only the value for “resourceFork” can be null, the value for “dataFork” can not be null. I guess I could still express this as follows, which as far as I can tell is at least in accordance with the OpenAPI specification? But using an “anyOf” with just one contained schema seems clumsy, is there a better way to handle this?

... 
"File": {
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "dataFork": {
      "$ref": "#/components/schemas/Fork"
    },
    "resourceFork": {
      "nullable": true,
      "anyOf": [
        {
          "$ref": "#/components/schemas/Fork"
        }
      ]
    },
    ...

@webron
Copy link
Contributor

webron commented Oct 4, 2017

@Rinzwind can't think of a different way to express it.

@Rinzwind
Copy link

Rinzwind commented Oct 5, 2017

@Rinzwind can't think of a different way to express it.

Thanks for the feedback. I’ve posted an issue for it at the OpenAPI Specification repository.

@jonschoning
Copy link

jonschoning commented Oct 25, 2017

Could you also use "allOf" . ? If so, would there be a difference between using the `anyOf`` method?

... 
"File": {
  "type": "object",
  "properties": {
    "name": {
      "type": "string"
    },
    "dataFork": {
      "$ref": "#/components/schemas/Fork"
    },
    "resourceFork": {
      "allOf": [
        {
          "$ref": "#/components/schemas/Fork"
        },
        {
          "nullable": true
        }
      ]
    },
    ...

@webron
Copy link
Contributor

webron commented Oct 25, 2017

allOf would actually be better than anyOf in this case.

@Rinzwind
Copy link

@webron: I am not sure whether the example given by @jonschoning has the intended semantics.

The section on “allOf” in the JSON Schema Validation proposal says: “An instance validates successfully against this keyword if it validates successfully against all schemas defined by this keyword's value.”

As far as I understand, null validates successfully against the schema { "nullable": true }. But null does not validate successfully against the schema { "$ref": "#/components/schemas/Fork" }. Therefore, I take it null also does not validate successfully against this schema:

{
  "allOf": [
    {
      "$ref": "#/components/schemas/Fork"
    },
    {
      "nullable": true
    } ] }

In other words, in @jonschoning's example, null would not be a valid value for “resourceFork”. Or did I misunderstand how schemas work in OpenAPI?

@jonschoning
Copy link

jonschoning commented Oct 26, 2017

@Rinzwind at the same time that allOf is interpreted the ref is dereferenced, so the net effect results in the nullable being a part of the refs definition. You can check this understanding by considering how allOf works with appending properties to a ref with allOf

@webron
Copy link
Contributor

webron commented Oct 26, 2017

Actually, @Rinzwind's point is correct, and that won't work. However, using anyOf also means that any type can be used because of nullable only schema.

@jonschoning
Copy link

jonschoning commented Oct 26, 2017

@webron
Copy link
Contributor

webron commented Oct 26, 2017

If you find that an example is missing, feel free to open an issue on that repo. Not really sure what's not clear about it though.

@Rinzwind
Copy link

Rinzwind commented Oct 26, 2017

@jonschoning As far as I understand, the following is meant by “Allows sending a null value for the defined schema. Default value is false.”: “When true, allows sending any value; when not present or false, allows sending any value except null.”

Rewording the definition according to the terminology employed in JSON Schema Validation (using in particular the wording for “uniqueItems” as inspiration):

nullable

The value of this keyword MUST be a boolean.

If this keyword has boolean value true, any instance validates successfully. If this keyword has boolean value false, any instance validates successfully unless it is null.

If not present, this keyword MUST be considered present with boolean value false.

I hope that helps. Keep in mind though that this is not a direct quote from the specification, but just my understanding of it.

Edit: I'm not quite sure whether my rewording in JSON Schema Validation terms is correct. It seems it would not imply that null validates successfully against {"type":"integer", "nullable": "true" } as intended. I'm not sure how to fix this. I'm inclined towards extending “linearity” with an additional exception that states that if the instance is null, and the keyword “nullable” is present with value true, then the instance validates successfully regardless of other keywords. But I’m not sure at this point whether that is correct.

@jonschoning
Copy link

Ok, I understand how, thank you.

It seems one cannot simply specify a ref should be nullable then with anyOf or allOf without also changing what validates per Ron's comment

@Rinzwind
Copy link

It seems one cannot simply specify a ref should be nullable then with anyOf or allOf without also changing what validates per Ron's comment

Indeed, as far as I understand, a schema like {"anyOf":[...,{"nullable":"true"}]} would allow null, but also any other value. The schema {"nullable":"true"} in OpenAPI is not equivalent to the schema {"type":"null"} in (pure) JSON Schema. I find the latter easier to understand, I'm not sure why OpenAPI introduced the former. I think I intuitively grasp its meaning, but as mentioned above, I'm having some trouble giving an exact definition for it. I agree with your earlier comment that the OpenAPI specification isn't very clear about the definition of “nullable”.

@webron webron changed the title Object array rendered as empty array Recursive rendering needs work Jul 5, 2018
@yanalapraneetha
Copy link

Hi,
I am facing the same issue with circular reference in my spec

api spec

openapi: 3.0.1 info: title: API version: 0.1.0 paths: /v1/message/{context}: get: tags: - cpsapi description: get pulse messages given the context in isolationId/modelId operationId: getPulseMessages parameters: - name: context in: path description: context id of message required: true schema: type: string responses: "200": description: successful operation content: application/json: schema: type: array items: $ref: '#/components/schemas/Message' "401": description: Not authenticated "500": description: Internal server error components: schemas: Message: type: object properties: ID: type: string description: unique id for the message message: type: string description: the actual pulse message replies: type: array description: Get replies items: $ref: '#/components/schemas/Message'

Reply property in above spec is shown in swagger ui like -
"replies": [
"string"
]

message.txt

Any update on this issue?

@vanDarg
Copy link

vanDarg commented May 25, 2021

I created a repository which shows the same problem in a specification generated from a NestJS application: https://codesandbox.io/s/nestjs-swagger-description-bug-dfesh?file=/src/app.controller.ts

OpenApi 3.0, no circular references, just recursive references.

Interestingly enough, the rendering problem only occurs within Responses -> Schema; if you look at the Schemas in the Schema summary at the bottom, everything is rendered correctly.
The used generated OpenApi spec document looks like this:

@StenCalDabran Interesting detail about your example: I just took this to the Swagger Editor (https://editor.swagger.io/) to see if it would resemble a similar issue I am having. I noticed the 'components' part was written before the 'paths' part. When I switched the order of those two (parts before components) it would render correctly in both places.

@StenCalDabran
Copy link

@StenCalDabran Interesting detail about your example: I just took this to the Swagger Editor (https://editor.swagger.io/) to see if it would resemble a similar issue I am having. I noticed the 'components' part was written before the 'paths' part. When I switched the order of those two (parts before components) it would render correctly in both places.

@vanDarg Very interesting indeed, thanks for pointing that out. I opened an issue in the nestjs/swagger repository (nestjs/swagger#1369), maybe it is possible to automatically generate the document in the other order in the first place - which of course would only mitigate the actual problem.

StenCalDabran pushed a commit to StenCalDabran/swagger that referenced this issue May 29, 2021
Ensure that the paths property comes before the components property,
as this may lead to several strange display bugs in swagger-ui.
See swagger-api/swagger-ui#5972 or
swagger-api/swagger-ui#3325 (comment)

Closes nestjs#1369
@akozlov75
Copy link

4 years have already gone by, guys/gals any progress with this issue?

@ponelat
Copy link
Member

ponelat commented Nov 16, 2021

Yeah, appears to still be an issue... created small reproduction based on example in first comment: https://gist.github.com/ponelat/e3e7e3e55247ef6d2cd09926bbeb4241

@benjamin-mogensen
Copy link

This is still an issue. I have a quite complex, deep schema, which Swagger UI reports it cannot resolve as soon as clicking on the model either under the endpoint or in the models section. Until I click those the UI looks fine. The $ref I have is recursive (a package can have inner packages AND can have an out package).
image

@feenr
Copy link
Contributor

feenr commented Jan 14, 2022

This issue may have been fixed? I was going to attempt a fix, but checking out the latest swagger-ui it wasn't reproducible.

@StenCalDabran
Copy link

This issue may have been fixed? I was going to attempt a fix, but checking out the latest swagger-ui it wasn't reproducible.

At least the example I built (see my previous reply) still does not work with the swagger editor, which seems to use the latest swagger-ui. If the original issue is indeed resolved, I could open a new issue, but they seem to have similar root problems.

@julienkosinski
Copy link
Author

Hello,
I don't monitor this issue anymore. If needed, I can close it now.

@gwcoffey
Copy link

This is still a problem in 4.5.0. Here's a minimal OAS that reproduces it.

{
  "openapi" : "3.0.1",
  "info" : {
    "title" : "Recursive Failure",
    "version" : "1.0"
  },
  "paths" : {
    "/test" : {
      "get" : {
        "responses" : {
          "200" : {
            "description": "Recursive example",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/test-response"
                }
              }
            }
          }
        }
      }
    }
  },
  "components" : {
    "schemas" : {
      "test-response": {
        "type": "object",
        "properties": {
          "foo": {
            "type": "string"
          },
          "bar": {
            "$ref" : "#/components/schemas/test-response"
          }
        }
      }
    }
  }
}

Here's what I see in swagger ui when I expand the /test path:

Screen Shot 2022-02-16 at 11 35 17 AM

Here's how the example is rendered:

{
  "foo": "string"
}

And here's how the schema is rendered when expanded:

Screen Shot 2022-02-16 at 11 37 21 AM

@geraldus
Copy link

Recursive schemas are still rendered incorrectly as {}. Note that there is no error appears in UI in my case, because generated swagger.json seems to be valid and have proper ref within.

@mristin
Copy link

mristin commented Jul 27, 2022

Hi,
I'd like to have a try at fixing this as it is critical feature for us with many users. Does anybody know whether somebody else is working on this, or what a good entry point for this problem would be?

@RockyMM
Copy link

RockyMM commented Sep 6, 2022

Just to put it outhere, some of the schemas from this bug report do render correctly, namely the initial bug report seems to be solved. Some are still not rendered correctly. I am using https://editor-next.swagger.io/ to test.

@atereques
Copy link

Hello,
I'm facing the same problem with schema rendering (and also with code completition, when creating JSON based on schema in IDE). We have custom query language, where filtering constraints can be nested within each other, see example of appropriate schema below.

openapi: 3.0.1
info:
  title: Web service for catalogue Brand
  version: 1.0.0-oas3
paths:
  /api/rest/catalogs/brands:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Query'
      responses:
        '200':
          description: Query result
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Entity'
components:
  schemas:
    Entity:
      properties:
        primaryKey:
          type: integer
    Query:
      type: object
      properties:
        limit:
          type: integer
        filterBy:
          $ref: '#/components/schemas/FilterContainer'
    FilterContainer:
      type: object
      properties:
        and:
          type: array
          items:
            $ref: '#/components/schemas/FilterContainer'
        not:
          $ref: '#/components/schemas/FilterContainer'
        attribute_status_lessThan:
          type: string
        attribute_status_equals:
          type: string

@Hronom
Copy link

Hronom commented Dec 16, 2022

Hello, I'm facing similar issues like described here #3325 (comment) is there solution for it?

@dtc-lan1wang
Copy link

still no solutions here?

@bergwerf
Copy link

This is still unsolved.

@Hronom
Copy link

Hronom commented May 31, 2023

+1 Same story and it's strange situation, when we use swagger-ui it's throw this error... BUT... when we use same swagger definition with this https://springdoc.org/ no error...

Please fix this, since it's create unnecessary questions/confusion for developers that use it.

I hope maintainers of this project very deep in context and able to find reasons for it as same as fix

@benjamin-mogensen
Copy link

In my experience Redoc and RapiDoc does not have this issue and also works with OAS 3.1 which swagger ui does not. So we use those instead.

@mbarmettler
Copy link

+1 we need to render inheritance structure like this:

public class TimelineHolidayQueryModel : TimeLineQuery
public class TimeLineQuery: PagedQuery
public class PagedQuery

after second inheritance, codegen to get Typescript interfaces, stops rendering

@s3tezsky
Copy link

s3tezsky commented Nov 6, 2024

Hi, I have run into the same issue (thanks to different lib using swagger-ui).

After some investigation I found out this issue was resolved in v5.11.8.

DjordyKoert pushed a commit to nelmio/NelmioApiDocBundle that referenced this issue Nov 7, 2024
## Description

This PR updates the
[swagger-ui](https://github.com/swagger-api/swagger-ui) to latest
version (5.18.1).

Provided version of swagger-ui cannot render recursive objects properly
(they are rendered as empty objects). This bug was resolved in v5.11.8
(more context can be found here:
swagger-api/swagger-ui#3325).

I have tested this update in our projects and it seems to be OK 🤞.

Cheers!

## What type of PR is this? (check all applicable)
- [x] Bug Fix
- [ ] Feature
- [ ] Refactor
- [ ] Deprecation
- [ ] Breaking Change
- [ ] Documentation Update
- [ ] CI

## Checklist
- [ ] I have made corresponding changes to the documentation (`docs/`)
- [ ] I have made corresponding changes to the changelog
(`CHANGELOG.md`)
DjordyKoert pushed a commit to nelmio/NelmioApiDocBundle that referenced this issue Nov 7, 2024
## Description

This PR updates the
[swagger-ui](https://github.com/swagger-api/swagger-ui) to latest
version (5.18.1).

Provided version of swagger-ui cannot render recursive objects properly
(they are rendered as empty objects). This bug was resolved in v5.11.8
(more context can be found here:
swagger-api/swagger-ui#3325).

I have tested this update in our projects and it seems to be OK 🤞.

Cheers!

## What type of PR is this? (check all applicable)
- [x] Bug Fix
- [ ] Feature
- [ ] Refactor
- [ ] Deprecation
- [ ] Breaking Change
- [ ] Documentation Update
- [ ] CI

## Checklist
- [ ] I have made corresponding changes to the documentation (`docs/`)
- [ ] I have made corresponding changes to the changelog
(`CHANGELOG.md`)

(cherry picked from commit b79abc1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests