diff --git a/.chronus/changes/fix-intermediate-discriminated-type-2024-2-25-18-51-32.md b/.chronus/changes/fix-intermediate-discriminated-type-2024-2-25-18-51-32.md new file mode 100644 index 0000000000..5c703bd584 --- /dev/null +++ b/.chronus/changes/fix-intermediate-discriminated-type-2024-2-25-18-51-32.md @@ -0,0 +1,8 @@ +--- +# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking +changeKind: fix +packages: + - "@azure-tools/typespec-autorest" +--- + +Fix: Discriminated inheritance wasn't resolving the `x-ms-discriminator-value` when it had an intermediate model. diff --git a/packages/typespec-autorest/src/openapi.ts b/packages/typespec-autorest/src/openapi.ts index 20a468d398..b0a4ca4e49 100644 --- a/packages/typespec-autorest/src/openapi.ts +++ b/packages/typespec-autorest/src/openapi.ts @@ -1635,6 +1635,29 @@ function createOAPIEmitter( ); } + function getDiscriminatorValue(model: Model): string | undefined { + let discriminator; + let current = model; + while (current.baseModel) { + discriminator = getDiscriminator(program, current.baseModel); + if (discriminator) { + break; + } + current = current.baseModel; + } + if (discriminator === undefined) { + return undefined; + } + const prop = getProperty(model, discriminator.propertyName); + if (prop) { + const values = getStringValues(prop.type); + if (values.length === 1) { + return values[0]; + } + } + return undefined; + } + function getSchemaForModel(model: Model, visibility: Visibility) { const array = getArrayType(model, visibility); if (array) { @@ -1647,17 +1670,11 @@ function createOAPIEmitter( }; if (model.baseModel) { - const discriminator = getDiscriminator(program, model.baseModel); - if (discriminator) { - const prop = getProperty(model, discriminator.propertyName); - if (prop) { - const values = getStringValues(prop.type); - if (values.length === 1) { - const extensions = getExtensions(program, model); - if (!extensions.has("x-ms-discriminator-value")) { - modelSchema["x-ms-discriminator-value"] = values[0]; - } - } + const discriminatorValue = getDiscriminatorValue(model); + if (discriminatorValue) { + const extensions = getExtensions(program, model); + if (!extensions.has("x-ms-discriminator-value")) { + modelSchema["x-ms-discriminator-value"] = discriminatorValue; } } } diff --git a/packages/typespec-autorest/test/discriminator.test.ts b/packages/typespec-autorest/test/discriminator.test.ts index a25e73f177..0dfbea9739 100644 --- a/packages/typespec-autorest/test/discriminator.test.ts +++ b/packages/typespec-autorest/test/discriminator.test.ts @@ -111,42 +111,28 @@ describe("typespec-autorest: polymorphic model inheritance with discriminator", deepStrictEqual(openApi.definitions.CatCls.allOf, [{ $ref: "#/definitions/Pet" }]); }); - it("defines discriminated unions with more than one level of inheritance", async () => { + it("defines discriminated inheritance with extra non discriminated leaf types", async () => { const openApi = await openApiFor(` @discriminator("kind") model Pet { - name: string; - weight?: float32; + kind: string; } model Cat extends Pet { kind: "cat"; - meow: int32; } model Dog extends Pet { kind: "dog"; - bark: string; } model Beagle extends Dog { purebred: boolean; } - - op read(): { @body body: Pet }; `); - ok(openApi.definitions.Pet, "expected definition named Pet"); - ok(openApi.definitions.Cat, "expected definition named Cat"); - ok(openApi.definitions.Dog, "expected definition named Dog"); - ok(openApi.definitions.Beagle, "expected definition named Beagle"); - deepStrictEqual(openApi.paths["/"].get.responses["200"].schema, { - $ref: "#/definitions/Pet", - }); deepStrictEqual(openApi.definitions.Pet, { type: "object", properties: { - kind: { type: "string", description: "Discriminator property for Pet." }, - name: { type: "string" }, - weight: { type: "number", format: "float" }, + kind: { type: "string" }, }, - required: ["kind", "name"], + required: ["kind"], discriminator: "kind", }); deepStrictEqual(openApi.definitions.Cat.allOf, [{ $ref: "#/definitions/Pet" }]); @@ -154,6 +140,35 @@ describe("typespec-autorest: polymorphic model inheritance with discriminator", deepStrictEqual(openApi.definitions.Beagle.allOf, [{ $ref: "#/definitions/Dog" }]); }); + it("defines discriminated inheritance with intermediate non discriminated model", async () => { + const openApi = await openApiFor(` + @discriminator("kind") + model Pet { + kind: string; + } + + model CommonPet extends Pet { + name: string; + } + + model Dog extends CommonPet { + kind: "dog-kind"; + } + `); + deepStrictEqual(openApi.definitions.Pet, { + type: "object", + properties: { + kind: { type: "string" }, + }, + required: ["kind"], + discriminator: "kind", + }); + deepStrictEqual(openApi.definitions.Dog.allOf, [{ $ref: "#/definitions/CommonPet" }]); + strictEqual(openApi.definitions.Dog["x-ms-discriminator-value"], "dog-kind"); + deepStrictEqual(openApi.definitions.CommonPet.allOf, [{ $ref: "#/definitions/Pet" }]); + strictEqual(openApi.definitions.CommonPet["x-ms-discriminator-value"], undefined); + }); + it("defines nested discriminated unions", async () => { const openApi = await openApiFor(` @discriminator("kind")