Skip to content

Commit

Permalink
Fix: Intermediate discriminated type (#512)
Browse files Browse the repository at this point in the history
fix #458
  • Loading branch information
timotheeguerin authored Apr 1, 2024
1 parent b58a8e8 commit 9cb95e8
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -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.
39 changes: 28 additions & 11 deletions packages/typespec-autorest/src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
}
}
Expand Down
51 changes: 33 additions & 18 deletions packages/typespec-autorest/test/discriminator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,49 +111,64 @@ 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" }]);
deepStrictEqual(openApi.definitions.Dog.allOf, [{ $ref: "#/definitions/Pet" }]);
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")
Expand Down

0 comments on commit 9cb95e8

Please sign in to comment.