prism: Circular $ref pointer not supported
Describe the bug
A clear and concise description of what the bug is. Pointing prism to a file that contains components that use recursive field definition, Prism fails with the message:
Circular $ref pointer found at /tmp/test.yaml#/components/schemas/Person/properties/spouse
To Reproduce
A minimal API definition triggering the error message:
info:
title: "Circular reference example"
description: "Exemplifies a schema with circular references"
version: 0.1.0
paths:
/directory:
get:
responses:
"200":
description: "The request was successfully processed"
content:
application/json:
schema:
$ref: "#/components/schemas/Person"
example:
name: John
spouse:
name: Cynthia
components:
schemas:
Person:
nullable: true
properties:
name:
type: string
nullable: false
spouse:
$ref: "#/components/schemas/Person"
Expected behavior
As the OAS file specifies a valid API, the server should be able to mock that API.
While having a circular reference can theoretically lead to a infinite recursion, this can be in practice avoided by having a nullable reference to the child object.
Environment
- Prism version: [4.1.0]
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 6
- Comments: 27 (13 by maintainers)
Commits related to this issue
- fix: handle slashes better when decycling - needed by https://github.com/stoplightio/prism/issues/1456 — committed to stoplightio/json by pytlesk4 3 years ago
- fix: handle slashes better when decycling (#90) - needed by https://github.com/stoplightio/prism/issues/1456 — committed to stoplightio/json by pytlesk4 3 years ago
Hey Everyone,
Finally got some time to work on circular ref support in prism, if you update prism to the latest, circular refs should be supported. Please create a new issue if something is not working as expected!
Hi,
I just faced the same issue today. I can understand that dynamically mocking and serializing a circular reference causes an issue. In my case though, the open api file provides a response example and prism would never have to do such a thing.
Nonetheless, just describing the circular reference in the OpenApi specification file prevent prism from booting 😕 Is there any way to work around this ?
3 cents from me.
Maybe if you have circular references and it’s hard(or maybe even impossible) to generate response but you have an example - then stick with it - otherwise - fail?
Sample schema:
Generated example by swagger editor:
Actual frustrating picture that I get with current version of prism:
P.S. there’s a difference between living in an ideal world(building ideal API) vs adopting. P.P.S. actually prism 3.x was doing this trick with examples. I don’t know why this functionality was removed 😦
BR, Dionis
Another $0.02 to ask for prioritizing support for stopping at non-required recursive fields or null.
We’re building OData APIs where this is incredibly commonplace, because the schema is full of non-required fields that allow you to explicitly use a $expand query parameter to add a navigation across a link relationship (which doesn’t implicitly cause the reciprocal link to get $expanded, so no recursion in the response body in practice).
However the schema needs to include all the possible combinations that could be $expanded, thus frequently producing recursion.
I’m blocked from any chance of adopting Prism without this support.
@XVincentX I don’t think @andrewsomething was chasing for an update, just asking for insight into how circular references work in the validation proxy.
@andrewsomething Hello! Validation and response generation are two very different concepts. Trying to create JSON based on schemas which have self-referencing relationships and knowing when to stop trying to create more JSON in that infinite loop is the main problem discussed in this thread. Looking at an instance of JSON and figuring out if it’s valid against the schema is waaaaaaay easier.
I’d recommend giving it a try and see if you have any problems. If you do, let us know. If not, enjoy!
Hey @dargiri,
I’ve explained why the functionality was disabled in https://github.com/stoplightio/prism/issues/1456#issuecomment-702176386
TL;DR: it’s not easy to know where we can handle circular references successfully and where not.
Rather than trying to do something according to common sense, ship something for the sake of and then get tons of issues with edge cases or one scenario making another one not work correctly — I decided it was a better idea to admit we do not have a story flashed out yet and, as long we don’t have it, I am not going to let it happen.
If this takes the priority, more than happy to work seriously on this.
@philsturgeon Wrong mention here. I’m not the author of the spouse example 😃
@linkdd could you narrow it down a bit? Haha can’t go digging through all that! 😃
@blemasle I don’t understand this JSON:
This looks like John is married to Cynthia but Cynthia is not married to John.
Poor John.
This doesn’t make any sense fundamentally so it’s going to lead to confusing conversations trying to use it as an example.
If you wanted a
nullin there, then sure your OpenAPI should be this:Supporting this in Prism would just be a case of picking oneOf’s at random, which I remember discussing in the past. Some people want oneOfs to always show the first, which is where you’d get into trouble, because again the chain would never be broken by a null.
Possibly a more logical example would be parent.
This is another chain which is not infinite but could be. It makes me wonder why people design APIs like this because clients should be able to follow a link if they want that information instead of having recursion thrust upon them.
If you’re using Prism you’re probably designing an API right now, and if you’re doing that its not too late to change the design. Use any Hypermedia Format and insert links, which might look as simple as this:
Prism is happy. Your API is happy. Your clients are happy.
If anyone has any other examples of circular references which should exist, lmk, and in the men time @XVincentX can we look into that oneOf shortcut with null? We can’t do anything about any of these other situations so far but we can t least handle that.
@XVincentX thanks for your reply 😃
If I can add: in the provided example, in practice would become Person (A) -> Spouse -> Person (B) -> Spouse -> Person (A) (can be a copy object, not memory reference to the previously defined one) - maybe there could be an option to resolve an arbitrary number of recursive levels and then give up (with the difference that this number could be overridden in the config)?
After all, the contract doesn’t specify how deep the circular reference needs to be (therefore one level would always be enough to match the contract?).