Skip to content

Commit f0705fe

Browse files
authored
fix: resolve discriminator defaultMapping into components schemas when bundling (#2736)
1 parent dd741d2 commit f0705fe

9 files changed

Lines changed: 139 additions & 12 deletions

File tree

.changeset/seven-emus-grab.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@redocly/openapi-core": patch
3+
"@redocly/cli": patch
4+
---
5+
6+
Fixed an issue where the discriminator's `defaultMapping` property was not resolved when bundling.

packages/core/src/__tests__/bundle.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,38 @@ describe('bundle', () => {
555555
expect(problems).toHaveLength(0);
556556
expect(res.parsed).toMatchSnapshot();
557557
});
558+
559+
it('should bundle discriminator with defaultMapping and mapping as component names to the same document', async () => {
560+
const document = outdent`
561+
openapi: 3.2.0
562+
components:
563+
schemas:
564+
Pet:
565+
type: object
566+
discriminator:
567+
propertyName: kind
568+
defaultMapping: Cat
569+
mapping:
570+
cat: Cat
571+
Cat:
572+
type: object
573+
properties:
574+
kind:
575+
type: string
576+
577+
`;
578+
579+
const {
580+
bundle: { parsed },
581+
problems,
582+
} = await bundleFromString({
583+
source: document,
584+
config: await createConfig({}),
585+
});
586+
587+
expect(problems).toMatchInlineSnapshot(`[]`);
588+
expect(parsed).toMatchInlineSnapshot(document);
589+
});
558590
});
559591

560592
describe('bundleFromString', () => {

packages/core/src/bundle/bundle-visitor.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import {
88
pointerBaseName,
99
refBaseName,
1010
type Location,
11+
isMappingRef,
1112
} from '../ref-utils.js';
1213
import { type ResolvedRefMap, type Document } from '../resolve.js';
1314
import { reportUnresolvedRef } from '../rules/common/no-unresolved-refs.js';
14-
import { type OasRef } from '../typings/openapi.js';
15+
import { type OasRef, type Oas3Discriminator } from '../typings/openapi.js';
1516
import { dequal } from '../utils/dequal.js';
1617
import { makeRefId } from '../utils/make-ref-id.js';
1718
import { type Oas3Visitor, type Oas2Visitor } from '../visitors.js';
@@ -205,19 +206,40 @@ export function makeBundleVisitor({
205206
};
206207

207208
if (version === 'oas3') {
208-
visitor.DiscriminatorMapping = {
209-
leave(mapping: Record<string, string>, ctx: UserContext) {
210-
for (const name of Object.keys(mapping)) {
211-
const $ref = mapping[name];
212-
const resolved = ctx.resolve({ $ref });
213-
if (!resolved.location || resolved.node === undefined) {
214-
reportUnresolvedRef(resolved, ctx.report, ctx.location.child(name));
215-
return;
216-
}
209+
const componentType = mapTypeToComponent('Schema', version)!;
210+
visitor.Discriminator = {
211+
leave(discriminator: Oas3Discriminator, ctx: UserContext) {
212+
if (
213+
typeof discriminator.defaultMapping !== 'string' ||
214+
!isMappingRef(discriminator.defaultMapping)
215+
) {
216+
return;
217+
}
217218

218-
const componentType = mapTypeToComponent('Schema', version)!;
219-
mapping[name] = saveComponent(componentType, resolved, ctx);
219+
const resolved = ctx.resolve({ $ref: discriminator.defaultMapping });
220+
if (!resolved.location || resolved.node === undefined) {
221+
reportUnresolvedRef(resolved, ctx.report, ctx.location.child('defaultMapping'));
222+
return;
220223
}
224+
225+
discriminator.defaultMapping = saveComponent(componentType, resolved, ctx);
226+
},
227+
DiscriminatorMapping: {
228+
leave(mapping, ctx) {
229+
for (const name of Object.keys(mapping)) {
230+
const $ref = mapping[name];
231+
if (!isMappingRef($ref)) {
232+
continue;
233+
}
234+
const resolved = ctx.resolve({ $ref });
235+
if (!resolved.location || resolved.node === undefined) {
236+
reportUnresolvedRef(resolved, ctx.report, ctx.location.child(name));
237+
return;
238+
}
239+
240+
mapping[name] = saveComponent(componentType, resolved, ctx);
241+
}
242+
},
221243
},
222244
};
223245
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
allOf:
2+
- $ref: ./Used.yaml
3+
- type: object
4+
properties:
5+
test:
6+
type: string
7+
const: foo
8+
required:
9+
- test
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
type: object
2+
properties:
3+
base:
4+
type: string
5+
discriminator:
6+
propertyName: test
7+
defaultMapping: ./Foo.yaml
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
openapi: 3.2.0
2+
info: {}
3+
servers: []
4+
paths:
5+
/:
6+
$ref: paths/_.yaml
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
get:
2+
responses:
3+
'200':
4+
content:
5+
application/json:
6+
schema:
7+
$ref: ../components/schemas/Used.yaml
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
apis:
2+
main:
3+
root: ./openapi.yaml
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
openapi: 3.2.0
2+
info: {}
3+
servers: []
4+
paths:
5+
/:
6+
get:
7+
responses:
8+
'200':
9+
content:
10+
application/json:
11+
schema:
12+
$ref: '#/components/schemas/Used'
13+
components:
14+
schemas:
15+
Used:
16+
type: object
17+
properties:
18+
base:
19+
type: string
20+
discriminator:
21+
propertyName: test
22+
defaultMapping: '#/components/schemas/Foo'
23+
Foo:
24+
allOf:
25+
- $ref: '#/components/schemas/Used'
26+
- type: object
27+
properties:
28+
test:
29+
type: string
30+
const: foo
31+
required:
32+
- test
33+
34+
bundling openapi.yaml using configuration for api 'main'...
35+
📦 Created a bundle for openapi.yaml at stdout <test>ms.

0 commit comments

Comments
 (0)