From 3ef793cab968d0386b84d0f1a3fa3fc35ebafad2 Mon Sep 17 00:00:00 2001
From: Garrett DeBruin <16618938+corranrogue9@users.noreply.github.com>
Date: Mon, 22 Jan 2024 16:41:37 -0800
Subject: [PATCH 1/3] Create property-validity-using-enums
---
graph/patterns/property-validity-using-enums | 30 ++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 graph/patterns/property-validity-using-enums
diff --git a/graph/patterns/property-validity-using-enums b/graph/patterns/property-validity-using-enums
new file mode 100644
index 00000000..456908d2
--- /dev/null
+++ b/graph/patterns/property-validity-using-enums
@@ -0,0 +1,30 @@
+# Property validity using enums
+
+Microsoft Graph API Design Pattern
+
+*Provide a short description of the pattern.*
+
+
+## Problem
+
+*Describe the business context relevant for the pattern.*
+
+*Provide a short description of the problem.*
+
+## Solution
+
+*Describe how to implement the solution to solve the problem.*
+
+*Describe related patterns.*
+
+## When to use this pattern
+
+*Describe when and why the solution is applicable and when it might not be.*
+
+## Issues and considerations
+
+*Describe tradeoffs of the solution.*
+
+## Example
+
+*Provide a short example from real life.*
From 919f3d80f9d20c32a87ae5f3510b16a374cb53c7 Mon Sep 17 00:00:00 2001
From: Garrett DeBruin <16618938+corranrogue9@users.noreply.github.com>
Date: Mon, 22 Jan 2024 16:41:53 -0800
Subject: [PATCH 2/3] Rename property-validity-using-enums to
property-validity-using-enums.md
---
...erty-validity-using-enums => property-validity-using-enums.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename graph/patterns/{property-validity-using-enums => property-validity-using-enums.md} (100%)
diff --git a/graph/patterns/property-validity-using-enums b/graph/patterns/property-validity-using-enums.md
similarity index 100%
rename from graph/patterns/property-validity-using-enums
rename to graph/patterns/property-validity-using-enums.md
From 631e6967fd598b9a64d64be6bdeb4546adaa4d87 Mon Sep 17 00:00:00 2001
From: Garrett DeBruin <16618938+corranrogue9@users.noreply.github.com>
Date: Mon, 22 Jan 2024 16:48:02 -0800
Subject: [PATCH 3/3] Update property-validity-using-enums.md
---
.../patterns/property-validity-using-enums.md | 281 +++++++++++++++++-
1 file changed, 272 insertions(+), 9 deletions(-)
diff --git a/graph/patterns/property-validity-using-enums.md b/graph/patterns/property-validity-using-enums.md
index 456908d2..b62319ed 100644
--- a/graph/patterns/property-validity-using-enums.md
+++ b/graph/patterns/property-validity-using-enums.md
@@ -2,29 +2,292 @@
Microsoft Graph API Design Pattern
-*Provide a short description of the pattern.*
-
+*There are often cases where a type has a property which denotes a kind of role that the type can fulfill. This property can evolve over time to a point where the way the type that fulfills that role needs to be configured in some way. This pattern allows configuring such properties in a forward compatible way.*
## Problem
-*Describe the business context relevant for the pattern.*
+When types have these different kinds of roles, they usually end up modeled like this:
+```xml
+
+
+
+
+
+
+
+
+
+
+
+```
+
+```http
+GET https://graph.microsoft.com/v1.0/identity/foos
-*Provide a short description of the problem.*
+{
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#foo",
+ "values": [
+ {
+ "roleKind": "first",
+ "roleThirdKindValue": null
+ },
+ {
+ "roleKind": "third",
+ "roleThirdKindValue": "some value"
+ }
+ ]
+}
+```
+
+In this scenario, `roleThirdKindValue` being valid depends on what value is provided for `roleKind` (Please note that, although these examples use an enum for `roleKind`,
+other types are still susceptible to this anti-pattern. Please review [this](https://dev.azure.com/msazure/One/_wiki/wikis/Microsoft%20Graph%20Partners/211730/Finite-number-of-choices)
+guidance to determine if an enum better suites your purposes).
## Solution
-*Describe how to implement the solution to solve the problem.*
+When it happens that the validity of one property depends on the value of another property there are two general approaches:
+1. Use inheritance
+2. Use composition
+
+Please review [these](https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/tutorials/inheritance#inheritance-and-an-is-a-relationship) MSDN guidelines for using inheritance ("is-a"
+relationship) or composition ("has-a" relationship). This guidance will be focused on composition. In that situation, the API should be modeled this way:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+```http
+GET https://graph.microsoft.com/v1.0/identity/foos
+
+{
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#foo",
+ "values": [
+ {
+ "roleKind": "first",
+ "roleKindSettings": null
+ },
+ {
+ "roleKind": "third",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleThirdKindSettings",
+ "roleThirdKindValue": "some value"
+ }
+ }
+ ]
+}
+```
+
+The first thing to notice is that the *validity* of a property (`roleThirdKindValue`) is no longer dependent on the value of another property (`roleKind`). This makes the API easier to
+understand without needing to refer to documentation, and also allows the type system to handle error cases syntactically, rather than relying on the workload to implement all of the error
+handling explicitly. The *possible* values for `roleThirdKindValue` are still dependent on the value of `roleKind`.
+
+The second thing to notice is that it always gives us a path forward if additional "settings" are needed. For example, to add settings for roleKind.second, this type can be added:
+
+```xml
+
+
+
+```
+
+```http
+GET https://graph.microsoft.com/v1.0/identity/foos
+
+{
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#foo",
+ "values": [
+ {
+ "roleKind": "first",
+ "roleKindSettings": null
+ },
+ {
+ "roleKind": "third",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleThirdKindSettings",
+ "roleThirdKindValue": "some value"
+ }
+ },
+ {
+ "roleKind": "second",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleSecondKindSettings",
+ "roleSecondKindValue": "some other value"
+ }
+ }
+ ]
+}
+```
+
+It also doesn't require introducing breaking changes. The original enum property remains, meaning that we only have the additive change of the "settings" property.
+
+The third thing to notice is that we can support additional "roles" on the same type. Suppose we later add:
+
+```xml
+
+
+
+
+
-*Describe related patterns.*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+```http
+GET https://graph.microsoft.com/v1.0/identity/foos
+
+{
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#foo",
+ "values": [
+ {
+ "roleKind": "first",
+ "roleKindSettings": null,
+ "anotherRoleKind": "third"
+ },
+ {
+ "roleKind": "third",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleThirdKindSettings",
+ "roleThirdKindValue": "some value"
+ },
+ "anotherRoleKind": "first"
+ },
+ {
+ "roleKind": "second",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleSecondKindSettings",
+ "roleSecondKindValue": "some other value"
+ },
+ "anotherRoleKind": "second"
+ }
+ ]
+}
+```
+
+We have added `anotherRoleKind` without needing to disturb the existing `roleKind` (if we modelled this using inheritance, we would not be able to do this).
## When to use this pattern
-*Describe when and why the solution is applicable and when it might not be.*
+This pattern should be used whenever an enum property indicates the validity of other properties within an entity type. It can also be adapted for other use cases where the value of a non-enum property indicates the same.
## Issues and considerations
-*Describe tradeoffs of the solution.*
+If possible, the enum property should not be introduced in the first place. However, once it is introduced, it is better to leave it to maintain client backwards compatibility.
## Example
-*Provide a short example from real life.*
+An example of this composition pattern on graph can be found in [this](https://microsoftgraph.visualstudio.com/onboarding/_git/onboarding?path=/reviews/213-Add-data-classification-service-API-to-Graph/api.md&version=GCd23e568a1d3f95edbc563d8e3d36a4ddf2e39f53&line=2169&lineEnd=2169&lineStartColumn=3&lineEndColumn=16&lineStyle=plain&_a=contents)
+API review. In this case, the validity of the `bodyText` property depends on the `scanningState` that is present. Although sometimes this would make sense to model with inheritance
+(again, please refer to the MSDN guidance around "is-a" vs "has-a" relationships), doing so would look something like this:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+```
+
+```http
+GET https://graph.microsoft.com/v1.0/identity/foos
+
+{
+ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#foo",
+ "values": [
+ {
+ "roleKind": "first",
+ "roleKindSettings": null,
+ "anotherRoleKind": "third"
+ },
+ {
+ "roleKind": "third",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleThirdKindSettings",
+ "roleThirdKindValue": "some value"
+ },
+ "anotherRoleKind": "first"
+ },
+ {
+ "roleKind": "second",
+ "roleKindSettings": {
+ "@odata.type":"microsoft.graph.roleSecondKindSettings",
+ "roleSecondKindValue": "some other value"
+ },
+ "anotherRoleKind": "second"
+ }
+ ]
+}
+```
+
+Doing this, however, would prevent the API owner from adding something like an `attachmentType` enum which informs the validity of a property about each attachment type. By using composition,
+the API owner is able to model both the `scanningState` *and* a future (currently unknown) `attachmentType` enum, something that could look like:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ...
+
+
+
+...
+
+
+
+
+
+
+
+```