redoc
redoc copied to clipboard
Discriminator / OneOf broken when using external file reference
The discriminator functionnality is broken when using with external files. Here's a reproducible test case :
openapi: 3.0.0
info:
title: test
version: 1.0.0
paths:
/test:
get:
responses:
'200':
description: test
content:
'application/json':
schema:
oneOf:
- $ref: './external-file.yaml#/components/schemas/simpleObject'
- $ref: './external-file.yaml#/components/schemas/complexObject'
discriminator:
propertyName: objectType
## external-file.yaml :
openapi: 3.0.0
components:
schemas:
simpleObject:
type: object
required:
- objectType
properties:
objectType:
type: string
complexObject:
type: object
required:
- objectType
properties:
objectType:
type: string
And when we try to see the responses object in ReDoc, we have the following error :
I tried to debug it quickly, and I figured out that in this case, the oneOf variable is empty : https://github.com/Rebilly/ReDoc/blob/master/src/components/Schema/Schema.tsx#L43
Another related issue (I think) : if we remove the discriminator part, it will work but the schemas names are not displayed correctly (object instead of simpleObject for example) :
Please tell me if you need more informations to fix it, thanks !
I'm seeing a similar issue to the second when using external references for discriminator mappings in schemas.
Any update about this issue ? It's really a blocker for us.
At least, can you please tell me if we are doing something wrong or if it's a real issue ?
While not a solution, this is result of few hours digging, hopefully it will help other people to code solutions.
- for schema with
oneOfanddiscriminator, code here expecting objects in oneOf array has property$refto resolve schema, but somehow (by JsonSchemaRefParser i guess) objects in oneOf array are resolved model objects. - for discriminator with
mapping, code here seems expecting a JsonSchemaRefParser bundled reference. But mappings do not come with property name$refso the JsonSchemaRefParser won't process it, therefore theparser.byRefreceive an invalid ref.
I scoured the internets for weeks with this same issue and found a "workaround". I greatly prefer API specs that make heavy use of external $ref (especially hosted) because of extensibility, modularity, and maintainability. But, as others have mentioned, $ref doesn't play nice with all doc rendering frameworks.
Discriminators are expected to make use of the schemas in components. To accomplish this with a $ref file, you need to add those discriminators via a separate $ref. I show all this below if it's not making sense. This may seem like an unnecessary level of abstraction, but, since unused schemas are just noise, I actually find it more intuitive since you then have full control over what schemas show in the discriminator simply by editing a list.
First, consider a dir structure like:
── raw_spec.yaml
├── my-components/
│  ├── my-requests/
│  │  └── pets_request.yaml
│  ├── my-schemas/
│  │  ├── animals.yaml
│  │  └── discriminators.yaml
└── my-paths/
│  │  └── pets.yaml
The partial spec looks like this:
openapi: 3.0.0
servers:
- url: https://pets.com
info:
version: 0.1.0
title: Pets API
paths:
/pets:
$ref: './my-paths/pets.yaml'
components:
schemas:
$ref: './my-components/my-schemas/discriminators.yaml'
Then, discriminators.yaml, looks like this:
Cat:
$ref: 'animals.yaml#/Cat'
Dog:
$ref: 'animals.yaml#/Dog'
The animals.yaml schemas look like this (note the self referencing for ease of maintenance):
animalSchema:
type: object
description: Top-level animal schema
required:
- petType
properties:
petType:
type: string
description: The type of animal
example: Cat
Cat:
allOf:
- $ref: 'animals.yaml#/animalSchema'
- type: object
properties:
cuddles:
type: boolean
Dog:
allOf:
- $ref: 'animals.yaml#/animalSchema'
- type: object
properties:
smells:
type: boolean
To make use of this bizarre abstraction, let's look at a them in the context of a request schema, for example in a path like /pets as mentioned above:
post:
description: post a pet
requestBody:
$ref: '../my-components/my-requests/pets_request.yaml#/pets_request_schema'
And then the pets_request_schema will look like this:
pets_request_schema:
content:
application/json:
schema:
type: object
description: a request schema
required:
- myPet
properties:
myPet:
type: array
items:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: petType
mapping:
Cattttt: '#/components/schemas/Cat'
Doggg: '#/components/schemas/Dog'
This approach of a class-like, hierarchical structure like in C/C++ can be tedious to think through and create the first time, but it generalizes exceptionally well. But, it is almost impossible to read for a user that wants a raw spec. My suggestion is to run a resolver on your spec, like json-refs, to build a full spec and then give that to ReDoc.
Sorry for the late reply. Just found some time to dig into this.
@chikei is absolutelly correct. Mostly it is caused by the way how internal dereferencer works in ReDoc...
I don't know how to fix it properly in a timely manner. You can workaround this by re-referencing schemas from main openapi file components:
paths:
/test:
get:
responses:
'200':
description: test
content:
'application/json':
schema:
oneOf:
- $ref: '#/components/schemas/simpleObject'
- $ref: '#/components/schemas/complexObject'
discriminator:
propertyName: objectType
components:
schemas:
simpleObject:
$ref: './external-file.yaml#/components/schemas/simpleObject'
complexObject:
$ref: './external-file.yaml#/components/schemas/complexObject'
There is still one issue with discriminator titles that I just fixed (https://github.com/Rebilly/ReDoc/commit/a3d7d7a32c2e95952348c16b5f295cbc1030e697) and which will land in tomorrow mornings release.
Wating fix this bug.
This is what I get without mapping:

And this I get with a mapping - it's completely broken, although there is no error nor warning in the cli:

And here the schema bit:
"getNode": {
"oneOf": [
{
"$ref": "./Group/GroupSchema_v1.json#/definitions/getGroup"
},
{
"$ref": "./Project/ProjectSchema_v1.json#/definitions/getProject"
},
{
"$ref": "./Task/TaskSchema_v1.json#/definitions/getTask"
},
{
"$ref": "./Team/TeamSchema_v1.json#/definitions/getTeam"
},
{
"$ref": "./WorkPackage/WorkPackageSchema_v1.json#/definitions/getWorkPackage"
}
],
"discriminator": {
"propertyName": "nodeType",
"mapping": {
"Group": "./Group/GroupSchema_v1.json#/definitions/getGroup",
"Project": "./Project/ProjectSchema_v1.json#/definitions/getProject",
"Task": "./Task/TaskSchema_v1.json#/definitions/getTask",
"Team": "./Team/TeamSchema_v1.json#/definitions/getTeam",
"Work Package": "./WorkPackage/WorkPackageSchema_v1.json#/definitions/getWorkPackage"
}
}
}
I'm using the latest redoc-cli 0.10.2 with redoc 2.0.0-rc.48
same issue
I was able to work around this by bundling a dist version using https://github.com/Redocly/openapi-cli and just using that:
openapi bundle --output dist/openapi.yaml src/openapi.yaml
Since this command bundles referenced schema into a local #/components/schema instead of rendering them inline like json-refs does, discriminator will work even when used with mappings.
I was able to work around this by bundling a dist version using https://github.com/Redocly/openapi-cli and just using that:
openapi bundle --output dist/openapi.yaml src/openapi.yamlSince this command bundles referenced schema into a local
#/components/schemainstead of rendering them inline like json-refs does,discriminatorwill work even when used withmappings.
Weird how this works, but using redocly doesn't.