Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions packages/core/src/getters/discriminators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,106 @@ describe('resolveDiscriminators getter', () => {
expect(allOf?.[0]).toHaveProperty('$ref', '#/components/schemas/Base');
});

it('rewrites variant allOf $ref to parent when parent has oneOf but no discriminator mapping', () => {
const schemas: OpenApiSchemasObject = {
Animal: {
type: 'object',
required: ['type'],
properties: {
type: { type: 'string' },
},
discriminator: { propertyName: 'type' },
oneOf: [
{ $ref: '#/components/schemas/Dog' },
{ $ref: '#/components/schemas/Cat' },
],
},
Dog: {
allOf: [
{ $ref: '#/components/schemas/Animal' },
{
type: 'object',
properties: { bark: { type: 'string' } },
},
],
},
Cat: {
allOf: [
{ $ref: '#/components/schemas/Animal' },
{
type: 'object',
properties: { meow: { type: 'string' } },
},
],
},
};
const result = resolveDiscriminators(structuredClone(schemas), context);
const dog = result.Dog as NonNullable<OpenApiSchemasObject[string]>;
const cat = result.Cat as NonNullable<OpenApiSchemasObject[string]>;

const dogAllOf = dog.allOf as
| (OpenApiSchemaObject | OpenApiReferenceObject)[]
| undefined;
expect(dogAllOf).toHaveLength(1);
expect(dogAllOf?.[0]).not.toHaveProperty('$ref');

const catAllOf = cat.allOf as
| (OpenApiSchemaObject | OpenApiReferenceObject)[]
| undefined;
expect(catAllOf).toHaveLength(1);
expect(catAllOf?.[0]).not.toHaveProperty('$ref');
});

it('rewrites variant allOf $ref to parent when parent has anyOf but no discriminator mapping', () => {
const schemas: OpenApiSchemasObject = {
Animal: {
type: 'object',
required: ['type'],
properties: {
type: { type: 'string' },
},
discriminator: { propertyName: 'type' },
anyOf: [
{ $ref: '#/components/schemas/Dog' },
{ $ref: '#/components/schemas/Cat' },
],
},
Dog: {
allOf: [
{ $ref: '#/components/schemas/Animal' },
{
type: 'object',
properties: { bark: { type: 'string' } },
},
],
},
Cat: {
allOf: [
{ $ref: '#/components/schemas/Animal' },
{
type: 'object',
properties: { meow: { type: 'string' } },
},
],
},
};
const result = resolveDiscriminators(structuredClone(schemas), context);
const dog = result.Dog as NonNullable<OpenApiSchemasObject[string]>;
const cat = result.Cat as NonNullable<OpenApiSchemasObject[string]>;

const dogAllOf = dog.allOf as
| (OpenApiSchemaObject | OpenApiReferenceObject)[]
| undefined;
expect(dogAllOf).toHaveLength(1);
expect(dogAllOf?.[0]).not.toHaveProperty('$ref');

const catAllOf = cat.allOf as
| (OpenApiSchemaObject | OpenApiReferenceObject)[]
| undefined;
expect(catAllOf).toHaveLength(1);
expect(catAllOf?.[0]).not.toHaveProperty('$ref');
});

it('hoists oneOf from discriminator when nested incorrectly', () => {
const schemas: OpenApiSchemasObject = {
Animal: {
Expand Down
15 changes: 12 additions & 3 deletions packages/core/src/getters/discriminators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,22 @@ export function resolveDiscriminators(
if (isBoolean(parentSchema)) {
continue;
}
if (!parentSchema.oneOf || !parentSchema.discriminator?.mapping) {
const variants = parentSchema.oneOf ?? parentSchema.anyOf;
if (!variants || !parentSchema.discriminator) {
continue;
}
const { mapping, propertyName } = parentSchema.discriminator;
const { propertyName, mapping } = parentSchema.discriminator;
if (!propertyName) {
continue;
}
const mappedRefs = mapping ? Object.values(mapping) : [];
const variantArrayRefs = variants
.filter(
(item): item is OpenApiReferenceObject & { $ref: string } =>
isReference(item) && typeof item.$ref === 'string',
)
.map((item) => item.$ref);
const variantRefs = [...new Set([...mappedRefs, ...variantArrayRefs])];

const parentProperties = parentSchema.properties as
| Record<string, OpenApiSchemaObject | OpenApiReferenceObject>
Expand All @@ -142,7 +151,7 @@ export function resolveDiscriminators(
);
const hasInheritableProps = Object.keys(inheritableProps).length > 0;

for (const mappingValue of Object.values(mapping)) {
for (const mappingValue of variantRefs) {
let variantSchema;
try {
const { originalName } = getRefInfo(mappingValue, context);
Expand Down
Loading