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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/openapi3"
---

Fix missing discriminator mapping entry when the first union variant causes a circular emit, affecting both the OpenAPI 3.0 and 3.2 emitters.
9 changes: 8 additions & 1 deletion packages/openapi3/src/schema-emitter-3-2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AssetEmitter,
createAssetEmitter,
ObjectBuilder,
Placeholder,
TypeEmitter,
} from "@typespec/asset-emitter";
import { compilerAssert, DiscriminatedUnion, Type } from "@typespec/compiler";
Expand Down Expand Up @@ -98,7 +99,13 @@ export class OpenAPI32SchemaEmitter extends OpenAPI31SchemaEmitter {
for (const [key, model] of variants.entries()) {
const ref = this.emitter.emitTypeReference(model);
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code");
mapping[key] = (ref.value as any).$ref;
if (ref.value instanceof Placeholder) {
ref.value.onValue((resolvedValue: any) => {
mapping[key] = (resolvedValue as any).$ref;
});
} else {
mapping[key] = (ref.value as any).$ref;
}
}
return mapping;
}
Expand Down
8 changes: 7 additions & 1 deletion packages/openapi3/src/schema-emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,13 @@ export class OpenAPI3SchemaEmitterBase<
for (const [key, model] of variants.entries()) {
const ref = this.emitter.emitTypeReference(model);
compilerAssert(ref.kind === "code", "Unexpected ref schema. Should be kind: code");
mapping[key] = (ref.value as any).$ref;
if (ref.value instanceof Placeholder) {
ref.value.onValue((resolvedValue) => {
mapping[key] = (resolvedValue as any).$ref;
});
} else {
mapping[key] = (ref.value as any).$ref;
}
}
return mapping;
}
Expand Down
27 changes: 27 additions & 0 deletions packages/openapi3/test/discriminator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,33 @@ worksFor(supportedVersions, ({ checkFor, openApiFor }) => {
]);
});

it("includes all variants in discriminator mapping when first union variant causes circular emit", async () => {
const openApi = await openApiFor(`
@discriminator("kind")
model Pet { kind: string; }

model Cat extends Pet { kind: "cat"; }
model Dog extends Pet { kind: "dog"; }
model Bird extends Pet { kind: "bird"; }

union PetVariant {
cat: Cat,
dog: Dog,
bird: Bird,
}

op read(): { @body body: PetVariant };
`);
deepStrictEqual(openApi.components.schemas.Pet.discriminator, {
propertyName: "kind",
mapping: {
cat: "#/components/schemas/Cat",
dog: "#/components/schemas/Dog",
bird: "#/components/schemas/Bird",
},
});
});

it("discriminator always needs to be marked as required", async () => {
const openApi = await openApiFor(`
@discriminator("kind")
Expand Down
Loading