From 4a630d9a927008227ecb63bae37a8899e50d4577 Mon Sep 17 00:00:00 2001 From: Mary Njenga Date: Mon, 8 Jun 2026 16:28:19 +0300 Subject: [PATCH 1/3] Add TypeSpec examples to flat-bag and subtypes pattern docs (#1061) --- graph/patterns/flat-bag.md | 50 +++++++++++++++++++++++++++++++++++++- graph/patterns/subtypes.md | 41 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/graph/patterns/flat-bag.md b/graph/patterns/flat-bag.md index 5dedbeef..5e38b871 100644 --- a/graph/patterns/flat-bag.md +++ b/graph/patterns/flat-bag.md @@ -48,4 +48,52 @@ The recurrencePattern has six variants expressed as six different values of the -``` \ No newline at end of file +``` + +### TypeSpec representation + +The same `recurrencePattern` flat-bag is expressed in TypeSpec using the `@flatBag` decorator on the model plus `@variant` decorators on the variant-conditional sibling properties. +The discriminator property is conventionally named `type`, and the discriminator enum must be closed (no `unknownFutureValue`). + +```TypeSpec +enum recurrencePatternType { + daily: 0, + weekly: 1, + absoluteMonthly: 2, + relativeMonthly: 3, + absoluteYearly: 4, + relativeYearly: 5, +} + +@flatBag("type") +@complex model recurrencePattern { + type: recurrencePatternType | null; + + // Shared property — meaningful for every variant + interval: int32; + + // wire-fidelity: shipped as Nullable="false"; variant-conditional but non-nullable + @variant("absoluteMonthly", "absoluteYearly") dayOfMonth: int32; + + @variant("weekly", "relativeMonthly", "relativeYearly") daysOfWeek: dayOfWeek[]; + + @variant("weekly") firstDayOfWeek: dayOfWeek | null; + + @variant("relativeMonthly", "relativeYearly") index: weekIndex | null; + + // wire-fidelity: shipped as Nullable="false"; variant-conditional but non-nullable + @variant("absoluteYearly", "relativeYearly") month: int32; +} +``` + +The `@flatBag` and `@variant` decorators are pure semantic metadata — they do not change the compiled CSDL output, which matches the CSDL shown above. +Their purpose is to enable validation that variant-conditional sibling properties target valid discriminator enum members, and to surface the pattern to downstream tooling. + +**Authoring rules:** + +- The discriminator property is typed as ` | null` to match the shipped Graph CSDL convention. +- Variant-conditional non-collection siblings are nullable (`T | null`). +- Variant-conditional collection siblings use `T[]` rather than `T[] | null` — empty array signals absence per Graph wire convention. +- Properties meaningful for every variant (`interval` above) remain unannotated. +- Non-nullable variant-conditional siblings (like `dayOfMonth`, `month`) preserve the shipped Graph wire shape. + The inline `// wire-fidelity:` comment documents the exception, and a `#suppress` for `flat-bag-siblings-should-be-nullable` may be needed under stricter rulesets. \ No newline at end of file diff --git a/graph/patterns/subtypes.md b/graph/patterns/subtypes.md index aad10593..3e281b4e 100644 --- a/graph/patterns/subtypes.md +++ b/graph/patterns/subtypes.md @@ -70,6 +70,47 @@ Groups and users are derived types and modeled as follows: ``` +### TypeSpec representation + +The same `directoryObject` hierarchy is expressed in TypeSpec using the `@abstract` decorator on the base type and the native `extends` keyword on each derived type. +No purpose-built decorators are needed beyond `@abstract`, `@entity`, and `@key`. + +```TypeSpec +@abstract +@entity model entity { + @key id: string; +} + +@abstract +@entity model directoryObject extends entity { + deletedDateTime: utcDateTime | null; +} + +@entity model group extends directoryObject { + description: string | null; + // ... other group-specific properties +} + +@entity model user extends directoryObject { + jobTitle: string | null; + // ... other user-specific properties +} +``` + +The `@abstract` decorator emits `Abstract="true"` in CSDL and marks the type as non-instantiable — consumers must POST or PATCH against a derived type. +Each derived type inherits the base properties (including the key) via `extends` and adds only its own variant-specific properties. +The TypeSpec compiler rejects redeclaration of base properties on derived types. + +**Authoring rules:** + +- The base type carries `@abstract` and the `@key` property; derived types inherit both via `extends`. +- Derived types use `extends ` and add only their own variant-specific properties. +- Derived types do NOT redeclare base properties (compile error). +- Hierarchy depth ≤ 3 levels — deeper hierarchies usually indicate wrong modeling. + +The compiled CSDL matches the structure shown above. +URL and query semantics shown in the following sections apply regardless of authoring language. + An API request to get members of a group returns a heterogeneous collection of users and groups where each element can be a user or a group, and has an additional `@odata.type` property that specifies the subtype: From 8fd2960bc9b6442daf3b66d04c63a92e1982aa74 Mon Sep 17 00:00:00 2001 From: "Joel Ngei (from Dev Box)" Date: Mon, 22 Jun 2026 19:41:31 +0300 Subject: [PATCH 2/3] Add TypeSpec representation for the facets pattern Documents how the facets API design pattern is authored in TypeSpec using the @facet decorator from @microsoft/typespec-msgraph (issue microsoftgraph/typespec-msgraph#1061, Phase 6): - facets.md: new "Facets in TypeSpec" section with a driveItem example (audio/file/folder/image/video as @facet nullable @complex properties), the linter rules, and the compiled CSDL (a standard nullable complex property -- @facet is authoring/lint-only and emits no CSDL annotation). - GuidelinesGraph.md: add a "TypeSpec representation" column to the modeling-variants comparison table and a pointer to the new section. --- graph/GuidelinesGraph.md | 12 ++--- graph/patterns/facets.md | 95 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) diff --git a/graph/GuidelinesGraph.md b/graph/GuidelinesGraph.md index 1aa99b62..2afa6e77 100644 --- a/graph/GuidelinesGraph.md +++ b/graph/GuidelinesGraph.md @@ -202,13 +202,13 @@ The three most often used patterns in Microsoft Graph today are type hierarchy, - **[Enums](./patterns/enums.md)** represent a subset of the nominal type they rely on, and are especially useful in cases where certain properties have predefined, limited options. -The following table shows a summary of the main qualities for each pattern and can help you select a pattern fit for your use case. +The following table shows a summary of the main qualities for each pattern and can help you select a pattern fit for your use case. The **TypeSpec representation** column shows how each pattern is authored with the [`@microsoft/typespec-msgraph`](https://aka.ms/typespec) library; see the [facets TypeSpec example](./patterns/facets.md#facets-in-typespec) for a worked sample. -| API qualities\patterns | Properties and behavior described in metadata | Supports combinations of properties and behaviors | Simple query construction | -|-------------------------|-----------------------------------------------|---------------------------------------------------|---------------------------| -| Type hierarchy | yes | no | no | -| Facets | partially | yes | yes | -| Flat bag | no | no | yes | +| API qualities\patterns | Properties and behavior described in metadata | Supports combinations of properties and behaviors | Simple query construction | TypeSpec representation | +|-------------------------|-----------------------------------------------|---------------------------------------------------|---------------------------|-------------------------| +| Type hierarchy | yes | no | no | `extends` (with `@abstract` base) | +| Facets | partially | yes | yes | `@facet` on a nullable `@complex` property | +| Flat bag | no | no | yes | plain entity properties (no dedicated decorator) | #### Pros and cons diff --git a/graph/patterns/facets.md b/graph/patterns/facets.md index 949d573a..d69a2790 100644 --- a/graph/patterns/facets.md +++ b/graph/patterns/facets.md @@ -107,3 +107,98 @@ Response shortened for readability: } ] ``` + +## Facets in TypeSpec + +In [TypeSpec](https://aka.ms/typespec), the facets pattern is expressed with the `@facet` decorator from the `@microsoft/typespec-msgraph` library. A facet is a **nullable property** on an `@entity` model whose type is a `@complex` model. The `@facet` decorator marks the property as a variant facet so that authoring tooling and linters can validate it. + +The following TypeSpec models the same `driveItem` facets shown in the CSDL example above — each variant (`audio`, `file`, `folder`, `image`, `video`) is a complex type, and the entity carries one nullable facet property per variant: + +```typespec +// One @complex type per variant facet +@complex +model audio { + @doc("The title of the album for this audio file.") + album: string | null; +} + +@complex +model file { + @doc("The MIME type for the file.") + mimeType: string | null; +} + +@complex +model folder { + @doc("Number of children contained immediately within this container.") + childCount: int32 | null; +} + +@complex +model image { + @doc("Width of the image, in pixels.") + width: int32 | null; + + @doc("Height of the image, in pixels.") + height: int32 | null; +} + +@complex +model video { + @doc("Duration of the video, in milliseconds.") + duration: int64 | null; +} + +// The entity declares one nullable facet property per variant +@entity +model driveItem { + @key id: string; + + @doc("The name of the item (file name, folder name).") + displayName: string | null; + + @doc("Audio facet. Present when the item is an audio file.") + @facet audio: audio | null; + + @doc("File facet. Present when the item is a file.") + @facet file: file | null; + + @doc("Folder facet. Present when the item is a folder.") + @facet folder: folder | null; + + @doc("Image facet. Present when the item is an image.") + @facet image: image | null; + + @doc("Video facet. Present when the item is a video.") + @facet video: video | null; +} +``` + +### Rules for `@facet` + +The `@facet` decorator is validated by the `@microsoft/typespec-msgraph` linter: + +- The property type must be a `@complex` model — primitive, enum, and entity types are rejected. +- The property must be **nullable** (`T | null`) — a variant may be absent. +- The property must be declared on an `@entity` model — facets on `@complex` models are rejected. + +### Compiled CSDL + +`@facet` is an **authoring-time marker**: it carries no wire-format meaning, so a facet property compiles to a standard nullable complex-typed property — identical to what a hand-authored facet produces. The TypeSpec above emits: + +```xml + + + + + + + + + + + + +``` + +Because `@facet` adds no CSDL annotation, applying or omitting it produces byte-identical metadata; its value is the design intent it records and the linter checks it enables. From 3bd341b03d58602ecae7cf8ad009f9a6bd476e02 Mon Sep 17 00:00:00 2001 From: Mary Njenga Date: Tue, 23 Jun 2026 11:12:42 +0300 Subject: [PATCH 3/3] Add TypeSpec representation column to pattern comparison table (#1061) - Type hierarchy: @abstract + extends - Flat bag: @flatBag + @variant - Facets: left blank pending separate work Co-Authored-By: Claude Opus 4.7 --- graph/GuidelinesGraph.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graph/GuidelinesGraph.md b/graph/GuidelinesGraph.md index 1aa99b62..c170d712 100644 --- a/graph/GuidelinesGraph.md +++ b/graph/GuidelinesGraph.md @@ -204,11 +204,11 @@ The three most often used patterns in Microsoft Graph today are type hierarchy, The following table shows a summary of the main qualities for each pattern and can help you select a pattern fit for your use case. -| API qualities\patterns | Properties and behavior described in metadata | Supports combinations of properties and behaviors | Simple query construction | -|-------------------------|-----------------------------------------------|---------------------------------------------------|---------------------------| -| Type hierarchy | yes | no | no | -| Facets | partially | yes | yes | -| Flat bag | no | no | yes | +| API qualities\patterns | Properties and behavior described in metadata | Supports combinations of properties and behaviors | Simple query construction | TypeSpec representation | +|-------------------------|-----------------------------------------------|---------------------------------------------------|---------------------------|--------------------------------| +| Type hierarchy | yes | no | no | `@abstract` + `extends` | +| Facets | partially | yes | yes | — | +| Flat bag | no | no | yes | `@flatBag` + `@variant` | #### Pros and cons