├── .github
└── CODEOWNERS
├── CONTRIBUTING.md
├── Guidelines.md
├── README.md
├── SECURITY.md
├── azure
├── .markdownlint.json
├── ConsiderationsForServiceDesign.md
├── Guidelines.md
├── README.md
└── VersioningGuidelines.md
├── graph
├── Guidelines-deprecated.md
├── GuidelinesGraph.md
├── ModelExample.png
├── articles
│ ├── collections.md
│ ├── coreTypes.md
│ ├── deprecation.md
│ ├── errorResponses.md
│ ├── filter-as-segment.md
│ ├── naming.md
│ └── nullable.md
└── patterns
│ ├── LRO.gif
│ ├── PatternDescriptionTemplate.md
│ ├── RELO.gif
│ ├── alternate-key.md
│ ├── antiPatternTemplate.md
│ ├── change-tracking.md
│ ├── default-properties.md
│ ├── dictionary-client-guidance.md
│ ├── dictionary.md
│ ├── enums.md
│ ├── evolvable-enums.md
│ ├── facets.md
│ ├── flat-bag.md
│ ├── long-running-operations.md
│ ├── namespace.md
│ ├── navigation-property.md
│ ├── operations.md
│ ├── subsets.md
│ ├── subtypes.md
│ ├── upsert.md
│ └── viewpoint.md
└── license.txt
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # These are the set of folks who should review PRs on the azureRestUpdates branch.
2 | #* @microsoft/azure-api-stewardship-board @Azure/api-stewardship-board
3 | /graph/ @microsoft/graphguidelinesapprovers
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the Microsoft REST API Guidelines
2 | The Microsoft REST API Guidelines is a Microsoft-wide initiative to develop consistent design guidelines for REST APIs. The initiative requires input and feedback from a variety of individuals both inside and outside of Microsoft.
3 |
4 | To provide feedback, please follow the guidance in this document. Please note that these are just guidelines, not rules. Use your best judgment and feel free to propose changes to anything in this repository, including the contribution guidelines.
5 |
6 | Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
7 | - [Creating issues](#creating-issues)
8 | - [Recommended setup for contributing](#recommended-setup-for-contributing)
9 | - [Documentation styleguide](#documentation-styleguide)
10 | - [Commit messages](#commit-messages)
11 | - [Pull requests](#pull-requests)
12 |
13 | ## Creating issues
14 | - You can [create an issue][new-issue], but before doing that please read the bullets below and include as many details as possible.
15 | - Perform a [cursory search][issue-search] to see if a similar issue has already been submitted.
16 | - Reference the version of the Microsoft REST API Guidelines you are using.
17 | - Include the guidance you expected and other places you've seen that guidance, e.g. [White House Web API Standards][white-house-api-guidelines].
18 | - Include sample requests and responses whenever possible.
19 |
20 | ### Related repositories
21 | This is the repository for Microsoft REST API Guidelines documentation only. Please ensure that you are opening issues in the right repository.
22 |
23 | ## Recommended setup for contributing
24 | - Fork this repository on GitHub
25 | - Install [Git][git] to your computer and clone your new forked repository
26 | - Install [Atom][atom], [VS Code][vscode], or your favorite editor
27 | - Install [markdown-toc package][markdown-toc]
28 |
29 | ## Documentation styleguide
30 | - Use [GitHub-flavored markdown][gfm]
31 | - Use syntax-highlighted examples liberally
32 | - Trim trailing empty lines from HTTP requests
33 | - Retain only essential headers for understanding the example
34 | - Use valid (e.g., member names quoted), pretty-printed JSON with a 2 space indent
35 | - Minimize JSON payloads by using ellipses
36 | - Write one sentence per line.
37 |
38 | ### Example
39 | #### Request
40 |
41 | ```http
42 | GET http://services.odata.org/V4/TripPinServiceRW/People HTTP/1.1
43 | Accept: application/json
44 | ```
45 |
46 | #### Response
47 |
48 | ```http
49 | HTTP/1.1 200 OK
50 | Content-Type: application/json
51 |
52 | {
53 | "@nextLink":"http://services.odata.org/V4/TripPinServiceRW/People?$skiptoken=8",
54 | "value":[
55 | {
56 | "userName":"russellwhyte",
57 | "firstName":"Russell",
58 | "lastName":"Whyte",
59 | "emails":[
60 | "Russell@example.com",
61 | "Russell@contoso.com"
62 | ],
63 | "addressInfo":[
64 | {
65 | "address":"187 Suffolk Ln.",
66 | "city":{
67 | "countryRegion":"United States",
68 | "name":"Boise",
69 | "region":"ID"
70 | }
71 | }
72 | ],
73 | "gender":"Male",
74 | },
75 | ...
76 | ]
77 | }
78 | ```
79 |
80 | ## Commit messages
81 | - Use the present tense: "Change ...", not "Changed ..."
82 | - Use the imperative mood: "Change ...", not "Changes ..."
83 | - Limit the first line to 72 characters or less
84 | - Reference issues and pull requests liberally
85 |
86 | ## Pull requests
87 | Pull requests serve as the primary mechanism by which contributions are proposed and accepted. We recommend creating a [topic branch][topic-branch] and sending a pull request to the `vNext` branch from the topic branch. For additional guidance, read through the [GitHub Flow Guide][github-flow-guide].
88 |
89 | Be prepared to address feedback on your pull request and iterate if necessary.
90 |
91 | [code-of-conduct]: https://opensource.microsoft.com/codeofconduct/
92 | [new-issue]: https://github.com/Microsoft/api-guidelines/issues/new
93 | [issue-search]: https://github.com/Microsoft/api-guidelines/issues
94 | [white-house-api-guidelines]: https://github.com/WhiteHouse/api-standards/blob/master/README.md
95 | [topic-branch]: https://www.git-scm.com/book/en/v2/Git-Branching-Branching-Workflows#Topic-Branches
96 | [gfm]: https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown
97 | [github-flow-guide]: https://guides.github.com/introduction/flow/
98 | [atom-beautify]: https://atom.io/packages/atom-beautify
99 | [atom]: https://atom.io/
100 | [markdown-toc]: https://atom.io/packages/markdown-toc
101 | [vscode]: https://code.visualstudio.com/
102 | [git]: https://git-scm.com/
103 |
--------------------------------------------------------------------------------
/Guidelines.md:
--------------------------------------------------------------------------------
1 | > # NOTICE TO READERS
2 | > This document has been deprecated and has been moved to the [Microsoft REST API Guidelines deprecated](./graph/Guidelines-deprecated.md). Please refer to the notes below for the latest guidance.
3 | >
4 | > ## **Guidance for Azure service teams**
5 | > Azure service teams should use the companion documents, [Azure REST API Guidelines](./azure/Guidelines.md) and [Considerations for Service Design](./azure/ConsiderationsForServiceDesign.md), when building or modifying their services. These documents provide a refined set of guidance targeted specifically for Azure services. For more information, see the [README](./azure/README.md) in the Azure folder.
6 | >
7 | > ## **Guidance for Microsoft Graph service teams**
8 | > Graph service teams should reference the companion document, [Microsoft Graph REST API Guidelines](./graph/GuidelinesGraph.md) when building or modifying their services. This document and the associated pattern catalog provides a refined set of guidance targeted specifically for Microsoft Graph services.
9 |
10 | ---
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Microsoft REST API Guidelines
2 | Thank you for your interest in the Microsoft REST API Guidelines. If you have landed here, you're probably interested in learning about APIs. If so, you are in the right place!
3 | We publish these guidelines here with the aim of fostering dialogue and learning in the API community at large. We further hope that these guidelines might encourage other organizations to create guidelines that are appropriate for them and in turn, if they're able, to publish theirs.
4 |
5 | ### Guidance for Azure service teams
6 | Azure service teams should reference the companion documents, [Azure REST API Guidelines](./azure/Guidelines.md) and [Considerations for Service Design](./azure/ConsiderationsForServiceDesign.md), when building or modifying their services. These documents provide a refined set of guidance targeted specifically for Azure services. For more information, please refer to the [README](./azure/README.md) in the Azure folder.
7 |
8 | ### Guidance for Microsoft Graph service teams
9 | Graph service teams should reference the companion document, [Microsoft Graph REST API Guidelines](./graph/GuidelinesGraph.md) when building or modifying their services. This document and the associated pattern catalog provide a refined set of guidance targeted specifically for Microsoft Graph services.
10 |
11 | [](https://creativecommons.org/licenses/by/4.0/)
12 |
13 | ## Code of Conduct
14 | This project adopts the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
15 |
16 | ## This repository
17 | This repository contains a collection of documents and related materials supporting the overall Microsoft REST API Guidelines initiative. To contribute to this repository, see the [contribution guidelines][contribution-guidance].
18 |
19 | [contribution-guidance]: CONTRIBUTING.md
20 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/azure/.markdownlint.json:
--------------------------------------------------------------------------------
1 | {
2 | "default": true,
3 | "MD012": {
4 | "maximum": 2
5 | },
6 | "MD013": {
7 | "line_length": 500
8 | },
9 | "MD022": false,
10 | "MD031": false,
11 | "MD032": false,
12 | "MD033": {
13 | "allowed_elements": [ "sup" ]
14 | },
15 | "MD036": false
16 | }
--------------------------------------------------------------------------------
/azure/README.md:
--------------------------------------------------------------------------------
1 | # Azure REST API Guidance
2 | When building software components that will be used by developers to build other software, providing APIs that are easy to use, fit to purpose, scalable, maintainable, and consistent across your product can make the difference between success and failure for your software.
3 |
4 | Designing powerful APIs with strong defaults, consistent behavior across related projects, and ease of use for developers arises from putting yourself in the shoes of the person using your interfaces, and taking their concerns to heart. The APIs you ship can have a dramatic long term effect on the health of your software product, and that is why the **REST API Stewardship Board** is here to help! We have published a collection of best practices, REST guidance, and OpenAPI style guidelines to help you create an amazing developer experience.
5 | * [Considerations for Service Design](ConsiderationsForServiceDesign.md)
6 | * [REST API Guidelines](Guidelines.md)
7 | * [OpenAPI Style Guidelines](https://github.com/Azure/azure-api-style-guide/blob/main/README.md)
8 | * [Versioning policy for Azure services, SDKs, and CLI tools](https://learn.microsoft.com/en-us/azure/developer/intro/azure-service-sdk-tool-versioning)
9 | * [Breaking Changes](https://aka.ms/azapi/breakingchanges) Note: Internal Microsoft link
10 |
11 | You can reach out to us via [email](mailto://azureapirbcore@microsoft.com) or in our [Teams](https://teams.microsoft.com/l/team/19%3a3ebb18fded0e47938f998e196a52952f%40thread.tacv2/conversations?groupId=1a10b50c-e870-4fe0-8483-bf5542a8d2d8&tenantId=72f988bf-86f1-41af-91ab-2d7cd011db47) channel.
12 |
13 | Note: The Teams channel is internal MS.
14 |
--------------------------------------------------------------------------------
/azure/VersioningGuidelines.md:
--------------------------------------------------------------------------------
1 | # Azure Versioning Guidelines
2 |
3 | ## History
4 |
5 |
6 | Expand change history
7 |
8 | | Date | Notes |
9 | | ----------- | -------------------------------------------------------------- |
10 | | 2024-Nov-14 | Azure Service Versioning & Breaking Change Guidelines |
11 |
12 |
13 |
14 | ## Guidelines
15 |
16 | This document provides a "Dos and Don'ts" list for complying with the Azure Versioning and Breaking Change Policy,
17 | as documented [internally](aka.ms/AzBreakingChangesPolicy) and [externally](https://learn.microsoft.com/azure/developer/intro/azure-service-sdk-tool-versioning).
18 |
19 | :white_check_mark: **DO** thoroughly ensure/test the API contract is entirely correct before merging it into a production branch of the specs repo.
20 |
21 | Testing helps avoid "BugFix" changes to the API definition. Testing should be done at the HTTP level as well as through generated SDKs.
22 |
23 | :white_check_mark: **DO** retire all prior preview API versions 90 days after a new GA or preview API version is released.
24 |
25 | :white_check_mark: **DO** contact the Azure Breaking Change Review board to coordinate communications to customers
26 | when releasing an API version requiring the retirement of a prior version.
27 |
28 | :white_check_mark: **DO** create a new preview API version for any features that should remain in preview following a new GA release.
29 |
30 | :white_check_mark: **DO** use a date strictly later than the most recent GA API version when releasing
31 | a new preview API version.
32 |
33 | :white_check_mark: **DO** deprovision any API version that has been retired. Retired APIs versions should behave like
34 | an unknown API version (see [ref](https://aka.ms/azapi/guidelines#versioning-api-version-unsupported)).
35 |
36 | :white_check_mark: **DO** remove retired API versions from the azure-rest-api-specs repo.
37 |
38 | :white_check_mark: **DO** review any change to service behavior that could disrupt customers with the Azure Breaking Changes review board, even if the change is not part of the API definition.
39 |
40 | Some examples of behavior changes that must be reviewed are:
41 | - Introducing or changing rate limits to be more restrictive than previously
42 | - Changing the permissions required to successfully execute an operation
43 |
44 | :no_entry: **DO NOT** change the behavior of an API version that is available to customers either in public preview or GA.
45 | Changes in behavior should always be introduced in a new API version, with prior versions working as before.
46 |
47 | :no_entry: **DO NOT** introduce breaking changes from a prior GA version just to satisfy ARM or Azure API guidelines.
48 |
49 | Avoiding breaking changes in a GA API takes precedence over adherence to API guidelines and resolving linter errors.
50 |
51 | :no_entry: **DO NOT** keep a preview feature in preview for more than 1 year; it must go GA (or be removed) within 1 year after introduction.
52 |
--------------------------------------------------------------------------------
/graph/ModelExample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/api-guidelines/3db3462a9361132adf60da74cff3aced08eb9ff7/graph/ModelExample.png
--------------------------------------------------------------------------------
/graph/articles/collections.md:
--------------------------------------------------------------------------------
1 | # Collections
2 |
3 | ## 1. Item keys
4 |
5 | Services SHOULD support durable identifiers for each item in the collection, and that identifier SHOULD be represented in JSON as "id". These durable identifiers are often used as item keys.
6 |
7 | Collections MAY support delta queries, see the [Change Tracking pattern](../patterns/change-tracking.md) section for more details.
8 |
9 | ## 2. Serialization
10 |
11 | Collections are represented in JSON using standard array notation for `value` property.
12 |
13 | ## 3. Collection URL patterns
14 |
15 | While there are multiple collections located directly under the Graph root going forward, you MUST have a singleton for the top-level segment and scope collections to an appropriate singleton. Collection names SHOULD be plural nouns when possible. Collection names shouldn't use suffixes, such as "Collection" or "List".
16 |
17 | For example:
18 |
19 | ```http
20 | GET https://graph.microsoft.com/v1.0/teamwork/devices
21 | ```
22 |
23 | Collections elements MUST be addressable by a unique id property. The id property MUST be a String and MUST be unique within the collection. The id property MUST be represented in JSON as "id".
24 | For example:
25 |
26 | ```http
27 | GET https://graph.microsoft.com/beta/teamwork/devices/0f3ce432-e432-0f3c-32e4-3c0f32e43c0f
28 | ```
29 |
30 | Where:
31 |
32 | - "https://graph.microsoft.com/beta/teamwork" - the service root represented as the combination of host (site URL) + the root path to the service.
33 | - "devices" – the name of the collection, unabbreviated, pluralized.
34 | - "0f3ce432-e432-0f3c-32e4-3c0f32e43c0f" – the value of the unique id property that MUST be the raw string/number/guid value with no quoting but properly escaped to fit in a URL segment.
35 |
36 | ### 3.1. Nested collections and properties
37 |
38 | Collection items MAY contain other collections.
39 | For example, a devices collection MAY contain device resources that have multiple mac addresses:
40 |
41 | ```http
42 | GET https://graph.microsoft.com/beta/teamwork/devices/0f3ce432-e432-0f3c-32e4-3c0f32e43c0f
43 | ```
44 |
45 | ```json
46 |
47 | {
48 | "value": {
49 | "@odata.type": "#microsoft.graph.teamworkDevice",
50 | "id": "0f3ce432-e432-0f3c-32e4-3c0f32e43c0f",
51 | "deviceType": "CollaborationBar",
52 | "hardwareDetail": {
53 | "serialNumber": "0189",
54 | "uniqueId": "5abcdefgh",
55 | "macAddresses": [],
56 | "manufacturer": "yealink",
57 | "model": "vc210"
58 | },
59 | ...
60 | }
61 | }
62 | ```
63 |
64 | ## 4. Big collections
65 |
66 | As data grows, so do collections.
67 | Services SHOULD support server-side pagination from day one even for all collections, as adding pagination is a breaking change.
68 | When multiple pages are available, the serialization payload MUST contain the opaque URL for the next page as appropriate.
69 | Refer to the [paging guidance](../Guidelines-deprecated.md#98-pagination) for more details.
70 |
71 | Clients MUST be resilient to collection data being either paged or nonpaged for any given request.
72 |
73 | ```json
74 | {
75 | "value":[
76 | { "id": "Item 1","price": 9 95,"sizes": null},
77 | { … },
78 | { … },
79 | { "id": "Item 99","price": 5 99,"sizes": null}
80 | ],
81 | "@nextLink": "{opaqueUrl}"
82 | }
83 | ```
84 |
85 | ## 5. Changing collections
86 |
87 | POST requests are not idempotent.
88 | This means that two POST requests sent to a collection resource with exactly the same payload MAY lead to multiple items being created in that collection.
89 | This is often the case for insert operations on items with a server-side generated id.
90 | For additional information refer to [Upsert pattern](../patterns/upsert.md).
91 |
92 | For example, the following request:
93 |
94 | ```http
95 | POST https://graph.microsoft.com/beta/teamwork/devices
96 | ```
97 |
98 | Would lead to a response indicating the location of the new collection item:
99 |
100 | ```http
101 | 201 Created
102 | Location: https://graph.microsoft.com/beta/teamwork/devices/123
103 | ```
104 |
105 | And once executed again, would likely lead to another resource:
106 |
107 | ```http
108 | 201 Created
109 | Location: https://graph.microsoft.com/beta/teamwork/devices/124
110 | ```
111 |
112 | ## 6. Sorting collections
113 |
114 | The results of a collection query MAY be sorted based on property values.
115 | The property is determined by the value of the _$orderBy_ query parameter.
116 |
117 | The value of the _$orderBy_ parameter contains a comma-separated list of expressions used to sort the items.
118 | A special case of such an expression is a property path terminating on a primitive property.
119 |
120 | The expression MAY include the suffix "asc" for ascending or "desc" for descending, separated from the property name by one or more spaces.
121 | If "asc" or "desc" is not specified, the service MUST order by the specified property in ascending order.
122 |
123 | NULL values MUST sort as "less than" non-NULL values.
124 |
125 | Items MUST be sorted by the result values of the first expression, and then items with the same value for the first expression are sorted by the result value of the second expression, and so on.
126 | The sort order is the inherent order for the type of the property.
127 |
128 | For example:
129 |
130 | ```http
131 | GET https://graph.microsoft.com/beta/teamwork/devices?$orderBy=companyAssetTag
132 | ```
133 |
134 | Will return all devices sorted by companyAssetTag in ascending order.
135 |
136 | For example:
137 |
138 | ```http
139 | GET https://graph.microsoft.com/beta/teamwork/devices?$orderBy=companyAssetTag desc
140 | ```
141 |
142 | Will return all devices sorted by companyAssetTag in descending order.
143 |
144 | Sub-sorts can be specified by a comma-separated list of property names with OPTIONAL direction qualifier.
145 |
146 | For example:
147 |
148 | ```http
149 | GET https://graph.microsoft.com/beta/teamwork/devices?$orderBy=companyAssetTag desc,activityState
150 | ```
151 |
152 | Will return all devices sorted by companyAssetTag in descending order and a secondary sort order of activityState in ascending order.
153 |
154 | Sorting MUST compose with filtering see [Odata 4.01 spec](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#_Toc31361038) for more details.
155 |
156 | ### 6.1. Interpreting a sorting expression
157 |
158 | Sorting parameters MUST be consistent across pages, as both client and server-side paging is fully compatible with sorting.
159 |
160 | If a service does not support sorting by a property named in a _$orderBy_ expression, the service MUST respond with an error message as defined in the Responding to Unsupported Requests section.
161 |
162 | ## 7. Filtering
163 |
164 | The _$filter_ querystring parameter allows clients to filter a collection of resources that are addressed by a request URL.
165 | The expression specified with _$filter_ is evaluated for each resource in the collection, and only items where the expression evaluates to true are included in the response.
166 | Resources for which the expression evaluates to false or to null, or which reference properties that are unavailable due to permissions, are omitted from the response.
167 |
168 | Example: return all devices with activity state equal to 'Active'
169 |
170 | ```http
171 | GET https://graph.microsoft.com/beta/teamwork/devices?$filter=(activityState eq 'Active')
172 | ```
173 |
174 | The value of the _$filter_ option is a Boolean expression.
175 |
176 | ### 7.1. Filter operations
177 |
178 | Services that support _$filter_ SHOULD support the following minimal set of operations.
179 |
180 | Operator | Description | Example
181 | -------------------- | --------------------- | -----------------------------------------------------
182 | Comparison Operators | |
183 | eq | Equal | city eq 'Redmond'
184 | ne | Not equal | city ne 'London'
185 | gt | Greater than | price gt 20
186 | ge | Greater than or equal | price ge 10
187 | lt | Less than | price lt 20
188 | le | Less than or equal | price le 100
189 | Logical Operators | |
190 | and | Logical and | price le 200 and price gt 3.5
191 | or | Logical or | price le 3.5 or price gt 200
192 | not | Logical negation | not price le 3.5
193 | Grouping Operators | |
194 | ( ) | Precedence grouping | (priority eq 1 or city eq 'Redmond') and price gt 100
195 |
196 | Services MUST use the following operator precedence for supported operators when evaluating _$filter_ expressions.
197 | Operators are listed by category in order of precedence from highest to lowest.
198 | Operators in the same category have equal precedence:
199 |
200 | | Group | Operator | Description |
201 | |:----------------|:---------|:----------------------|
202 | | Grouping | ( ) | Precedence grouping |
203 | | Unary | not | Logical Negation |
204 | | Relational | gt | Greater Than |
205 | | | ge | Greater Than or Equal |
206 | | | lt | Less Than |
207 | | | le | Less Than or Equal |
208 | | Equality | eq | Equal |
209 | | | ne | Not Equal |
210 | | Conditional AND | and | Logical And |
211 | | Conditional OR | or | Logical Or |
212 |
213 | ## 8. Pagination
214 |
215 | RESTful APIs that return collections MAY return partial sets.
216 | Consumers of these services MUST expect partial result sets and correctly page through to retrieve an entire set.
217 |
218 | There are two forms of pagination that MAY be supported by RESTful APIs.
219 | Server-driven paging allows servers to even out load across clients and mitigates against denial-of-service attacks by forcibly paginating a request over multiple response payloads.
220 | Client-driven paging enables clients to request only the number of resources that it can use at a given time.
221 |
222 | Sorting and Filtering parameters MUST be consistent across pages, because both client- and server-side paging is fully compatible with both filtering and sorting.
223 |
224 | ### 8.1. Server-driven paging
225 |
226 | Paginated responses MUST indicate a partial result by including a `@odata.nextLink` token in the response.
227 | The absence of a `nextLink` token means that no additional pages are available, see [Odata 4.01 spec](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_ServerDrivenPaging) for more details.
228 |
229 | Clients MUST treat the `nextLink` URL as opaque, which means that query options may not be changed while iterating over a set of partial results.
230 |
231 | Example:
232 |
233 | ```http
234 | GET https://graph.microsoft.com/beta/teamwork/devices
235 | Accept: application/json
236 |
237 | HTTP/1.1 200 OK
238 | Content-Type: application/json
239 |
240 | {
241 | "value": [...],
242 | "@odata.nextLink": "{opaqueUrl}"
243 | }
244 | ```
245 |
246 | ### 8.2. Client-driven paging
247 |
248 | Clients MAY use _$top_ and _$skip_ query parameters to specify a number of results to return and an offset into the collection.
249 |
250 | The server SHOULD honor the values specified by the client; however, clients MUST be prepared to handle responses that contain a different page size or contain a `@odata.nextLink` token.
251 |
252 | When both _$top_ and _$skip_ are given by a client, the server SHOULD first apply _$skip_ and then _$top_ on the collection.
253 |
254 | Note: If the server can't honor _$top_ and/or _$skip_, the server MUST return an error to the client informing about it instead of just ignoring the query options.
255 | This will avoid the risk of the client making assumptions about the data returned.
256 |
257 | Example:
258 |
259 | ```http
260 | GET https://graph.microsoft.com/beta/teamwork/devices?$top=5&$skip=2
261 |
262 | Accept: application/json
263 |
264 | HTTP/1.1 200 OK
265 | Content-Type: application/json
266 |
267 | {
268 | "value": [...]
269 | }
270 | ```
271 |
272 | ### 8.3. Additional considerations
273 |
274 | **Stable order prerequisite:** Both forms of paging depend on the collection of items having a stable order.
275 | The server MUST supplement any specified order criteria with additional sorts (typically by key) to ensure that items are always ordered consistently.
276 |
277 | **Missing/repeated results:** Even if the server enforces a consistent sort order, results MAY be missing or repeated based on creation or deletion of other resources.
278 | Clients MUST be prepared to deal with these discrepancies.
279 | The server SHOULD always encode the record ID of the last read record, helping the client in the process of managing repeated/missing results.
280 |
281 | **Combining client- and server-driven paging:** Note that client-driven paging does not preclude server-driven paging.
282 | If the page size requested by the client is larger than the default page size supported by the server, the expected response would be the number of results specified by the client, paginated as specified by the server paging settings.
283 |
284 | **Page Size:** Clients MAY request server-driven paging with a specific page size by specifying a _$maxpagesize_ preference.
285 | The server SHOULD honor this preference if the specified page size is smaller than the server's default page size.
286 |
287 | **Paginating embedded collections:** It is possible for both client-driven paging and server-driven paging to be applied to embedded collections.
288 | If a server paginates an embedded collection, it MUST include additional `nextLink` tokens as appropriate.
289 |
290 | **Recordset count:** Developers who want to know the full number of records across all pages, MAY include the query parameter _$count=true_ to tell the server to include the count of items in the response.
291 |
292 | ## 9. Compound collection operations
293 |
294 | Filtering, Sorting and Pagination operations MAY all be performed against a given collection.
295 | When these operations are performed together, the evaluation order MUST be:
296 |
297 | 1. **Filtering**. This includes all range expressions performed as an AND operation.
298 | 2. **Sorting**. The potentially filtered list is sorted according to the sort criteria.
299 | 3. **Pagination**. The materialized paginated view is presented over the filtered, sorted list. This applies to both server-driven pagination and client-driven pagination.
300 |
301 | ## 10. Empty Results
302 |
303 | When a filter is performed on a collection and the result set is empty you MUST respond with a valid response body and a 200 response code.
304 | In this example the filters supplied by the client resulted in a empty result set.
305 | The response body is returned as normal and the _value_ attribute is set to a empty collection.
306 | You SHOULD maintain consistency in your API whenever possible.
307 |
308 | ```http
309 | GET https://graph.microsoft.com/beta/teamwork/devices?$filter=('deviceType' eq 'Collab' or companyAssetTa eq 'Tag1')
310 | Accept: application/json
311 |
312 | HTTP/1.1 200 OK
313 | Content-Type: application/json
314 |
315 | {
316 | "value": []
317 | }
318 | ```
319 |
--------------------------------------------------------------------------------
/graph/articles/coreTypes.md:
--------------------------------------------------------------------------------
1 | # Core Types
2 |
3 | ## Overview
4 |
5 | Types exist in Microsoft Graph which are highly-connected/central to the Microsoft Graph ecosystem. Often, these types are in the position of being able to contain structural properties relevant to other APIs, because they are connected to many entities in Microsoft Graph.
6 |
7 | Structural properties should be only added to these core types when they are intrinsic to the entity itself, and strictly not for the purpose of convenience due to the entity's position in Microsoft Graph.
8 |
9 | ## Core Types in Microsoft Graph
10 |
11 | The following types are identified as core types, and will require strong justification to allow new structural properties to be added in all cases.
12 |
13 | - ```user```
14 | - ```group```
15 | - ```device```
16 |
17 | ## Alternatives to Adding Structural Properties
18 |
19 | Instead of adding a structural property to the existing core type (`user`, `group` or `device`), create a new type that models the information captured in the proposed structural property(s).
20 | Then, model the relationship between the existing core type and the new type by adding a navigation property. For information on modeling with navigation properties, see [Navigation Property](../patterns/navigation-property.md).
21 |
22 | ## Example:
23 |
24 | Modeling adding "bank account information", which includes two properties `accountNumber` and `routingNumber`, to entity type ```user```.
25 |
26 | ### Don't:
27 |
28 | Don't add new properties to core types such as `user`.
29 |
30 | ```xml
31 |
32 |
33 |
34 |
35 | ```
36 |
37 | ### Do:
38 |
39 | Model the information by creating a new type and model the relationship to the existing core type with a navigation property. To determine which option is most appropriate, see [Navigation Property](../patterns/navigation-property.md):
40 |
41 | #### Option 1: Add a navigation property on the existing core type to the new type, containing the new type.
42 |
43 | Define the new entity type:
44 | ```xml
45 |
46 |
47 |
48 |
49 | ```
50 |
51 | Add a contained navigation from user to the new entity type:
52 | ```xml
53 |
54 |
55 |
56 | ```
57 |
58 | #### Option 2: Contain the new type in an entity set elsewhere, and add a navigation property to the new type on the existing core type.
59 |
60 | Define the new entity type:
61 | ```xml
62 |
63 |
64 |
65 |
66 | ```
67 |
68 | Contain the new entity type in an entity set or singleton:
69 | ```xml
70 |
71 | ```
72 |
73 | Add a navigation from user to the new type:
74 | ```xml
75 |
76 |
77 |
78 | ```
79 |
80 | #### Option 3: Contain the new type in an entity set elsewhere, and add a navigation property to the existing core type on the new type.
81 |
82 | Define the new entity type, with a navigation to the user:
83 | ```xml
84 |
85 |
86 |
87 |
88 |
89 | ```
90 |
91 | Contain the new entity type in an entity set or singleton:
92 | ```xml
93 |
94 | ```
95 |
--------------------------------------------------------------------------------
/graph/articles/deprecation.md:
--------------------------------------------------------------------------------
1 | # Deprecation guidelines
2 |
3 | If your API requires the introduction of breaking changes, you must add Revisions annotations to the API definition with the following terms:
4 |
5 | - **Date:** Date when the element was marked as deprecated.
6 | - **Version:** Used to organize the ChangeLog. Use the format "YYYY-MM/Category", where "YYYY-MM" is the month the deprecation is announced, and "Category" is the category under which the change is described.
7 | - **Kind:** Deprecated
8 | - **Description:** Human readable description of the change. Used in ChangeLog, documentation, etc.
9 | - **RemovalDate:** Earliest date when the element can be removed.
10 |
11 | The annotation can be applied to a type, an entity set, a singleton, a property, a
12 | navigation property, a function, or an action. If a type is marked as deprecated, it
13 | is not necessary to mark the members of that type as deprecated, nor is it necessary
14 | to annotate any usages of that type.
15 |
16 | ## Example of property annotation
17 |
18 | ```xml
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | ```
33 |
34 | When the request URL contains a reference to a deprecated model element, the gateway adds a [Deprecation header](https://tools.ietf.org/html/draft-dalal-deprecation-header-02) (with the date the element was marked as deprecated) and a Sunset header (with the date of two years beyond the deprecation date) to the response.
35 |
36 | ## Deprecation header example
37 |
38 | ```
39 | Deprecation: Wed, 30 Mar 2022 11:59:59 GMT
40 | Sunset: Thursday, 30 June 2024 23:59:59 GMT
41 | Link: https://docs.microsoft.com/en-us/graph/changelog#2022-03-30_name ; rel="deprecation"; type="text/html"; title="name",https://docs.microsoft.com/en-us/graph/changelog#2022-03-30_state ; rel="deprecation"; type="text/html"; title="state"
42 |
43 | ```
44 |
--------------------------------------------------------------------------------
/graph/articles/errorResponses.md:
--------------------------------------------------------------------------------
1 | # Error condition responses
2 |
3 | For non-success conditions, developers SHOULD be able to write one piece of code that handles errors consistently across different Microsoft REST API services.
4 | This allows building of simple and reliable infrastructure to handle exceptions as a separate flow from successful responses.
5 | The following is based on the OData v4 JSON spec.
6 | However, it is very generic and does not require specific OData constructs.
7 | APIs SHOULD use this format even if they are not using other OData constructs.
8 |
9 | The error response MUST be a single JSON object.
10 | This object MUST have a name/value pair named "error". The value MUST be a JSON object.
11 |
12 | This object MUST contain name/value pairs with the names "code" and "message", and it MAY contain name/value pairs with the names "target", "details" and "innererror."
13 |
14 | The value for the "code" name/value pair is a language-independent string and MUST match the HTTP response status code description, converted to camelCase, as listed in the [Status Code Registry (iana.org)](https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml)
15 | For example, if the HTTP status code is "Not Found", then the "code" value MUST be "notFound".
16 |
17 | Most services will require a larger number of more specific error codes, which are not interesting to all clients.
18 | These error codes SHOULD be exposed in the "innererror" name/value pair as described below.
19 | Introducing a new value for "code" that is visible to existing clients is a breaking change and requires a version increase.
20 | Services can avoid breaking changes by adding new error codes to "innererror" instead.
21 |
22 | The value for the "message" name/value pair MUST be a human-readable representation of the error.
23 | It is intended as an aid to developers and is not suitable for exposure to end users.
24 | Services wanting to expose a suitable message for end users MUST do so through an [odata-json-annotations](https://docs.oasis-open.org/odata/odata-json-format/v4.01/cs02/odata-json-format-v4.01-cs02.html#sec_AnnotateaJSONObject) or custom property.
25 | Services SHOULD NOT localize "message" for the end user, because doing so might make the value unreadable to the app developer who may be logging the value, as well as make the value less searchable on the Internet.
26 |
27 | The value for the "target" name/value pair is the target of the particular error (e.g., the name of the property in error).
28 |
29 | The value for the "details" name/value pair MUST be an array of JSON objects that MUST contain name/value pairs for "code" and "message", and MAY contain a name/value pair for "target", as described above.
30 | The objects in the "details" array usually represent distinct, related errors that occurred during the request.
31 | See example below.
32 |
33 | The value for the "innererror" name/value pair MUST be an object.
34 | The contents of this object are service-defined.
35 | Services wanting to return more specific errors than the root-level code MUST do so by including a name/value pair for "code" and a nested "innererror". Each nested "innererror" object represents a higher level of detail than its parent.
36 | When evaluating errors, clients MUST traverse through all of the nested "innererrors" and choose the deepest one that they understand.
37 | This scheme allows services to introduce new error codes anywhere in the hierarchy without breaking backwards compatibility, so long as old error codes still appear.
38 | The service MAY return different levels of depth and detail to different callers.
39 | For example, in development environments, the deepest "innererror" MAY contain internal information that can help debug the service.
40 | To guard against potential security concerns around information disclosure, services SHOULD take care not to expose too much detail unintentionally.
41 | Error objects MAY also include custom server-defined name/value pairs that MAY be specific to the code.
42 | Error types with custom server-defined properties SHOULD be declared in the service's metadata document.
43 | See example below.
44 |
45 | Error responses MAY contain Odata JSON annotations in any of their JSON objects.
46 |
47 | We recommend that for any transient errors that may be retried, services SHOULD include a Retry-After HTTP header indicating the minimum number of seconds that clients SHOULD wait before attempting the operation again.
48 |
49 | ## ErrorResponse : Object
50 |
51 | Property | Type | Required | Description
52 | -------- | ---- | -------- | -----------
53 | `error` | Error | ✔ | The error object.
54 |
55 | ## Error : Object
56 |
57 | Property | Type | Required | Description
58 | -------- | ---- | -------- | -----------
59 | `code` | String | ✔ | One of a server-defined set of error codes.
60 | `message` | String | ✔ | A human-readable representation of the error.
61 | `target` | String | | The target of the error.
62 | `details` | Error[] | | An array of details about specific errors that led to this reported error.
63 | `innererror` | InnerError | | An object containing more specific information than the current object about the error.
64 |
65 | ## InnerError : Object
66 |
67 | Property | Type | Required | Description
68 | -------- | ---- | -------- | -----------
69 | `code` | String | | A more specific error code than was provided by the containing error.
70 | `innererror` | InnerError | | An object containing more specific information than the current object about the error.
71 |
72 | ## Examples
73 |
74 | Example of "innererror":
75 |
76 | ```json
77 | {
78 | "error": {
79 | "code": "unauthorized",
80 | "message": "Previous passwords may not be reused",
81 | "target": "password",
82 | "innererror": {
83 | "code": "passwordError",
84 | "innererror": {
85 | "code": "passwordDoesNotMeetPolicy",
86 | "minLength": "6",
87 | "maxLength": "64",
88 | "characterTypes": ["lowerCase","upperCase","number","symbol"],
89 | "minDistinctCharacterTypes": "2",
90 | "innererror": {
91 | "code": "passwordReuseNotAllowed"
92 | }
93 | }
94 | }
95 | }
96 | }
97 | ```
98 |
99 | In this example, the most basic error code is "unauthorized", but for clients that are interested, there are more specific error codes in "innererror."
100 | The "passwordReuseNotAllowed" code may have been added by the service at a later date, having previously only returned "passwordDoesNotMeetPolicy."
101 | Existing clients do not break when the new error code is added, but new clients MAY take advantage of it.
102 | The "passwordDoesNotMeetPolicy" error also includes additional name/value pairs that allow the client to determine the server's configuration, validate the user's input programmatically, or present the server's constraints to the user within the client's own localized messaging.
103 |
104 | Example of "details":
105 |
106 | ```json
107 | {
108 | "error": {
109 | "code": "badRequest",
110 | "message": "Multiple errors in ContactInfo data",
111 | "target": "contactInfo",
112 | "details": [
113 | {
114 | "code": "nullValue",
115 | "target": "phoneNumber",
116 | "message": "Phone number must not be null"
117 | },
118 | {
119 | "code": "nullValue",
120 | "target": "lastName",
121 | "message": "Last name must not be null"
122 | },
123 | {
124 | "code": "malformedValue",
125 | "target": "address",
126 | "message": "Address is not valid"
127 | }
128 | ]
129 | }
130 | }
131 | ```
132 |
133 | In this example there were multiple problems with the request, with each individual error listed in "details."
134 |
--------------------------------------------------------------------------------
/graph/articles/filter-as-segment.md:
--------------------------------------------------------------------------------
1 | # Filter as segment
2 |
3 | There is an [OData feature](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_AddressingaSubsetofaCollection) which allows having a `$filter` in a URL segment.
4 | This feature is useful whenever there are operations on a collection and the client wants to perform those operations on a *subset* of the collection.
5 | For example, the `riskyUsers` API on Microsoft Graph has an action defined to let clients "dismiss" risky users (i.e. consider those users "not risky"):
6 |
7 | ```xml
8 |
9 |
10 |
11 |
12 | ```
13 |
14 | Using this action, clients can call
15 |
16 | ```http
17 | POST /identityProtection/riskyUsers/dismiss
18 | {
19 | "userIds": [
20 | "{userId1}",
21 | "{userId2}",
22 | ...
23 | ]
24 | }
25 | ```
26 |
27 | in order to dismiss the risky users with the provided IDs. Using the filter-as-segment OData feature, the action could instead be defined as:
28 |
29 | ```xml
30 |
31 |
32 |
33 | ```
34 |
35 | and clients could call
36 |
37 | ```http
38 | POST /identityProtection/riskyUsers/$filter=@f/dismiss?@f=id IN ('{userId1}','{userId2}',...)
39 | ```
40 |
41 | Doing this is beneficial due to the robust nature of OData filter expressions: clients will be able to dismiss risky users based on any supported filter without the service team needing to implement a new `dismiss` overload that filters based on the new criteria.
42 | However, there are some concerns about the discoverability of using the filter-as-segment feature, as well as the support of [parameter aliasing](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_ParameterAliases) that's required.
43 | As a result, functions should be introduced that act in the same way as the filter-as-segment:
44 |
45 | ```xml
46 |
47 |
48 |
49 |
50 |
51 | ```
52 |
53 | Clients would now be able to call
54 |
55 | ```http
56 | POST /identityProtection/riskyUsers/filter(expression='id IN (''{userId1}'',''{userId2}'',...)')/dismiss
57 | ```
58 |
59 | NOTE: the `'` literal in the filter expression must be escaped with `''`
60 |
61 | An example implementation of a filter function using OData WebApi can be found [here](https://github.com/OData/AspNetCoreOData/commit/7732f7e6b812d9a79a73529562f2e74b68e2794f).
62 |
--------------------------------------------------------------------------------
/graph/articles/naming.md:
--------------------------------------------------------------------------------
1 | # Naming
2 |
3 | ## 1. Approach
4 |
5 | Naming policies should aid developers in discovering functionality without having to constantly refer to documentation.
6 | Use of common patterns and standard conventions greatly aids developers in correctly guessing common property names and meanings.
7 | Services SHOULD use verbose naming patterns and MUST NOT use abbreviations other than acronyms that are the dominant mode of expression in the domain being represented by the API, (e.g. Url).
8 |
9 | ## 2. Casing
10 |
11 | - Acronyms SHOULD follow the casing conventions as though they were regular words (e.g. Url).
12 | - All identifiers including namespaces, entityTypes, entitySets, properties, actions, functions and enumeration values MUST use lowerCamelCase.
13 | - HTTP headers are the exception and SHOULD use standard HTTP convention of Capitalized-Hyphenated-Terms.
14 |
15 | ## 3. Names to avoid
16 |
17 | Certain names are so overloaded in API domains that they lose all meaning or clash with other common usages in domains that cannot be avoided when using REST APIs, such as OAUTH.
18 | Services SHOULD NOT use the following names:
19 |
20 | - Context
21 | - Scope
22 | - Resource
23 |
24 | ## 4. Forming compound names
25 |
26 | - Services SHOULD avoid using articles such as 'a', 'the', 'of' unless needed to convey meaning.
27 | - e.g. names such as aUser, theAccount, countOfBooks SHOULD NOT be used, rather user, account, bookCount SHOULD be preferred.
28 | - Services SHOULD add a type to a property name when not doing so would cause ambiguity about how the data is represented or would cause the service not to use a common property name.
29 | - When adding a type to a property name, services MUST add the type at the end, e.g. createdDateTime.
30 |
31 | ## 5. Identity properties
32 |
33 | - Services MUST use string types for identity properties.
34 | - For OData services, the service MUST use the OData @id property to represent the canonical identifier of the resource.
35 | - Services MAY use the simple 'id' property to represent a local or legacy primary key value for a resource.
36 | - Services SHOULD use the name of the relationship postfixed with 'Id' to represent a foreign key to another resource, e.g. subscriptionId.
37 | - The content of this property SHOULD be the canonical ID of the referenced resource.
38 |
39 | ## 6. Date and time properties
40 |
41 | - For properties requiring both date and time, services MUST use the suffix 'DateTime'.
42 | - For properties requiring only date information without specifying time, services MUST use the suffix 'Date', e.g. birthDate.
43 | - For properties requiring only time information without specifying date, services MUST use the suffix 'Time', e.g. appointmentStartTime.
44 |
45 | ## 7. Name properties
46 |
47 | - For the overall name of a resource typically shown to users, services MUST use the property name 'displayName'.
48 | - Services MAY use other common naming properties, e.g. givenName, surname, signInName.
49 |
50 | ## 8. Collections and counts
51 |
52 | - Services MUST name collections as plural nouns or plural noun phrases using correct English.
53 | - Services MAY use simplified English for nouns that have plurals not in common verbal usage.
54 | - e.g. schemas MAY be used instead of schemata.
55 | - Services MUST name counts of resources with a noun or noun phrase suffixed with 'Count'.
56 |
57 | ## 9. Common property names
58 |
59 | Where services have a property, whose data matches the names below, the service MUST use the name from this table.
60 | This table will grow as services add terms that will be more commonly used.
61 | Service owners adding such terms SHOULD propose additions to this document.
62 |
63 | | | |
64 | |-------------------- | - |
65 | attendees |
66 | body |
67 | completedDateTime | **NOTE** completionDateTime may be used for cases where the timestamp represents a point in the future |
68 | createdDateTime |
69 | childCount |
70 | children |
71 | contentUrl |
72 | country |
73 | createdBy |
74 | displayName |
75 | errorUrl |
76 | eTag |
77 | event |
78 | expirationDateTime |
79 | givenName |
80 | jobTitle |
81 | kind |
82 | id |
83 | lastModifiedDateTime |
84 | location |
85 | memberOf |
86 | message |
87 | name |
88 | owner |
89 | people |
90 | person |
91 | postalCode |
92 | photo |
93 | preferredLanguage |
94 | properties |
95 | signInName |
96 | surname |
97 | tags |
98 | userPrincipalName |
99 | webUrl |
100 |
--------------------------------------------------------------------------------
/graph/articles/nullable.md:
--------------------------------------------------------------------------------
1 | # Nullable Properties
2 |
3 | A nullable property means *only* that the property may have `null` as a value; the "nullability" of a property does not say anything about how a value is set into a property.
4 | For example, a non-nullable property is *not* required to create a new instance of an entity.
5 | It only means that the property will have a value when it is retrieved.
6 | In the case that no value is provided when the entity is created, this means that the service will create one; this value can be specified with the `DefaultValue` attribute, but if the value is contextual and determine at request time, then the property can both be non-nullable *and* have no `DefaultValue` specified.
7 | Below are some examples of nullable and non-nullable properties.
8 |
9 | ## CSDL
10 |
11 | ```xml
12 |
13 | ...
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ...
24 |
25 | ```
26 |
27 | ## HTTP Requests
28 |
29 | ### 1. Create a servicePrincipal with no properties
30 |
31 | ```HTTP
32 | POST /servicePrincipals
33 |
34 | 400 Bad Request
35 | {
36 | "error": {
37 | "code": "badRequest",
38 | "message": "The 'appId' property is required to create a servicePrincipal."
39 | }
40 | }
41 | ```
42 |
43 | ### 2. Create a servicePrincipal without a display name
44 |
45 | ```HTTP
46 | POST /servicePrincipals
47 | {
48 | "appId": "00000000-0000-0000-0000-000000000001"
49 | }
50 |
51 | 201 Created
52 | {
53 | "appId": "00000000-0000-0000-0000-000000000001",
54 | "displayName": "some application name",
55 | "foo": "testval",
56 | "bar": "differentvalue",
57 | ...
58 | }
59 | ```
60 | Notes:
61 | 1. `displayName` was given a value by the service even though no value was provided by the client
62 | 2. `foo` has the default value as specified by its `DefaultValue` attribute in the CSDL
63 | 3. `bar` has the default value as specified by its `DefaultValue` attribute in the CSDL
64 |
65 | ### 3. Update the display name of a service principal to null
66 |
67 | ```HTTP
68 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
69 | {
70 | "displayName": null
71 | }
72 |
73 | 400 Bad Request
74 | {
75 | "error": {
76 | "code": "badRequest",
77 | "message": "null is not a valid value for the property 'displayName'; 'displayName' is not a nullable property."
78 | }
79 | }
80 | ```
81 | Notes:
82 | 1. `displayName` cannot be set to `null` because it has be marked with `Nullable="false"` in the CSDL.
83 |
84 | ### 4. Update the display name of a service principal
85 |
86 | ```HTTP
87 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
88 | {
89 | "displayName": "a non-generated display name"
90 | }
91 |
92 | 200 OK
93 | {
94 | "appId": "00000000-0000-0000-0000-000000000001",
95 | "displayName": "a non-generated display name",
96 | "foo": "testval",
97 | "bar": "differentvalue",
98 | ...
99 | }
100 | ```
101 | Notes:
102 | 1. `displayName` can be set to any value other than `null`
103 | 2. The response body here is provided for clarity, and is not part of the guidance itself. The [OData v4.01 standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_UpdateanEntity) states that the workload can decide the behavior.
104 |
105 | ### 5. Update the foo property of a service principal to null
106 |
107 | ```HTTP
108 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
109 | {
110 | "foo": null
111 | }
112 |
113 | 200 OK
114 | {
115 | "appId": "00000000-0000-0000-0000-000000000001",
116 | "displayName": "a non-generated display name",
117 | "foo": null,
118 | "bar": "differentvalue",
119 | ...
120 | }
121 | ```
122 | Notes:
123 | 1. `foo` can be set to `null` because it has be marked with `Nullable="true"` in the CSDL.
124 | 2. The response body here is provided for clarity, and is not part of the guidance itself. The [OData v4.01 standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_UpdateanEntity) states that the workload can decide the behavior.
125 |
126 | ### 6. Update the foo property of a service principal to a non-default value
127 |
128 | ```HTTP
129 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
130 | {
131 | "foo": "something other than testval"
132 | }
133 |
134 | 200 OK
135 | {
136 | "appId": "00000000-0000-0000-0000-000000000001",
137 | "displayName": "a non-generated display name",
138 | "foo": "something other than testval",
139 | "bar": "differentvalue",
140 | ...
141 | }
142 | ```
143 | Notes:
144 | 1. `foo` can be set to `something other than testval`
145 | 2. The response body here is provided for clarity, and is not part of the guidance itself. The [OData v4.01 standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_UpdateanEntity) states that the workload can decide the behavior.
146 |
147 | ### 7. Update the bar property of a service principal to null
148 |
149 | ```HTTP
150 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
151 | {
152 | "bar": null
153 | }
154 |
155 | 400 Bad Request
156 | {
157 | "error": {
158 | "code": "badRequest",
159 | "message": "null is not a valid value for the property 'bar'; 'bar' is not a nullable property."
160 | }
161 | }
162 | ```
163 | Notes:
164 | 1. `bar` cannot be set to `null` because it has be marked with `Nullable="false"` in the CSDL.
165 |
166 | ### 8. Update the bar property of a service principal to a non-default value
167 |
168 | ```HTTP
169 | PATCH /servicePrincipals/00000000-0000-0000-0000-000000000001
170 | {
171 | "bar": "a new bar"
172 | }
173 |
174 | 200 OK
175 | {
176 | "appId": "00000000-0000-0000-0000-000000000001",
177 | "displayName": "a non-generated display name",
178 | "foo": "something other than testval",
179 | "bar": "a new bar",
180 | ...
181 | }
182 | ```
183 | Notes:
184 | 1. `bar` can be set to `a new bar`
185 | 2. The response body here is provided for clarity, and is not part of the guidance itself. The [OData v4.01 standard](https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_UpdateanEntity) states that the workload can decide the behavior.
186 |
187 | ### 9. Create a service principal while customizing the display name
188 | ```HTTP
189 | POST /servicePrincipals
190 | {
191 | "appId": "00000000-0000-0000-0000-000000000001",
192 | "displayName": "a different name"
193 | }
194 |
195 | 201 Created
196 | {
197 | "appId": "00000000-0000-0000-0000-000000000001",
198 | "displayName": "a different name",
199 | "foo": "testval",
200 | "bar": "differentvalue",
201 | ...
202 | }
203 | ```
204 | Notes:
205 | 1. `displayName` isn't required to create a new `servicePrincipal`, but it *can* be provided; this is orthogonal to whether or not the property has `Nullable="true"` or `Nullable="false"`.
206 | 2. `foo` has the default value as specified by its `DefaultValue` attribute in the CSDL
207 | 3. `bar` has the default value as specified by its `DefaultValue` attribute in the CSDL
208 |
209 | ### 10. Create a service principal with a null display name
210 | ```HTTP
211 | POST /servicePrincipals
212 | {
213 | "appId": "00000000-0000-0000-0000-000000000001",
214 | "displayName": null
215 | }
216 |
217 | 400 Bad Request
218 | {
219 | "error": {
220 | "code": "badRequest",
221 | "message": "null is not a valid value for the property 'displayName'; 'displayName' is not a nullable property."
222 | }
223 | }
224 | ```
225 | Notes:
226 | 1. `displayName` isn't required to create a new `servicePrincipal`, but it *can* be provided; it *cannot* be provided as `null` because the property was marked with `Nullable="false"`
227 |
228 | ### 11. Create a service principal with a value for the foo property
229 | ```HTTP
230 | POST /servicePrincipals
231 | {
232 | "appId": "00000000-0000-0000-0000-000000000001",
233 | "foo": "a foo value on creation"
234 | }
235 |
236 | 201 Created
237 | {
238 | "appId": "00000000-0000-0000-0000-000000000001",
239 | "displayName": "some application name",
240 | "foo": "a foo value on creation",
241 | "bar": "differentvalue",
242 | ...
243 | }
244 | ```
245 | Notes:
246 | 1. `displayName` was given a value by the service even though no value was provided by the client
247 | 2. `foo` isn't required to create a new `servicePrincipal`, but it *can* be provided; this is orthogonal to whether or not the property has `Nullable="true"` or `Nullable="false"`.
248 | 3. `bar` has the default value as specified by its `DefaultValue` attribute in the CSDL
249 |
250 | ### 12. Create a service principal with null for the foo property
251 | ```HTTP
252 | POST /servicePrincipals
253 | {
254 | "appId": "00000000-0000-0000-0000-000000000001",
255 | "foo": null
256 | }
257 |
258 | 201 Created
259 | {
260 | "appId": "00000000-0000-0000-0000-000000000001",
261 | "displayName": "some application name",
262 | "foo": null,
263 | "bar": "differentvalue",
264 | ...
265 | }
266 | ```
267 | Notes:
268 | 1. `displayName` was given a value by the service even though no value was provided by the client
269 | 2. `foo` isn't required to create a new `servicePrincipal`, but it *can* be provided; because the property has `Nullable="true"`, a `null` value can be provided for it.
270 | 3. `bar` has the default value as specified by its `DefaultValue` attribute in the CSDL
271 |
272 | ### 13. Create a service principal with a value for the bar property
273 | ```HTTP
274 | POST /servicePrincipals
275 | {
276 | "appId": "00000000-0000-0000-0000-000000000001",
277 | "bar": "running out of ideas for value names"
278 | }
279 |
280 | 201 Created
281 | {
282 | "appId": "00000000-0000-0000-0000-000000000001",
283 | "displayName": "some application name",
284 | "foo": "testval",
285 | "bar": "running out of ideas for value names",
286 | ...
287 | }
288 | ```
289 | Notes:
290 | 1. `displayName` was given a value by the service even though no value was provided by the client
291 | 2. `foo` has the default value as specified by its `DefaultValue` attribute in the CSDL
292 | 3. `bar` isn't required to create a new `servicePrincipal`, but it *can* be provided; this is orthogonal to whether or not the property has `Nullable="true"` or `Nullable="false"`.
293 |
294 | ### 14. Create a service principal with null for the bar property
295 | ```HTTP
296 | POST /servicePrincipals
297 | {
298 | "appId": "00000000-0000-0000-0000-000000000001",
299 | "bar": null
300 | }
301 |
302 | 400 Bad Request
303 | {
304 | "error": {
305 | "code": "badRequest",
306 | "message": "null is not a valid value for the property 'bar'; 'bar' is not a nullable property."
307 | }
308 | }
309 | ```
310 | Notes:
311 | 1. `bar` isn't required to create a new `servicePrincipal`, but it *can* be provided; it *cannot* be provided as `null` because the property was marked with `Nullable="false"`
312 |
--------------------------------------------------------------------------------
/graph/patterns/LRO.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/api-guidelines/3db3462a9361132adf60da74cff3aced08eb9ff7/graph/patterns/LRO.gif
--------------------------------------------------------------------------------
/graph/patterns/PatternDescriptionTemplate.md:
--------------------------------------------------------------------------------
1 | # Pattern name
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *Provide a short description of the pattern.*
6 |
7 |
8 | ## Problem
9 |
10 | *Describe the business context relevant for the pattern.*
11 |
12 | *Provide a short description of the problem.*
13 |
14 | ## Solution
15 |
16 | *Describe how to implement the solution to solve the problem.*
17 |
18 | *Describe related patterns.*
19 |
20 | ## When to use this pattern
21 |
22 | *Describe when and why the solution is applicable and when it might not be.*
23 |
24 | ## Issues and considerations
25 |
26 | *Describe tradeoffs of the solution.*
27 |
28 | ## Example
29 |
30 | *Provide a short example from real life.*
31 |
--------------------------------------------------------------------------------
/graph/patterns/RELO.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/api-guidelines/3db3462a9361132adf60da74cff3aced08eb9ff7/graph/patterns/RELO.gif
--------------------------------------------------------------------------------
/graph/patterns/alternate-key.md:
--------------------------------------------------------------------------------
1 | # Alternate key
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The alternate key pattern provides the ability to query for a single, specific resource identifiable via one of an alternative set of properties that is not its primary key.*
6 |
7 | ## Problem
8 |
9 | The resources exposed in Microsoft Graph are identified through a primary key, which guarantees uniqueness inside the same resource collection. Often though, that same resource can also be uniquely identified by an alternative, more convenient property that provides a better developer experience.
10 |
11 | Take a look at the `user` resource: while the `id` is the typical way to get the resource details, the `mail` address is also a unique property that can be used to identify it.
12 |
13 | The resource can be accessed using the `$filter` query parameter, such as
14 |
15 | ```http
16 | GET https://graph.microsoft.com/v1.0/users?$filter=mail eq 'bob@contoso.com'
17 | ```
18 | However, in this case, the returned result is wrapped in an array that needs to be unpacked. When the uniqueness of the property within the collection implies that only zero or one results can be returned from the call this array provides a suboptimal experience for callers.
19 |
20 | ## Solution
21 |
22 | Typically resources in Graph are accessed using a simple forward-slash delimited URL pattern (this pattern is sometimes referred to as key-as-segment).
23 |
24 | ```http
25 | https://graph.microsoft.com/v1.0/users/0 - Retrieves the employee with ID = 0.
26 | ```
27 |
28 | However, resources can also be accessed using parentheses to delimit the key, like this:
29 |
30 | ```http
31 | https://graph.microsoft.com/v1.0/users(0) - Also retrieves the employee with ID = 0.
32 | ```
33 |
34 | Resource addressing by using an alternative key can be achieved by using this same parentheses-style convention with one difference: alternate keys MUST specify the key property name to unambiguously determine the alternate key, like this:
35 |
36 | ```http
37 | https://graph.microsoft.com/v1.0/users(email='bob@contoso.com') Retrieves the employee with the email matching `bob@contoso.com`.
38 | ```
39 |
40 | In the same way as requesting a resource via the canonical key, if a resource cannot be located that matches the alternate key, then a 404 must be returned.
41 |
42 | > **Note:** When requesting a resource via alternate keys, the simple slash-delimited URL style does not work.
43 |
44 | > **Note:** Do not use multi-part alternate keys. Feedback has been that customers find multi-part keys confusing.
45 | > Either create a composite single-part surrogate key property or fall back to logical operations in a $filter clause.
46 |
47 | ## When to use this pattern
48 |
49 | Use this pattern when your resource type has other keys than its canonical key which uniquely identify a single resource.
50 |
51 | ## Example
52 |
53 | The same user is identified via the alternate key SSN, the canonical (primary) key ID using the non-canonical long form with a specified key property name, and the canonical short form without a key property name.
54 |
55 | Declare `mail` and `ssn` as alternate keys on an entity:
56 |
57 | ```xml
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | ```
90 |
91 | 1. Get a specific resource through `$filter`:
92 |
93 | ```http
94 | GET https://graph.microsoft.com/v1.0/users/?$filter=ssn eq '123-45-6789'
95 | ```
96 |
97 | ```json
98 | {
99 | "value": [
100 | {
101 | "givenName": "Bob",
102 | "jobTitle": "Retail Manager",
103 | "mail": "bob@contoso.com",
104 | "mobilePhone": "+1 425 555 0109",
105 | "officeLocation": "18/2111",
106 | "preferredLanguage": "en-US",
107 | "ssn": "123-45-6789",
108 | "surname": "Vance",
109 | "userPrincipalName": "bob@contoso.com",
110 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66"
111 | }
112 | ]
113 | }
114 | ```
115 |
116 | 2. Get a specific resource either through its primary key or through the two alternate keys:
117 |
118 | ```http
119 | GET https://graph.microsoft.com/v1.0/users/1a89ade6-9f59-4fea-a139-23f84e3aef66
120 | GET https://graph.microsoft.com/v1.0/users(1a89ade6-9f59-4fea-a139-23f84e3aef66)
121 | GET https://graph.microsoft.com/v1.0/users(ssn='123-45-6789')
122 | GET https://graph.microsoft.com/v1.0/users(mail='bob@contoso.com')
123 | ```
124 |
125 | All four yield the same response:
126 |
127 | ```json
128 | {
129 | "givenName": "Bob",
130 | "jobTitle": "Retail Manager",
131 | "mail": "bob@contoso.com",
132 | "mobilePhone": "+1 425 555 0109",
133 | "officeLocation": "18/2111",
134 | "preferredLanguage": "en-US",
135 | "ssn": "123-45-6789",
136 | "surname": "Vance",
137 | "userPrincipalName": "bob@contoso.com",
138 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66"
139 | }
140 | ```
141 |
142 | 3. Request a resource for an unsupported alternate key property:
143 |
144 | ```http
145 | GET https://graph.microsoft.com/v1.0/users(name='Bob')
146 |
147 | 400 Bad Request
148 | {
149 | "error" : {
150 | "code" : "400",
151 | "message": "'name' is not a valid alternate key for the resource type 'user'."
152 | }
153 | }
154 | ```
155 |
156 | 4. Request a resource where the alternate key property does not exist on any resource in the collection:
157 |
158 | ```http
159 | GET https://graph.microsoft.com/v1.0/users(email='unknown@contoso.com')
160 |
161 | 404 Not Found
162 | {
163 | "error" : {
164 | "code" : "404",
165 | "message": "No user with the the specified 'email' could be found."
166 | }
167 | }
168 | ```
169 |
--------------------------------------------------------------------------------
/graph/patterns/antiPatternTemplate.md:
--------------------------------------------------------------------------------
1 |
2 | # Antipattern name
3 |
4 | *name with a negative connotation*
5 |
6 | *Example: Flatbag of properties*
7 |
8 | ## Description
9 |
10 | *Example: The flat bag pattern is a known anti-pattern in Microsoft Graph, where multiple variants of a common concept are modeled as a single entity type with all potential properties plus an additional property to distinguish the variants.*
11 |
12 | ## Consequences
13 | *Describe the consequences in terms of the developer experience*
14 |
15 | *Example: This is the least recommended modeling choice because it is weakly typed, which increases the number of variations and complexity of solutions, making it difficult to verify the semantic correctness of the API for both clients and producers...*
16 |
17 | ## Preferable solutions
18 |
19 | *Example: It is preferable to use type hierarchy and facets patterns.
20 | Should provide a link to a valid pattern or patterns.*
21 |
22 | ## Example
23 |
24 | *Provide an example of better modeling*
25 |
--------------------------------------------------------------------------------
/graph/patterns/change-tracking.md:
--------------------------------------------------------------------------------
1 | # Change tracking
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The change tracking pattern provides the ability for API consumers to request changes in data from Microsoft Graph without having to re-read data that has not changed.*
6 |
7 |
8 | ## Problem
9 |
10 | API consumers require an efficient way to acquire changes to data in the Microsoft Graph, for example to synchronize an external store or to drive a change-centric business process.
11 |
12 | ## Solution
13 |
14 | API designers can enable the change tracking (delta) capability on a resource in the Microsoft Graph (typically on an entity collection or a parent resource) by declaring a delta function on that resource and applying `Org.OData.Capabilities.V1.ChangeTracking` annotation.
15 |
16 | This function returns a delta payload. A delta payload consists of a collection of annotated full or partial Microsoft Graph entities plus either a `nextLink` to further pages of original or change data that are immediately available OR a `deltaLink` to get the next set of changes at some later date.
17 |
18 | The `nextLink` provides a mechanism to do server-driven paging through the change data that is currently available. When there are no further pages of changes immediately available, a `deltaLink` is returned instead.
19 | The `deltaLink` provides a mechanism for the API consumer to catch up on changes since their last request to the delta function. If no changes have happened since the last request, then the deltaLink MUST return an empty collection.
20 |
21 | Both `nextLink` and `deltaLink` MUST be considered opaque URLs. The best practice is to make them opaque via encoding.
22 |
23 | The pattern requires a sequence of requests on the delta function, for additional details see [Change Tracking](https://learn.microsoft.com/en-us/graph/delta-query-overview?tabs=http#use-delta-query-to-track-changes-in-a-resource-collection):
24 |
25 | 1. GET request which returns the first page of the current state of the resources that delta applies to.
26 | 2. [Optionally] Further GET requests to retrieve more pages of the current state via the `@odata.nextLink` URL.
27 | 3. After some time, a GET request to see if there are new changes via the `@odata.deltaLink` URL.
28 | 4. [Optionally] GET requests to retrieve more pages of changes via the `@odata.nextLink` URL.
29 |
30 | Delta payload requirements:
31 | - The payload is a collection of change records using the collection format.
32 | - The change records are full or partial representations of the resources according to their resource types.
33 | - When a change representing a resource update is included in the payload the API producer MAY return either the changed properties or the full entity. The ID of the resource MUST be included in every change record.
34 | - When an entity is deleted, the delta function MUST return the ID of the deleted entity as well as an `@removed` annotation with the reason field.
35 | - When an entity is deleted, the reason MUST be set to “changed” if the entity can be restored.
36 | - When an entity is deleted. the reason MUST be set to “deleted” if the entity cannot be restored.
37 | - There is no mechanism to indicate that a resource has entered or exited the dataset based on a change that causes it to match or no longer match any `$filter` query parameter.
38 | - When a link to an entity is deleted, when the linked entity is deleted, or when a link to an entity is added, the implementer MUST return a `property@delta` annotation.
39 | - When a link to an entity is deleted, but the entity still exists, the reason MUST be set to `changed`.
40 | - When a link to an entity is deleted along with the entity, the reason MUST be set to `deleted`.
41 |
42 | API producers MAY choose to collate multiple changes to the same resource into a single change record.
43 |
44 | API consumers are expected to differentiate resource adds from updates by interpreting the id property of the change records against the existence of resources in whatever external system is doing the processing.
45 |
46 |
47 | ## When to use this pattern
48 |
49 | API consumers want a pull mechanism to request and process change to Microsoft Graph data, either via proactive polling or by responding to Microsoft Graph notifications.
50 |
51 | API consumers need guaranteed data integrity over the set of changes to Microsoft Graph data.
52 |
53 | ## Considerations
54 |
55 | - API service MAY be able to respond to standard OData query parameters with the initial call to the delta function:
56 |
57 | - `$select` to enforce the set of properties on which change is reported.
58 | - `$filter` to influence the scope of changes returned.
59 | - `$expand` to include linked resources with the set of changes.
60 | - `$top` parameter to influence the size of the set of change records.
61 |
62 | These query parameters MUST be encoded into subsequent `@odata.nextLink` or `@odata.deltaLink`, such that the same options are preserved through the call sequence without callers respecifying them, which MUST NOT be allowed. OData query parameters must be honored in full, or a 400-error returned.
63 | - Making a sequence of calls to a delta function followed by the opaque URLs in the `nextLink` and `deltaLink` MUST guarantee that the data at the start time of the call sequence and all changes to the data thereafter will be returned at least once. It is not necessary to avoid duplicates in the sequence. When the delta function is returning changes, they MUST be sequenced chronologically refer to [public documentation](https://learn.microsoft.com/en-us/graph/delta-query-overview?view=graph-rest-1.0) for more details.
64 | - The delta function can be bound to
65 | - an entity collection, as with `/users/delta` that returns the changes to the users' collection, or
66 | - some logical parent resource that returns an entity collection, where the change records are implied to be relative to all collections contained within the parent.For example `/me/planner/all/delta` returns changes to any resource within a planner, which are referenced by 'all' navigation property, and `/communications/onlineMeetings/getAllRecordings/delta` returns changes to any meeting recordings returned by `getAllRecordings` function.
67 |
68 | - API service should use `$skipToken` and `$deltaToken` within their implementations of `nextLink` and `deltaLink`, however the URLs are defined as being opaque and the existence of the tokens MUST NOT be documented. It is not a breaking change to modify the structure of `nextLinks` or `deltaLinks`.-
69 | - `nextLink` and `deltaLink` URLs are valid for a specific period before the client application needs to run a full synchronization again.For `nextLink`, a minimal validity time should be 1 hour. For `deltaLink`, a minimal validity time should be seven days. When a link is no longer valid it must return a standard error with a 410 GONE response code.
70 | - Although this capability is similar to the OData `$delta` feed capability, it is a different construct. Microsoft Graph APIs MUST provide change tracking through the delta function and MUST NOT implement the OData `$delta` feed when providing change tracking capabilities to ensure the uniformity of the API experience.
71 | - The Graph delta payload format has some deviations from the OData 4.01 change tracking format to simplify parsing, for example the context annotation is removed.
72 | - Additional implementation details are documented [internally](https://dev.azure.com/msazure/One/_wiki/wikis/Microsoft%20Graph%20Partners/211718/Deltas).
73 |
74 |
75 | ## Alternatives
76 |
77 | - Change notifications pattern with rich payloads – for use cases where API consumers would find calling back into Microsoft Graph onerous and absolute integrity guarantees are less critical.
78 |
79 |
80 | ## Examples
81 |
82 | ### Change tracking on entity set
83 |
84 | ```xml
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | ```
97 |
98 | ### Change tracking on navigation property
99 |
100 | ```xml
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | ```
120 | ### Change tracking on function that return an entity collection
121 |
122 | Firstly, an API designer needs to define the function as composable (so that a delta function can be added to it), by adding the `IsComposable` annotation:
123 |
124 | ```xml
125 |
126 |
127 |
128 |
129 | ```
130 |
131 | Next, define the `delta` function. The binding parameter and the return type of the delta function MUST be the same as the return type of the target `getAllRecordings` function:
132 |
133 | ```xml
134 |
135 |
136 |
137 |
138 | ```
139 | Finally, for the function, the designer needs to add an annotation (either as a child of the entity or by targeting the entity type as below) stating that it supports change tracking (delta query):
140 |
141 | ```xml
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | ```
150 | Here is the HTTP request to start the change tracking process on `getAllRecordings`
151 |
152 | ```http
153 | GET https://graph.microsoft.com/v1.0/communications/onlineMeetings/getAllRecordings/delta
154 | ```
155 |
156 | ### Delta payload
157 |
158 | Here after the initial delta call, a user resource is updated, and there is one user added to and one removed from that user’s directReports collection. Additionally, a second user is deleted. In this case, there are no further pages of change records currently available. For detailed sequence of requests see [Change Tracking](https://learn.microsoft.com/en-us/graph/delta-query-overview?tabs=http#use-delta-query-to-track-changes-in-a-resource-collection).
159 |
160 | ```http
161 | GET https://graph.microsoft.com/v1.0/users/delta?$skiptoken=pqwSUjGYvb3jQpbwVAwEL7yuI3dU1LecfkkfLPtnIjvB7XnF_yllFsCrZJ
162 |
163 | {
164 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users",
165 | "@odata.deltaLink": "https://graph.microsoft.com/v1.0/users/delta?$deltatoken=mS5DuRZGjVL-abreviated",
166 | "value": [
167 | {
168 | "businessPhones": ["+1 309 555 0104"],
169 | "displayName": "Grady Archie",
170 | "givenName": "Grady",
171 | "jobTitle": "Designer",
172 | "mail": "GradyA@contoso.onmicrosoft.com",
173 | "officeLocation": "19/2109",
174 | "preferredLanguage": "en-US",
175 | "surname": "Archie",
176 | "userPrincipalName": "GradyA@contoso.onmicrosoft.com",
177 | "id": "0baaae0f-b0b3-4645-867d-742d8fb669a2",
178 | "directReports@delta": [
179 | {
180 | "@odata.type": "#microsoft.graph.user",
181 | "id": "99789584-a1e1-4232-90e5-866170e3d4e7"
182 | } ,
183 | {
184 | "id": "66789583-f1e2-6232-70e5-366170e3d4a6",
185 | "@removed": {
186 | "reason": "deleted"
187 | }
188 | }
189 | ]
190 | },
191 | {
192 | "id": "0bbbbb0f-b0b3-4645-867d-742d8fb669a2",
193 | "@removed": {
194 | "reason": "changed"
195 | }
196 | }
197 | ]
198 | }
199 | ```
200 |
--------------------------------------------------------------------------------
/graph/patterns/default-properties.md:
--------------------------------------------------------------------------------
1 | # Default properties
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The default properties pattern allows API producers to omit specific properties from the response unless they are explicitly requested using `$select`.*
6 |
7 | ## Problem
8 |
9 | API designers want to control the set of properties that their entities return by default, when the incoming request does not specify a `$select`. This can be desirable when an entity type has many properties or an API producer needs to add properties that are computationally expensive to return by default.
10 |
11 | ## Solution
12 |
13 | For incoming requests targeting an entity type where the caller does not specify a `$select` clause, API producers **may** return a subset of the entity type's properties, omitting computationally expensive properties. To get the non-default properties of an entity type, callers must explicitly request them using `$select`.
14 |
15 | The pattern also uses an instance annotation to inform callers that other properties are also available. The same annotation is also used to encourage callers to use `$select`.
16 |
17 | ## When to use this pattern
18 |
19 | API producers should use this pattern when adding expensive or non-performant properties to an existing entity type, or when adding properties to an entity type that has already grown too large (with more than 20 properties).
20 |
21 | ## Issues and considerations
22 |
23 | - Do **not** rely on the `ags:Default` schema annotation for default properties functionality, as this is a legacy implementation. Returning default properties **must** be implemented by API producers.
24 | - Changing a default property to non-default is considered a breaking change.
25 | - One of the challenges with default properties is informing developers that the response does not contain the full set of properties. To solve for this discovery problem, if the response contains default properties only, then:
26 | - the response **must** contain a `@microsoft.graph.tips` instance annotation.
27 | - the `@microsoft.graph.tips` instance annotation **must** only be emitted if the client uses "developer mode" via the `Prefer: ms-graph-dev-mode` HTTP request header. It is expected that this header will only be used by client developer and scripting tools like Graph Explorer, the Microsoft Graph Postman collections, and Microsoft Graph PowerShell.
28 | - the `@microsoft.graph.tips` instance annotation value **must** contain "This request only returns a subset of the resource's properties. Your app will need to use $select to return non-default properties. To find out what other properties are available for this resource see https://learn.microsoft.com/graph/api/resources/{entityTypeName}".
29 | - Callers must be able to use `$filter` with non-default properties, even though they won't show up by default in the response.
30 |
31 | Additionally, for incoming requests targeting an entity type where the caller does not specify a `$select` clause, the API Gateway Service will inject a `@microsoft.graph.tips` instance annotation, informing callers to use $select, when in "developer mode".
32 | API producers who use [response passthrough](https://dev.azure.com/msazure/One/_wiki/wikis/Microsoft%20Graph%20Partners/391069/Enabling-response-passthrough) must also implement this behavior, supplying the same information as shown in the [examples section below](#calling-an-api-without-using-select).
33 |
34 | ## Examples
35 |
36 | In this example we'll use the following `channel` entity type.
37 |
38 | ```xml
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | ```
52 |
53 | In this scenario, the API producer wants to add the `moderationSettings` property to the `channel` entity type.
54 | But when paging through 1000 channels at a time, this additional property will introduce a considerable increase in the response times.
55 | The API producer will use the default properties pattern here, and **not** return `moderationSettings` by default.
56 |
57 | ### Calling an API with default properties
58 |
59 | In this example, the caller, using Graph Explorer, does not use $select, and the API returns just the default properties.
60 |
61 | #### Request
62 |
63 | ```http
64 | GET /teams/{id}/channels
65 | Prefer: ms-graph-dev-mode
66 | ```
67 |
68 | #### Response
69 |
70 | ```http
71 | 200 ok
72 | Content-type: application/json
73 | ```
74 |
75 | ```json
76 | {
77 | "@odata.context": "https://graph.microsoft.com/beta/$metadata#Collection(microsoft.graph.channel)",
78 | "@microsoft.graph.tips": "This request only returns a subset of the resource properties. Your app will need to use $select to return non-default properties. To find out what other properties are supported for this resource, please see the Properties section in https://learn.microsoft.com/graph/api/resources/channel.",
79 | "value": [
80 | {
81 | "displayName": "My First Shared Channel",
82 | "description": "This is my first shared channels",
83 | "id": "19:PZC_kAPAm12RPBMkEaJyXaY_d2PE6mJV6MzO1EiCbnk1@thread.tacv2",
84 | "membershipType": "shared",
85 | "email": "someemail@dot.com",
86 | "webUrl": "webUrl-value",
87 | "filesFolderWebUrl": "sharePointUrl-value",
88 | "tenantId": "tenantId-value",
89 | "isFavoriteByDefault": null,
90 | "createdDateTime": "2019-08-07T19:00:00Z"
91 | },
92 | {
93 | "displayName": "My Second Private Channel",
94 | "description": "This is my second shared channels",
95 | "id": "19:PZC_kAPAm12RPBMkEaJyXaY_d2PE6mJV6MzO1EiCbnk2@thread.tacv2",
96 | "membershipType": "private",
97 | "email": "someemail2@dot.com",
98 | "webUrl": "webUrl-value2",
99 | "filesFolderWebUrl": "sharePointUrl-value2",
100 | "tenantId": "tenantId-value",
101 | "isFavoriteByDefault": null,
102 | "createdDateTime": "2019-08-09T19:00:00Z"
103 | }
104 | ]
105 | }
106 | ```
107 |
108 | In the response, we can see that `moderationSettings` is not being returned. Additionally, the API producer is returning a `tips` instance annotation, informing the caller that this response only returns default properties, how to get the non-default properties, and where to find information about this type's properties. The `tips` instance annotation is only emitted if the `Prefer: ms-graph-dev-mode` HTTP request header is present.
109 |
110 | ### Calling an API with default properties and $select
111 |
112 | In this example, the caller needs `moderationSettings` for their API scenario. They try this out in Graph Explorer first.
113 |
114 | #### Request
115 |
116 | ```http
117 | GET /teams/{id}/channels?$select=id,membershipType,moderationSettings
118 | Prefer: ms-graph-dev-mode
119 | ```
120 |
121 | #### Response
122 |
123 | ```http
124 | 200 ok
125 | Content-type: application/json
126 | ```
127 |
128 | ```json
129 | {
130 | "@odata.context": "https://graph.microsoft.com/beta/$metadata#Collection(microsoft.graph.channel)",
131 | "value": [
132 | {
133 | "id": "19:PZC_kAPAm12RPBMkEaJyXaY_d2PE6mJV6MzO1EiCbnk1@thread.tacv2",
134 | "membershipType": "shared",
135 | "channelModerationSettings": {
136 | "userNewMessageRestriction": "everyone",
137 | "replyRestriction": "everyone",
138 | "allowNewMessageFromBots": true,
139 | "allowNewMessageFromConnectors": true
140 | }
141 | },
142 | {
143 | "id": "19:PZC_kAPAm12RPBMkEaJyXaY_d2PE6mJV6MzO1EiCbnk2@thread.tacv2",
144 | "membershipType": "private",
145 | "channelModerationSettings": {
146 | "userNewMessageRestriction": "moderators",
147 | "replyRestriction": "authorAndModerators",
148 | "allowNewMessageFromBots": true,
149 | "allowNewMessageFromConnectors": true
150 | }
151 | }
152 | ]
153 | }
154 | ```
155 |
156 | In this case, because the request has a `$select`, the `tips` instance annotation is not emitted.
157 |
158 | ### Calling an API without using $select
159 |
160 | The caller makes a `GET` request without $select, to an API that doesn't have any default properties, via Graph Explorer.
161 |
162 | #### Request
163 |
164 | ```http
165 | GET /me/todo/lists
166 | Prefer: ms-graph-dev-mode
167 | ```
168 |
169 | #### Response
170 |
171 | ```http
172 | 200 ok
173 | Content-type: application/json
174 | ```
175 |
176 | ```json
177 | {
178 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('99a6e897-8c54-4354-a739-626fbe28ed78')/todo/lists",
179 | "@microsoft.graph.tips": "Use $select to choose only the properties your app needs, as this can lead to performance improvements. For example: GET me/todo/lists?$select=displayName,isOwner",
180 | "value": [
181 | {
182 | "@odata.etag": "W/\"c5yMNreru0OMO71/IwuKGQAG6WUnjQ==\"",
183 | "displayName": "Tasks",
184 | "isOwner": true,
185 | "isShared": false,
186 | "wellknownListName": "defaultList",
187 | "id": "AAMkADU3NTBhNWUzLWE0MWItNGViYy1hMTA0LTkzNjRlYTA2ZWI2ZAAuAAAAAAAFup0i-hqtR5N14AJlh2qTAQATqGUvrHrTEbWPAKDJQ2mMAAACWIG1AAA="
188 | },
189 | {
190 | "@odata.etag": "W/\"c5yMNreru0OMO71/IwuKGQAG6WUnmQ==\"",
191 | "displayName": "Outlook Commitments",
192 | "isOwner": true,
193 | "isShared": false,
194 | "wellknownListName": "none",
195 | "id": "AQMkADU3NTBhNWUzLWE0MWItNGViYy1hMTA0LTkzNjRlYTA2ZWI2ZAAuAAADBbqdIv4arUeTdeACZYdqkwEAc5yMNreru0OMO71-IwuKGQABWbOTpQAAAA=="
196 | }
197 | ]
198 | }
199 | ```
200 |
201 | Notice how for this scenario, where there are no default properties and the caller does not use `$select`, there's a `tips` instance annotation, encouraging the app developer to use `$select`. This `tips` annotation is automatically added to the response by the API gateway service, as long as the workload service doesn't use response passthrough (in which case it is the responsibility of the workload service).
202 |
--------------------------------------------------------------------------------
/graph/patterns/dictionary-client-guidance.md:
--------------------------------------------------------------------------------
1 | # Dictionary types
2 |
3 | > **Note:** This document will be moved into a central client guidance document in the future.
4 |
5 | *The client guidance is a collection of additional information provided to SDK implementers and client applications. This information is meant to help understand how various guidelines and concepts translate in their world and clarify a few unknowns. Always read the corresponding guideline first to get a contextual understanding.*
6 |
7 | For more information, see the [Dictionary](./dictionary.md) pattern.
8 |
9 | ## OpenAPI example
10 |
11 | The following json-schema/OpenAPI example defines a dictionary of which values are of type **RoleSettings**.
12 |
13 | In **components** in **schemas**:
14 |
15 | ```json
16 | {
17 | "roleSettings": {
18 | "type": "object",
19 | "properties": {
20 | "domain": {
21 | "type": "string"
22 | }
23 | }
24 | }
25 | }
26 | }
27 | ```
28 |
29 | ```json
30 | {
31 | "type": "object",
32 | "patternProperties": {
33 | ".*": {
34 | "$ref": "#/components/schemas/roleSettings"
35 | },
36 | "additionalProperties": false
37 | }
38 | }
39 | ```
40 |
41 | ## SDK support
42 |
43 | SDKs need to provide support for dictionary types so that SDK consumers get a delightful development experience. Examples are provided for different languages. Other aspects need to be taken into consideration:
44 |
45 | - Dictionaries support OData annotations (values prefixed with **@OData**); such annotations should not be inserted directly in the dictionary but rather in the additional properties manager.
46 | - Dictionary types can inherit another dictionary type; this inheritance must be respected.
47 | - Dictionary values can be of union types; if the target language doesn't support union types, a wrapper type should be generated as a backward compatible solution with properties for each type of the union.
48 |
49 | ### Dotnet
50 |
51 | ```CSharp
52 | Dictionary
53 | ```
54 |
55 | ### Java
56 |
57 | ```Java
58 | Map
59 | ```
60 |
61 | ### JavaScript/TypeScript
62 |
63 | ```TypeScript
64 | Map
65 | ```
66 |
67 | or
68 |
69 | ```JavaScript
70 | {
71 | [key: string]: {value: RoleSettings}
72 | }
73 | ```
74 |
75 | ## Request builder generation annotation
76 |
77 | By default, SDKs are not required to contain a set of request builders to run CRUD requests on entries in the dictionary. The dictionary is updated as a whole by consumers by sending requests to the parent entity.
78 |
79 | If a **SupportedHttpMethod** annotation is specified for the dictionary type, request builders should be generated to allow consumers to automatically update the entries.
80 |
--------------------------------------------------------------------------------
/graph/patterns/dictionary.md:
--------------------------------------------------------------------------------
1 | # Dictionary
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | _The dictionary type provides the ability to create a set key/value pairs where the set of keys is dynamically specified by the API consumer._
6 |
7 | ## Problem
8 |
9 | The API design requires a resource to include an unknown quantity of data values whose keys are defined by the API consumer.
10 |
11 | ## Solution
12 |
13 | API designers use a JSON object to represent a dictionary in an `application/json` response payload. When describing the model in CSDL, a new complex type can be created that derives from `graph.Dictionary` and optionally uses the `Org.OData.Validation.V1.OpenPropertyTypeConstraint` to constrain the type that can be used for the values in the dictionary as appropriate.
14 |
15 | Dictionary entries can be added, removed, or modified via `PATCH` to the dictionary property. Entries are removed by setting the property to `null`.
16 |
17 | ## When to use this pattern
18 |
19 | Before using a dictionary type in your API definition, make sure that your scenario fits the following criteria:
20 |
21 | - The data values MUST be related to one another semantically as a collection.
22 | - The values MUST be primitive or complex types.
23 | - The client MUST define the keys of this type, as opposed to the service defining them in advance.
24 |
25 | ### Alternatives
26 |
27 | - [Open extensions](https://docs.microsoft.com/graph/extensibility-open-users) when you want to provide clients the ability to extend Microsoft Graph.
28 | - [Complex types](https://docs.microsoft.com/odata/webapi/complextypewithnavigationproperty) when the set of data values are known.
29 |
30 | ## Issues and considerations
31 |
32 | Dictionaries, sometimes called maps, are a collection of name-value pairs. They allow dynamic data sets to be accessed in a systematic manner and are a good compromise between a strictly defined-ahead-of-time structure with all its named properties and a loosely defined dynamic object (such as OData OpenTypes).
33 |
34 | Because dictionary entries are removed by setting the value to `null`, dictionaries don't support null values.
35 |
36 | For more information, see the [OData reference](https://github.com/oasis-tcs/odata-vocabularies/blob/master/vocabularies/Org.OData.Core.V1.md#dictionary).
37 |
38 | ## Examples
39 |
40 | ### String dictionary
41 |
42 | #### CSDL declaration
43 | The following example demonstrates defining a dictionary that can contain string values.
44 |
45 | ```xml
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | Edm.String
56 |
57 |
58 |
59 |
60 | ```
61 |
62 | Please note that schema validation will fail due to the casing of `Dictionary`.
63 | This warning should be suppressed.
64 |
65 | #### Defining a dictionary property
66 | The following example shows defining a dictionary property, "userTags", on the item entity type.
67 |
68 | ```xml
69 |
70 | ...
71 |
72 |
73 | ```
74 |
75 | #### Reading a dictionary
76 | Dictionaries are represented in JSON payloads as a JSON object, where the property names are comprised of the keys and their values are the corresponding key values.
77 |
78 | The following example shows reading an item with a dictionary property named "userTags":
79 |
80 | ```HTTP
81 | GET /item
82 | ```
83 | Response:
84 | ```json
85 | {
86 | ...
87 | "userTags":
88 | {
89 | "anniversary": "2002-05-19",
90 | "favoriteMovie": "Princess Bride"
91 | }
92 | }
93 | ```
94 |
95 | #### Setting a dictionary value
96 | The following example shows setting a dictionary value. If "hairColor" already exists, it is updated, otherwise it is added.
97 |
98 | ```http
99 | PATCH /item/userTags
100 | ```
101 | ```json
102 | {
103 | "hairColor": "purple"
104 | }
105 | ```
106 |
107 | #### Deleting a dictionary value
108 | A dictionary value can be removed by setting the value to null.
109 | ```http
110 | PATCH /item/userTags
111 | ```
112 | ```json
113 | {
114 | "hairColor": null
115 | }
116 | ```
117 |
118 | ### Complex typed dictionary
119 |
120 | #### CSDL declaration
121 | Dictionaries can also contain complex types whose values may be constrained to a particular set of complex types.
122 |
123 | The following example defines a complex type **roleSettings**, an **assignedRoleGroupDictionary** that contains **roleSettings**, and an **assignedRoles** property that uses the dictionary..
124 |
125 | ```xml
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | ...
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | of roleSettings
144 | keyed by name of roleGroup. -->
145 |
146 |
147 | microsoft.graph.roleSettings
148 |
149 |
150 |
151 |
152 | ```
153 |
154 | #### Reading a entity with a complex-typed dictionary
155 |
156 | The following example illustrates reading an entity containing the complex-typed dictionary "assignedRoles".
157 |
158 | ```HTTP
159 | GET /users/10
160 | ```
161 |
162 | Response:
163 |
164 | ```json
165 | {
166 | "id": "10",
167 | "displayName": "Jane Smith",
168 | "assignedRoles": {
169 | "author": {
170 | "domain": "contoso"
171 | },
172 | "maintainer": {
173 | "domain": "fabrikam"
174 | },
175 | "architect": {
176 | "domain": "adventureWorks"
177 | }
178 | }
179 | }
180 | ```
181 |
182 | #### Reading the dictionary property
183 | The following example shows getting just the "assignedRoles" dictionary property.
184 |
185 | ```HTTP
186 | GET /users/10/assignedRoles
187 | ```
188 |
189 | Response:
190 |
191 | ```json
192 | {
193 | "author": {
194 | "domain": "contoso"
195 | },
196 | "maintainer": {
197 | "domain": "fabrikam"
198 | },
199 | "architect": {
200 | "domain": "adventureWorks"
201 | }
202 | }
203 | ```
204 |
205 | #### Reading an individual entry from the dictionary
206 | The following example shows reading a single complex-typed entry named "author" from the "assignedRoles" dictionary.
207 |
208 | ```HTTP
209 | GET /users/10/assingedRoles/author
210 | ```
211 |
212 | Response:
213 |
214 | ```json
215 | {
216 | "domain": "contoso"
217 | }
218 | ```
219 |
220 | #### Setting an individual entry in the dictionary
221 | The following examples shows updating the dictionary to set the value for the "author" entry. If the "author" entry does not exists it is added with the specified values; otherwise, if the "author" entry already exists, it is updated with the specified values (unspecified values are left unchanged).
222 |
223 | ```HTTP
224 | PATCH /users/10/assignedRoles/author
225 | ```
226 | ```json
227 | {
228 | "author" : {
229 | "domain": "contoso"
230 | }
231 | }
232 | ```
233 |
234 | #### Deleting an individual entry from the dictionary
235 | The following example shows deleting the "author" entry by setting its value to null.
236 |
237 | ```HTTP
238 | PATCH /users/10/assignedRoles
239 | ```
240 | ```json
241 | {
242 | "author": null
243 | }
244 | ```
245 |
246 | #### Setting multiple dictionary entries
247 | The following example sets values for the "author", "maintainer" and "viewer" entries, and removes the "architect" entry by setting it to null.
248 |
249 | ```HTTP
250 | PATCH /users/10/assignedRoles
251 | ```
252 | ```json
253 | {
254 | "author": {
255 | "domain": "contoso1"
256 | },
257 | "maintainer": {
258 | "domain": "fabrikam1"
259 | },
260 | "reviewer": {
261 | "domain": "fabrikam"
262 | },
263 | "architect": null
264 | }
265 |
266 | ```
267 |
268 | ## See also
269 |
270 | - [SDK implementation guidance](./dictionary-client-guidance.md)
271 |
--------------------------------------------------------------------------------
/graph/patterns/enums.md:
--------------------------------------------------------------------------------
1 | ### Enums
2 |
3 | In OData, enums represent a subset of the nominal type they rely on, and are especially useful in cases where certain properties have predefined, limited options.
4 |
5 | ```xml
6 |
7 |
8 |
9 |
10 |
11 | ```
12 |
13 | #### Pros
14 |
15 | - Our SDK generators will translate the enum to the best representation of the target programming language, resulting in a better developer experience and free client side validation
16 |
17 | #### Cons
18 |
19 | - Adding a new value requires to go through a (generally fast) API Review
20 | - If the enum is not [evolvable](./patterns/evolvable-enums.md), adding a new value is a breaking change and will generally not be allowed
21 |
22 | #### Enum or Booleans
23 |
24 | Enumerations are a good alternative to Booleans when one of the two values (`true`, `false`) conveys other possible values not yet conceived. Let's assume we have an `publicNotification` type and a property to communicate how to display it:
25 |
26 | ```xml
27 |
28 |
29 |
30 |
31 |
32 | ```
33 |
34 | The `false` value here merely communicates that the notification shall not be displayed as a tip. What if, in the future, the notification could be displayed as a `tip` or `alert`, and then in a more distant future, a `dialog` option is viable?
35 |
36 | With the current model, the only way is to add more boolean properties to convey the new information:
37 |
38 | ```diff
39 |
40 |
41 |
42 |
43 | +
44 | +
45 |
46 | ```
47 |
48 | Additionally speaking, the workload will now also have to validate the data structure and make sure that only one of the 3 values is `true`
49 |
50 | By using an evolvable enum, instead, all we need to do is to add new members:
51 |
52 | ```diff
53 |
54 |
55 |
56 | +
57 | -
58 | -
59 | -
60 |
61 | ```
62 |
63 | ```xml
64 |
65 |
66 |
67 |
68 |
69 |
70 | ```
71 |
72 | Similarly speaking, if you find yourself using a `nullable` Enum, that is a indication that maybe what you are trying to model is something that has 3 states and an enum is more appropraite. For instance, let's assume we have a boolean property called `syncEnabled`, where `null` means that the value is undefined and inherited from the general tenant configuration. Instead of modelling like a boolean:
73 |
74 | ```xml
75 |
76 | ```
77 |
78 | An enum not only better conveys the message:
79 |
80 | ```xml
81 |
82 |
83 |
84 |
85 |
86 |
87 | ```
88 |
89 | but it is also open for future scenarios:
90 |
91 | ```diff
92 |
93 |
94 |
95 |
96 |
97 | +
98 |
99 | ```
100 |
101 | Additionally speaking, depending on the situation, a nullable enum can very likely be avoided by adding a `none` member.
102 |
103 | If used, `EnumType` names should be singular if the are non-flags enums, and the names should be plural if they are flags enums.
104 |
105 | #### Flag Enums or Collection of Enums
106 |
107 | In case an enum can have multiple values at the same time the tentation is to model the property as a collection of Enums:
108 |
109 | ```xml
110 |
111 | ```
112 |
113 | However, [Flagged Enums](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#_Toc38530378) can model this use case scenario:
114 |
115 | ```diff
116 | -
117 | +
118 | -
119 | +
120 | -
121 | +
122 | -
123 | +
124 | -
125 | +
126 |
127 | ```
128 |
129 | With such enum, customers can select multiple values in a single field:
130 |
131 | `displayMethod = tip | alert`
132 |
133 | In cases where two properties want to use the same *conceptual* `EnumType`, but one property is a collection while the other is single-values, the model should define *two* separate `EnumType`s, one being a non-flags enum with a singular name and the other marked as a flags enum with its name being the plural form of the non-flags enum.
134 |
135 | #### Flag enum + non-flag enum
136 |
137 | There are occasions where one API will want to use a non-flag enum, but another API will want a flags enum.
138 | For example, the `displayMethod` example above may have one API that is configuring which display methods to use, and another API which is configuring that particular display method.
139 | In this case, the first API will want a flags enum, but the second API will want to only allow configuring one display method at a time, and will therefore prefer a non-flags enum.
140 |
141 | Two enum types should be defined, one as a flags enum and the other as a non-flags enum.
142 | The flags enum should be named such that it is plural, and the non-flags enum should be named such that it is singular.
143 | The two types should be kept in sync with each other.
144 |
--------------------------------------------------------------------------------
/graph/patterns/evolvable-enums.md:
--------------------------------------------------------------------------------
1 | # Evolvable enums
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The evolvable enums pattern allows API producers to extend enumerated types with new members without breaking API consumers.*
6 |
7 | Note: You might be interested in reading the [Enum guidance](./enums.md) first
8 |
9 | ## Problem
10 |
11 | Frequently API producers want to add new members to an enum type after it is initially published. Some serialization libraries might fail when they encounter members in an enum type that were added after the serialization model was generated. In this documentation, we refer to any added enum members as unknown.
12 |
13 | ## Solution
14 |
15 | The solution is to add a 'sentinel' member named `unknownFutureValue` at the end of the currently known enum members. The API producer then replaces any member that is numerically after `unknownFutureValue` with `unknownFutureValue`.
16 |
17 | If an API consumer can handle unknown enum values, the consumer can opt into receiving the unknown enum members by specifying the `Prefer: include-unknown-enum-members` HTTP header in their requests. The API producer then indicates that this preference has been applied by returning the `Preference-Applied: include-unknown-enum-members` HTTP header in the response.
18 |
19 | ## When to use this pattern
20 |
21 | It is a best practice to include an `unknownFutureValue` value when the enum is initially introduced to allow flexibility to extend the enum during the lifetime of the API. Even if the API producer believes that they have included all possible members in an enum, we still strongly recommend that you include an `unknownFutureValue` member to allow for unforeseen future circumstances that may require extending the enum.
22 |
23 | This pattern must not be used in scenarios where an API consumer wants to use enum members that are not known to the API producer.
24 |
25 | ## Issues and considerations
26 |
27 | Consider the following:
28 |
29 | - An enum member with the name of `unknownFutureValue` MUST only be used as a sentinel value. An API producer MUST not include a member named `unknownFutureValue` in an enum for any other purpose.
30 |
31 | - Changing the value (that is, position) of the `unknownFutureValue` sentinel member is considered a breaking change and must follow the [deprecation](../deprecation.md) process.
32 |
33 | - Enum types can have multiple members with the same numeric value to allow for aliasing enum members. `unknownFutureValue` MUST not be aliased to any other enum member.
34 |
35 | - There is no ability for a client to indicate that it can handle a subset of unknown enum members. Instead, they can only specify that either they cannot handle any unknown enum members or they can handle any unknown enum members.
36 |
37 | - The `Prefer: include-unknown-enum-members` header applies to all included enums in the request/response. There is no way for an API consumer to apply the behavior to only a subset of enum types.
38 |
39 | - New values MUST not be inserted into the enum before `unknownFutureValue`. Implementers are recommended to make the numeric value of `unknownFutureValue` one greater than the last known enum member to ensure that there are no gaps into which a new member could be inadvertently added. The exception to this is the case of flagged enums, in which case the value of `unknownFutureValue` should be the next power of 2 value.
40 |
41 | - For flagged enums, care should be exercised to ensure that `unknownFutureValue` is not included in any enum members that represent a combination of other enum members.
42 |
43 | - If the value of a property containing a flag enum contains multiple unknown values, they should all be replaced with a single `unknownFutureValue` value (that is, there should not be multiple `unknownFutureValue` values returned).
44 |
45 | - If an API consumer specifies `unknownFutureValue` for the value of a property in a `POST`/`PUT` request or as a parameter of an action or function, the API producer must reject the request with a `400 Bad Request` HTTP status.
46 |
47 | - If an API consumer specifies `unknownFutureValue` for the value of a property in a `PATCH` request, the API producer must treat the property as if it were absent (that is, the existing value should not be changed). In the case where the API producer treats `PATCH` as an upsert, the call MUST be rejected with a `400 Bad Request` HTTP status.
48 |
49 | - If an API consumer specifies an enum member greater than `unknownFutureValue` in any request without specifying the `Prefer: include-unknown-enum-members` header, the API producer must reject the request with a `400 Bad Request` HTTP status.
50 |
51 | - For details about how the `unknownFutureValue` value is handled as part of a `$filter` clause, consult the following examples:
52 |
53 | - **CSDL**
54 |
55 | ```xml
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | ```
67 |
68 | - **Filter behavior**
69 |
70 | | `$filter` clause | `Prefer: include-unknown-enum-members` Absent | `Prefer: include-unknown-enum-members` Present |
71 | |---|---|---|
72 | | `enumProperty eq unknownFutureValue`| Return entities where enumProperty has any value greater than `unknownFutureValue` replacing actual value with `unknownFutureValue`| Return nothing |
73 | | `enumProperty gt unknownFutureValue`| Return entities where enumProperty has any value greater than `unknownFutureValue` replacing actual value with `unknownFutureValue` | Return entities where enumProperty has any value greater than `unknownFutureValue` |
74 | | `enumProperty lt unknownFutureValue`| Return entities where enumProperty has any known value (i.e. less than `unknownFutureValue`) | Return entities where enumProperty has any value less than `unknownFutureValue`|
75 | | `enumProperty eq newValue` | `400 Bad Request` | Return entities where enumProperty has the value `newValue` |
76 | | `enumProperty gt newValue` | `400 Bad Request` | Return entities where enumProperty has a value greater than `newValue` |
77 | | `enumProperty lt newValue` | `400 Bad Request` | Return entities where enumProperty has a value less than `newValue` |
78 |
79 | - If an evolvable enum is included in an `$orderby` clause, the actual numeric value of the member should be used to order the collection. After sorting, the member should then be replaced with `unknownFutureValue` when the `Prefer: include-unknown-enum-members` header is absent.
80 |
81 | ## Examples
82 |
83 | For the following examples, we consider the `managedDevice` entity, which refers to the `managedDeviceArchitecture` enum type.
84 |
85 | ```xml
86 |
87 |
88 |
89 |
90 |
91 | ```
92 |
93 | When the `managedDeviceArchitecture` enum was initially published to Microsoft Graph, it was defined as follows:
94 |
95 | ```xml
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | ```
106 |
107 | The enum was later extended to add a new value of `quantum`, leading to the following CSDL:
108 |
109 | ```xml
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 | ```
120 |
121 | ### Default behavior
122 |
123 | ```http
124 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture
125 | ```
126 |
127 | ```json
128 | {
129 | "value": [
130 | {
131 | "id": "0",
132 | "displayName": "Surface Pro X",
133 | "processorArchitecture" : "arm64"
134 | },
135 | {
136 | "id": "1",
137 | "displayName": "Prototype",
138 | "processorArchitecture": "unknownFutureValue"
139 | }
140 | {
141 | "id": "2",
142 | "displayName": "My Laptop",
143 | "processorArchitecture": "x64"
144 | }
145 | ]
146 | }
147 | ```
148 |
149 | In this case, the value of the `processorArchitecture` property is `quantum`. However, because the client did not request the `include-unknown-enum-members` header, the value was replaced with `unknownFutureValue`.
150 |
151 | ### Include opt-in header
152 |
153 | ```http
154 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture
155 |
156 | Prefer: include-unknown-enum-members
157 | ```
158 |
159 | ```json
160 | Preference-Applied: include-unknown-enum-members
161 |
162 | {
163 | "value": [
164 | {
165 | "displayName": "Surface Pro X",
166 | "processorArchitecture" : "arm64"
167 | },
168 | {
169 | "displayName": "Prototype",
170 | "processorArchitecture": "quantum"
171 | },
172 | {
173 | "displayName": "My Laptop",
174 | "processorArchitecture": "x64"
175 | }
176 | ]
177 | }
178 | ```
179 |
180 | ### Default sort behavior
181 |
182 | ```http
183 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture&$orderBy=processorArchitecture
184 | ```
185 |
186 | ```json
187 | {
188 | "value": [
189 | {
190 | "displayName": "Surface Pro X",
191 | "processorArchitecture" : "arm64"
192 | },
193 | {
194 | "displayName": "My Laptop",
195 | "processorArchitecture": "x64"
196 | },
197 | {
198 | "displayName": "Prototype",
199 | "processorArchitecture": "unknownFutureValue"
200 | }
201 | ]
202 | }
203 | ```
204 |
205 | ### Sort behavior with opt-in header
206 |
207 | ```http
208 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture
209 |
210 | Prefer: include-unknown-enum-members
211 | ```
212 |
213 | ```json
214 | Preference-Applied: include-unknown-enum-members
215 |
216 | {
217 | "value": [
218 | {
219 | "displayName": "Surface Pro X",
220 | "processorArchitecture" : "arm64"
221 | },
222 | {
223 | "displayName": "My Laptop",
224 | "processorArchitecture": "x64"
225 | },
226 | {
227 | "displayName": "Prototype",
228 | "processorArchitecture": "quantum"
229 | }
230 | ]
231 | }
232 | ```
233 |
234 | ### Default filter behavior
235 |
236 | ```http
237 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture&$filter=processorArchitecture gt x64
238 | ```
239 |
240 | ```json
241 | {
242 | "value": [
243 | {
244 | "displayName": "My Laptop",
245 | "processorArchitecture": "x64"
246 | },
247 | {
248 | "displayName": "Prototype",
249 | "processorArchitecture": "unknownFutureValue"
250 | }
251 | ]
252 | }
253 | ```
254 |
255 | ### Filter behavior with opt-in header
256 |
257 | ```http
258 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices?$select=displayName,processorArchitecture&$filter=processorArchitecture gt x64
259 |
260 | Prefer: include-unknown-enum-members
261 | ```
262 |
263 | ```json
264 | Preference-Applied: include-unknown-enum-members
265 |
266 | {
267 | "value": [
268 | {
269 | "displayName": "My Laptop",
270 | "processorArchitecture": "x64"
271 | },
272 | {
273 | "displayName": "Prototype",
274 | "processorArchitecture": "quantum"
275 | }
276 | ]
277 | }
278 | ```
279 |
280 | ### Patch example
281 |
282 | ```http
283 | PATCH https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/1
284 |
285 | {
286 | "displayName": "Secret Prototype",
287 | "processorArchitecture": "unknownFutureValue"
288 | }
289 | ```
290 |
291 | ```json
292 | {
293 | "id": "1",
294 | "displayName": "Secret Prototype",
295 | "processorArchitecture": "unknownFutureValue"
296 | }
297 | ```
298 |
299 | ```http
300 | GET https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/1
301 | Prefer: include-unknown-enum-members
302 | ```
303 |
304 | ```json
305 | Preference-Applied: include-unknown-enum-members
306 |
307 | {
308 | "id": "1",
309 | "displayName": "Secret Prototype",
310 | "processorArchitecture": "quantum"
311 | }
312 | ```
313 |
314 | ## Flag enum examples
315 |
316 | For the following examples, we consider the `windowsUniversalAppX` entity, which refers to the `windowsArchitecture` flag enum type.
317 |
318 | ```xml
319 |
320 |
321 |
322 |
323 |
324 | ```
325 |
326 | When the `windowsArchitecture` enum was initially published to Microsoft Graph, it was defined as follows:
327 |
328 | ```xml
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 | ```
339 |
340 | The enum was later extended to add a new value of `quantum`, leading to the following CSDL:
341 |
342 | ```xml
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 | ```
353 |
354 | ### Flag enum default behavior
355 |
356 | ```http
357 | GET https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps?$select=displayName,applicableArchitectures
358 | ```
359 |
360 | ```json
361 | {
362 | "value": [
363 | {
364 | "id": "0",
365 | "displayName": "OneNote",
366 | "applicableArchitectures" : "neutral"
367 | },
368 | {
369 | "id": "1",
370 | "displayName": "Minecraft",
371 | "applicableArchitectures": "x86,x64,arm,unknownFutureValue"
372 | }
373 | {
374 | "id": "2",
375 | "displayName": "Edge",
376 | "applicableArchitectures": "x64,arm,unknownFutureValue"
377 | }
378 | ]
379 | }
380 | ```
381 |
382 | In this case, the value of the `applicableArchitectures` property includes `quantum`. However, because the client did not request the `include-unknown-enum-members` header, the value was replaced with `unknownFutureValue`.
383 |
384 | ### Flag enum include opt-in header
385 |
386 | ```http
387 | GET https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps?$select=displayName,applicableArchitectures
388 |
389 | Prefer: include-unknown-enum-members
390 | ```
391 |
392 | ```json
393 | Preference-Applied: include-unknown-enum-members
394 |
395 | {
396 | "value": [
397 | {
398 | "id": "0",
399 | "displayName": "OneNote",
400 | "applicableArchitectures" : "neutral"
401 | },
402 | {
403 | "id": "1",
404 | "displayName": "Minecraft",
405 | "applicableArchitectures": "x86,x64,arm,quantum"
406 | }
407 | {
408 | "id": "2",
409 | "displayName": "Edge",
410 | "applicableArchitectures": "x64,arm,quantum"
411 | }
412 | ]
413 | }
414 | ```
415 |
416 | ### Flag enum default filter behavior
417 |
418 | ```http
419 | GET https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps?$select=displayName,applicableArchitectures&$filter=applicableArchitectures has unknownFutureValue
420 | ```
421 |
422 | ```json
423 | {
424 | "value": [
425 | {
426 | "id": "1",
427 | "displayName": "Minecraft",
428 | "applicableArchitectures": "x86,x64,arm,unknownFutureValue"
429 | }
430 | {
431 | "id": "2",
432 | "displayName": "Edge",
433 | "applicableArchitectures": "x64,arm,unknownFutureValue"
434 | }
435 | ]
436 | }
437 | ```
438 |
439 | ### Flag enum include opt-in header filter behavior
440 |
441 | ```http
442 | GET https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps?$select=displayName,applicableArchitectures&$filter=applicableArchitectures has unknownFutureValue
443 |
444 | Prefer: include-unknown-enum-members
445 | ```
446 |
447 | ```json
448 | Preference-Applied: include-unknown-enum-members
449 |
450 | {
451 | "value": []
452 | }
453 | ```
454 |
455 | ### Flag enum patch example
456 |
457 | ```http
458 | PATCH https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps/1
459 |
460 | {
461 | "displayName": "Minecraft 2",
462 | "processorArchitecture": "unknownFutureValue"
463 | }
464 | ```
465 |
466 | ```json
467 | {
468 | "id": "1",
469 | "displayName": "Minecraft 2",
470 | "applicableArchitectures": "unknownFutureValue"
471 | }
472 | ```
473 |
474 | ```http
475 | GET https://graph.microsoft.com/v1.0/deviceAppManagement/mobileApps/1
476 |
477 | Prefer: include-unknown-enum-members
478 | ```
479 |
480 | ```json
481 | Preference-Applied: include-unknown-enum-members
482 |
483 | {
484 | "id": "1",
485 | "displayName": "Minecraft 2",
486 | "applicableArchitectures": "x86,x64,arm,quantum"
487 | }
488 | ```
489 |
--------------------------------------------------------------------------------
/graph/patterns/facets.md:
--------------------------------------------------------------------------------
1 | # Facets
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *A frequent pattern in Microsoft Graph is to model multiple variants of a common concept as a single entity type with common properties and facets for variants.*
6 |
7 | ## Problem
8 |
9 | An API designer needs to model a set of heterogeneous resources that have common properties and behaviors and might express features of multiple variants at a time because variants are not mutually exclusive.
10 | For example, a movie clip stored on OneDrive is both a file and a video. There are properties associated to each variant.
11 |
12 | ## Solution
13 |
14 | API designers create multiple complex types to bundle properties for each variant, and then define an entity type with a property for each complex type to hold the properties of the variant.
15 |
16 | In this solution, a child variant is identified by the presence of one or more facets in the parent object.
17 |
18 | ## When to use this pattern
19 |
20 | The facets pattern is useful when there is a number of variants and they are not mutually exclusive. It also makes it syntactically easier to query resources by using the OData `$filter` expression because it doesn't require casting.
21 |
22 | You can consider related patterns such as [type hierarchy](./subtypes.md) and [flat bag of properties](./flat-bag.md).
23 |
24 | ## Issues and considerations
25 |
26 | When introducing a new facet, you need to ensure that the new facet doesn't change the semantic of the model with its implicit constraints.
27 |
28 | ## Example
29 |
30 | The driveItem resource represents a file, folder, image, or other item stored in a drive and is modeled by using an entity type with multiple facets.
31 |
32 | ```XML
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | ...
62 |
63 | ```
64 |
65 | An API request to get all items from a personal OneDrive returns a heterogenous collection with different facets populated. In the following example, there is a folder, a file, and an image in the collection. The image entity has two facets populated: file and image.
66 |
67 | ```
68 | GET https://graph.microsoft.com/v1.0/me/drive/root/children
69 |
70 | Response shortened for readability:
71 |
72 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('93816c1c-1b19-41de-a322-a1643d7f4d39')/drive/root/children",
73 | "value": [
74 | {
75 | "createdDateTime": "2021-07-07T13:59:47Z",
76 | "name": "Microsoft Teams Chat Files",
77 | ...,
78 | "folder": {
79 | "childCount": 15
80 | }
81 | },
82 | ...
83 | {
84 | "createdDateTime": "2021-12-15T00:07:36Z",
85 | "name": "Versioning and Deprecation.docx",
86 | ...,
87 | "file": {
88 | "mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
89 | "hashes": {
90 | "quickXorHash": "r2d9uZilW0zEIXwycymsUQzhV+U="
91 | }
92 | },
93 | ...
94 | },
95 | {
96 | "createdDateTime": "2021-12-21T16:32:51Z",
97 | "name": "WhaleShark.jpg",
98 | ...
99 | "file": {
100 | "mimeType": "image/jpeg",
101 | "hashes": {
102 | "quickXorHash": "2vHpAA7RDZJteIwl1pXR980xuh4="
103 | }
104 | },
105 | ...,
106 | "image": {}
107 | }
108 | ]
109 | ```
110 |
--------------------------------------------------------------------------------
/graph/patterns/flat-bag.md:
--------------------------------------------------------------------------------
1 | # Flat bag of properties
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *A known pattern in Microsoft Graph is to model multiple variants of a common concept as a single entity type with all potential properties plus an additional property to distinguish the variants.*
6 |
7 | ## Problem
8 |
9 | API designers need to model a small and limited number of variants of a common concept with a concise list of non-overlapping properties and consistent behavior across variants. The designer also wants to simplify query construction.
10 |
11 | ## Solution
12 |
13 | The API designer creates one entity type with all the potential properties plus an additional property to distinguish the variants, often called `variantType`. For each value of `variantType`, some properties are meaningful and others are ignored.
14 |
15 | ## When to use this pattern
16 |
17 | The flat bag pattern is useful when there is a small number of variants with similar behavior, and variants are queried for mostly read-only operations. The pattern also makes it syntactically easier to query resources by using the OData `$filter` expression because it doesn't require casting.
18 |
19 | ## Issues and considerations
20 |
21 | In general, the flat bag pattern is the least recommended modeling choice because it is weakly typed, and it is difficult to semantically verify targeted resource modifications. However, there are circumstances when query simplicity and a limited number of properties might overweight considerations of a more strongly typed approach.
22 | The pattern is not recommended for a large number of variants and properties because the payload becomes sparsely populated.
23 |
24 | You can consider related patterns such as [type hierarchy](./subtypes.md) and [facets](./facets.md).
25 |
26 | ## Example
27 |
28 | A good example for flat bag implementation is the recurrencePattern type on [recurrencePattern](https://docs.microsoft.com/graph/api/resources/recurrencepattern).
29 |
30 | The recurrencePattern has six variants expressed as six different values of the `type` property (for example: daily, weekly, ...). The key here is that for each of these values, some properties are meaningful and others are ignored (for example: `daysOfWeek` is relevant when `type` is `weekly` but not when it is `daily`).
31 |
32 | ```
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | ```
--------------------------------------------------------------------------------
/graph/patterns/long-running-operations.md:
--------------------------------------------------------------------------------
1 | # Long running operations
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The long running operations (LRO) pattern provides the ability to model operations where processing a client request takes a long time, but the client isn't blocked and can do some other work until operation completion.*
6 |
7 | ## Problem
8 |
9 | The API design requires modeling operations on resources, which takes a long time
10 | to complete, so that API clients don't need to wait and can continue doing other
11 | work while waiting for the final operation results. The client should be able to
12 | monitor the progress of the operation and have an ability to cancel it if
13 | needed.
14 |
15 | The API needs to provide a mechanism to track the work
16 | being done in the background. The mechanism needs to be expressed in the same
17 | web style as other interactive APIs. It also needs to support checking on the status and/or
18 | being notified asynchronously of the results.
19 |
20 | ## Solution
21 |
22 | The solution is to model the API as a synchronous service that returns a
23 | resource that represents the eventual completion or failure of a long running
24 | operation.
25 |
26 | There are two flavors of this solution:
27 |
28 | - The returned resource is the targeted resource and includes the status of
29 | the operation. This pattern is often called RELO (resource-based long running operation).
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | - The returned resource is a new API resource called *stepwise operation* and is created to track the status. This LRO solution is similar to the concept of Promises or Futures in other programming languages.
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | The RELO pattern is the preferred pattern for long running operations and should be
46 | used wherever possible. The pattern avoids complexity, and consistent resource
47 | presentation makes things simpler for our users and tooling chain.
48 |
49 | - For the RELO pattern, you should return the Location header that indicates the location of the resource.
50 | - The API response says the targeted resource is being created by returning a 201 status code and the resource URI is provided in the Location header, but the response indicates that the request is not completed by including "Provisioning" status.
51 |
52 | - For the LRO pattern, you should return the Location header that indicates the location of a new stepwise operation resource.
53 | - The API response says the operation resource is being created at the URL provided in the Location header and indicates that the request is not completed by including a 202 status code.
54 | - Microsoft Graph doesn’t allow tenant-wide operation resources; therefore, stepwise operations are often modeled as a navigation property on the target resource.
55 |
56 | - For most implementations of the LRO pattern (like the example above), there will be 3 permissions necessary to comply with the principle of least privilege: `ArchiveOperation.ReadWrite.All` to create the `archiveOperation` entity, `ArchiveOperation.Read.All` to track the `archiveOperation` entity to completion, and `Archives.Read.All` to retrieve the `archive` that was created as a result of the operation.
57 | For APIs that would have been modeled as a simple `GET` on the resource URL, but that are modeled as long-running operations due to MSGraph performance requirements, only the `Archive.Read.All` permission is necessary as long as creating the `archiveOperation` entity is "safe".
58 | Here, "safe" means that there are no side effects of creating the `archiveOperation` entity that would change the functionality of any entities outside of the `archive` being retrieved.
59 | This requirment does not mean that the API must be idempotent, but an idempotent API is suffucient to meet this requirement.
60 |
61 | ## When to use this pattern
62 |
63 | Any API call that is expected to take longer than one second in the 99th percentile should use the long running operations pattern.
64 |
65 | How do you select which flavor of LRO pattern to use? An API designer can follow these heuristics:
66 |
67 | 1. If a service can create a resource with a minimal latency and continue updating its status according to the well-defined and stable state transition model until completion, then the RELO model is the best choice.
68 |
69 | 2. Otherwise, a service should follow the stepwise operation pattern.
70 |
71 | ## Issues and considerations
72 |
73 | - One or more API consumers MUST be able to monitor and operate on the same resource at the same time.
74 |
75 | - The state of the system SHOULD always be discoverable and testable. Clients
76 | SHOULD be able to determine the system state even if the operation tracking
77 | resource is no longer active. Clients MAY issue a GET on some resource to
78 | determine the state of a long running operation.
79 |
80 | - The long running operations pattern SHOULD work for clients looking to "fire and forget"
81 | and for clients looking to actively monitor and act upon results.
82 |
83 | - The long running operations pattern might be supplemented by the [change notification pattern](./change-notification.md).
84 |
85 | - Cancellation of a long running operation does not explicitly mean a rollback. On a per API-defined case, it
86 | might mean a rollback or compensation or completion or partial completion,
87 | etc. Following a canceled operation, the API should return a consistent state that allows
88 | continued service.
89 |
90 | - A recommended minimum retention time for a stepwise operation is 24 hours.
91 | Operations SHOULD transition to "tombstone" for an additional period of time
92 | prior to being purged from the system.
93 |
94 | - Services that provide a new operation resource MUST support GET semantics on the operation.
95 | - Services that return a new operation MUST always return an LRO (even if the LRO is created in the completed state); that way API consumers don't have to deal with two different shapes of response.
96 |
97 | ## Examples
98 |
99 | ### Create a new resource using RELO
100 |
101 | A client wants to provision a new database:
102 |
103 | ```
104 | POST https://graph.microsoft.com/v1.0/storage/databases/
105 |
106 | {
107 | "displayName": "Retail DB",
108 | }
109 | ```
110 |
111 | The API responds synchronously that the database has been created and indicates
112 | that the provisioning operation is not fully completed by including the
113 | Content-Location header and status property in the response payload:
114 |
115 | ```
116 | HTTP/1.1 201 Created
117 | Location: https://graph.microsoft.com/v1.0/storage/databases/db1
118 |
119 | {
120 | "id": "db1",
121 | "displayName": "Retail DB",
122 | "status": "provisioning",
123 | [ … other fields for "database" …]
124 | }
125 | ```
126 |
127 | The client waits for a period of time, and then invokes another request to try to get the database status:
128 |
129 | ```
130 | GET https://graph.microsoft.com/v1.0/storage/databases/db1
131 |
132 | HTTP/1.1 200 Ok
133 | {
134 | "id": "db1",
135 | "displayName": "Retail DB",
136 | "status": "succeeded",
137 | [ … other fields for "database" …]
138 | }
139 | ```
140 |
141 | ### Cancel RELO operation
142 |
143 | A client wants to cancel provisioning of a new database:
144 |
145 | ```
146 | DELETE https://graph.microsoft.com/v1.0/storage/databases/db1
147 |
148 | ```
149 |
150 | The API responds synchronously that the database is being deleted and indicates
151 | that the operation is accepted and is not fully completed by including the
152 | status property in the response payload. The API might provide a
153 | recommendation to wait for 30 seconds:
154 |
155 | ```
156 | HTTP/1.1 202 Accepted
157 | Retry-After: 30
158 |
159 | {
160 | "id": "db1",
161 | "displayName": "Retail DB",
162 | "status": "deleting",
163 | [ … other fields for "database" …]
164 | }
165 | ```
166 |
167 | The client waits for a period of time, and then invokes another request to try to get the deletion status:
168 |
169 | ```
170 | GET https://graph.microsoft.com/v1.0/storage/databases/db1
171 |
172 | HTTP/1.1 404 Not Found
173 | ```
174 | ### Create a new resource using the stepwise operation
175 |
176 | ```
177 | POST https://graph.microsoft.com/v1.0/storage/archives/
178 |
179 | {
180 | "displayName": "Image Archive",
181 | ...
182 | }
183 | ```
184 |
185 | The API responds synchronously that the request has been accepted and includes
186 | the Location header with an operation resource for further polling:
187 |
188 | ```
189 | HTTP/1.1 202 Accepted
190 |
191 | Location: https://graph.microsoft.com/v1.0/storage/operations/123
192 |
193 | ```
194 |
195 | ### Poll on a stepwise operation
196 |
197 | ```
198 |
199 | GET https://graph.microsoft.com/v1.0/storage/operations/123
200 | ```
201 |
202 | The server responds that results are still not ready and optionally provides a
203 | recommendation to wait 30 seconds:
204 |
205 | ```
206 | HTTP/1.1 200 OK
207 | Retry-After: 30
208 |
209 | {
210 | "createdDateTime": "2015-06-19T12-01-03.4Z",
211 | "lastActionDateTime": "2015-06-19T12-01-03.45Z",
212 | "status": "running"
213 | }
214 | ```
215 |
216 | The client waits the recommended 30 seconds and then invokes another request to get
217 | the results of the operation:
218 |
219 | ```
220 | GET https://graph.microsoft.com/v1.0/storage/operations/123
221 | ```
222 |
223 |
224 | The server responds with a "status:succeeded" operation that includes the resource
225 | location:
226 |
227 | ```
228 | HTTP/1.1 200 OK
229 |
230 | {
231 | "createdDateTime": "2015-06-19T12-01-03.45Z",
232 | "lastActionDateTime": "2015-06-19T12-06-03.0024Z",
233 | "status": "succeeded",
234 | "resourceLocation": "https://graph.microsoft.com/v1.0/storage/archives/987"
235 | }
236 | ```
237 |
238 | ### Trigger a long running action using the stepwise operation
239 |
240 | ```
241 | POST https://graph.microsoft.com/v1.0/storage/copyArchive
242 |
243 | {
244 | "displayName": "Image Archive",
245 | "destination": "Second-tier storage"
246 | ...
247 | }
248 | ```
249 |
250 | The API responds synchronously that the request has been accepted and includes
251 | the Location header with an operation resource for further polling:
252 |
253 | ```
254 | HTTP/1.1 202 Accepted
255 |
256 | Location: https://graph.microsoft.com/v1.0/storage/operations/123
257 |
258 | ```
259 |
--------------------------------------------------------------------------------
/graph/patterns/namespace.md:
--------------------------------------------------------------------------------
1 | # Namespace
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The namespace pattern provides the ability to organize resource definitions together into a logical set.*
6 |
7 | ## Problem
8 |
9 | When building a complex offering, API designers might need to model many different
10 | resources and their relationships. For a better user experience and
11 | discoverability, related API elements need to be grouped together.
12 |
13 | ## Solution
14 |
15 | API designers can use the namespace attribute of the CSDL schema to declare a
16 | namespace and logically organize related API entities in the Microsoft Graph metadata.
17 |
18 | ```XML
19 |
20 | ...
21 |
22 | ```
23 |
24 | A public namespace must contain the `microsoft.graph.` prefix and be presented in camel
25 | case; that is, `microsoft.graph.myNamespace`. Elements defined in namespaces not prefixed
26 | with `microsoft.graph` will be mapped to the public `microsoft.graph` namespace.
27 |
28 | Namespaces should not include more than two segments following the `microsoft.graph` prefix;
29 | that is, `microsoft.graph.myNamespace.mySubNamespace`.
30 |
31 | Public namespaces must define an alias, and that alias must be the concatenation of
32 | the segments following the `microsoft.graph` prefix with proper camel casing rules applied;
33 | that is, `myNamespaceMySubNamespace`.
34 |
35 | When type casting is required in the API query, request, or response, a fully
36 | qualified type name is represented as concatenation of the namespace or alias,
37 | followed by a dot (`.`) and the type name.
38 |
39 | ## When to use this pattern
40 |
41 | API resource grouping creates a user-friendly experience, keeping all resources for a specific feature close together and limiting the length of IDE prompts such as auto-complete in some programming languages.
42 |
43 | For a consistent user experience, new namespace should be aligned with a top-level API category.
44 |
45 | ## Issues and considerations
46 |
47 | - Microsoft Graph consistency requirements discourage using the same type names for different concepts even within different namespaces. Microsoft Graph type names must be descriptive and should represent a single concept across the API Surface.
48 |
49 | - A namespace must be consistent with an API category in the navigation path according to [Microsoft Graph REST API Guidelines](../GuidelinesGraph.md#uniform-resource-locators-urls).
50 |
51 | - Changing a namespace prefixed with `microsoft.graph`, or moving types between, into, or out of a namespace prefixed with `microsoft.graph`, is a breaking change.
52 |
53 | - To extend a type in a different schema, a service must declare that schema and the type in it. This is conceptually similar to .NET partial types.
54 |
55 | - To reference a type in a different schema, simply refer to that type by its fully qualified name (namespace + type name).
56 |
57 | - Cyclical references between namespaces are not allowed because many object-oriented languages don’t support cycles between namespaces.
58 |
59 | - Microsoft Graph has some predefined constraints for declared namespaces:
60 |
61 | - All public namespaces must have the prefix `microsoft.graph`.
62 |
63 | - Public namespaces must declare an alias that is the concatenation of the segments following the `microsoft.graph` prefix.
64 |
65 | - At most, two levels of nesting below `microsoft.graph` is recommended.
66 |
67 | - If a namespace does not begin with the `microsoft.graph` prefix, all types in the schema are mapped into the public `microsoft.graph` namespace.
68 |
69 | ## Examples
70 |
71 | ### Namespace and type declarations
72 |
73 | ```XML
74 | ”\>
75 | …
76 |
78 | …
79 |
80 | ```
81 |
82 | Fully qualified type name: `microsoft.graph.search.bookmark`
83 |
84 | ### Managing multiple schemas
85 |
86 | Workloads must define schemas in their CSDL by using the Edmx format.
87 | Following is an example of a workload that exposes multiple namespaces.
88 |
89 | > **Tip:** As with schemas that exist in the `microsoft.graph` namespace, defining an
90 | entity type is optional; by default your schema derives all entity types
91 | from `microsoft.graph.entity`.
92 |
93 | > **Warning:** Do not deviate from the general structure in the following example.
94 | The schema validation tool expects the XML structure (including XML namespace
95 | declarations) to match this example.
96 |
97 | ```XML
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 | ```
--------------------------------------------------------------------------------
/graph/patterns/navigation-property.md:
--------------------------------------------------------------------------------
1 | # Navigation Property
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *A navigation property is used to identify a relationship between resources.*
6 |
7 | ## Problem
8 | --------
9 |
10 | It is often valuable to represent a relationship between resources in an API.
11 |
12 | Relationships between resources are often implicitly represented by a property contained in one of the resources that provides a key to a related resource. Usually that information is returned in a representation as an id value and the property is named using a convention that identifies the target type of related resource. e.g. userId
13 |
14 | The use of foreign key properties to describe related resources is a weakly typed mechanism and requires additional information for a developer to traverse the relationship. Discovery of related resources is not trivial.
15 |
16 | ## Solution
17 | --------
18 |
19 | Navigation properties are an [OData convention](https://docs.microsoft.com/en-us/odata/webapi/model-builder-untyped#navigation-property) defined in the [CSDL Specification](https://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#_Toc38530365) that allows an API designer to describe a special kind of property in a model that references an related entity. In the HTTP API this property name translates to a path segment that can be appended to the URL of the primary resource in order to access a representation of the related resource. This prevents the client from needing to know any additional information on how to construct the URL to the related resource and the client does not need to retrieve the primary resource if it is only interested in the related resource. It is the responsibility of the API implementation to determine the Id of the related resource and return the representation of the related entity. For example:
20 |
21 | - /user/{userId}/manager represents many-to-one relationship
22 | - /user/{userId}/messages represents one-to-many relationship
23 |
24 | Additionally, using the OData Expand query parameter, related entities can be nested into the primary entity so both can be retrieved in a single round trip.
25 |
26 | These relationships can be described in CSDL as follows:
27 |
28 | ```xml
29 |
30 |
31 |
32 |
33 | ```
34 |
35 | ## Issues and Considerations
36 | -------------------------
37 |
38 | In the current Microsoft Graph implementation, there are scenarios which use navigation properties that cross backend services that have automatic support; there are also some limitations for other scenarios. These limitations are being eliminated over time, but it will be necessary to ensure support for any particular scenario. [Automatic support and limitations of the current implementation](https://dev.azure.com/msazure/One/_wiki/wikis/Microsoft%20Graph%20Partners/354352/Cross-workload-navigations?anchor=supported-scenarios) are documented internally.
39 |
40 | Navigation properties defined within an entity are not returned by default when retreiving the representation of an entity unless explicity desired by a service. The API can consumer can use the `expand` query parameterm, where supported, to retreive both the source and the target entity of the relationship in a single request.
41 |
42 | Implementing support for accessing the "$ref" of a navigation property allows a caller to return just the URL of related resource. e.g. `/user/23/manager/$ref`. This is useful when a client wishes to identify the related resource but doesn't need all of its properties.
43 |
44 | The strongly-typed nature of navigation properties is valuable for backend services and for client applications, when compared with the weakly-typed foreign key property.
45 | Strong typing allows some documentation and visualizations to be automatically generated, it allows SDK generation, and it allows some automated client code generation; it also prevents the need to store duplicate data on the service side and as a result has improved data consistency across APIs since the duplicate data does not need to be regularly refreshed.
46 |
47 | ## When to Use this Pattern
48 | ------------------------
49 |
50 | ### "Many-to-one" relationships
51 |
52 | The use of navigation properties is preferred over including an Id field to reference the related entity in a many-to-one relationship. Id values require a client to make two round trips to retrieve the details of a related entity. With a navigation property a client can retrieve a related entity in a single round trip.
53 |
54 | Many-to-one relationships are always non-contained relationships as the lifetime of the target cannot depend on the source.
55 |
56 | ```xml
57 |
58 |
59 |
60 | ```
61 |
62 |
63 | ### "Zero-or-one-to-one" relationships
64 |
65 | These navigation properties can be used as a structural organization mechanism to separate properties of an entity in a way that is similar to how complex types are often used. The primary difference being that the target of the navigation property are not returned by default when the source entity is retreived. The use of the navigation properties over complex properties is preferred when the source and target information comes from different backend APIs.
66 |
67 | These relationships must be contained.
68 |
69 | ```xml
70 |
71 |
72 |
73 | ```
74 |
75 | ### "One-to-many" relationships
76 |
77 | Resources that contain a parent Id property in a child resource can utilize a navigation property in the parent resource that is declared as a collection of child resources. If desirable, a parent navigation property can also be created in the child resource to the parent resource. This is usually not necessary as the parent URL is a subset of child resource URL. The main use of this would be when retrieving child resources and choosing to expand properties of the parent resource so that both can be retrieved in a single request.
78 |
79 | `/invoice/{invoiceId}/items/{itemId}?expand=parentInvoice(select=invoiceDate,Customer)`
80 |
81 | ```xml
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | ```
90 |
91 | One-to-many relationships may be contained or non-contained relations.
92 |
93 |
94 | ## Example
95 | -------
96 |
97 | ### Retrieving a related entity
98 |
99 | ```http
100 | GET /users/{id}/manager?$select=id,displayName
101 |
102 | 200 OK
103 | Content-Type: application/json
104 |
105 | {
106 | "id": "6b3ee805-c449-46a8-aac8-8ff9cff5d213",
107 | "displayName": "Bob Boyce"
108 | }
109 | ```
110 |
111 | This navigation property could be described with the following CSDL:
112 | ```xml
113 |
114 |
115 |
116 | ```
117 | `ContainsTarget` is set to false for clarity, this is the default value when the attribute is omitted.
118 | ### Retrieving a reference to a related entity
119 |
120 | ```http
121 | GET /users/{id}/manager/$ref
122 |
123 | 200 OK
124 | Content-Type: application/json
125 |
126 | {
127 | "@odata.id": "https://graph.microsoft.com/v1.0/directoryObjects/6b3ee805-c449-46a8-aac8-8ff9cff5d213/Microsoft.DirectoryServices.User"
128 | }
129 | ```
130 | Note: Currently the base URL returned in $ref results are incorrect. In order to process these URLs the client will need to convert the URL to a Graph URL.
131 |
132 | ### Retrieving an entity with a related entity included
133 |
134 | ```http
135 | GET /users/{id}?select=id,displayName&expand=manager(select=id,displayName)
136 |
137 | 200 OK
138 | Content-Type: application/json
139 |
140 | {
141 | "id": "3f057904-f936-4bf0-9fcc-c1e6f84289d8",
142 | "displayName": "Jim James",
143 | "manager": {
144 | "@odata.type": "#microsoft.graph.user",
145 | "id": "6b3ee805-c449-46a8-aac8-8ff9cff5d213",
146 | "displayName": "Bob Boyce"
147 | }
148 | }
149 | ```
150 |
151 | ### Creating an entity with a reference to a related entity
152 |
153 | Create a new user that references an existing manager
154 | ```http
155 | POST /users
156 | Content-Type: application/json
157 |
158 | {
159 | "displayName": "Bob",
160 | "manager@odata.bind": "https://graph.microsoft.com/v1.0/users/{managerId}"
161 | }
162 |
163 | 201 Created
164 | ```
165 |
166 | ### Updating a related entity reference
167 |
168 | Update the user entity to contain a relationship to an existing manager.
169 |
170 | ```http
171 | PATCH /users/{id}
172 | Content-Type: application/json
173 |
174 | {
175 | "displayName": "Bob",
176 | "manager@odata.bind": "https://graph.microsoft.com/v1.0/users/{managerId}"
177 | }
178 |
179 | 204 No Content
180 | ```
181 |
182 | ### Clear a related entity reference
183 |
184 | Remove the relationship between the user and the manager.
185 |
186 | ```http
187 | DELETE /users/{id}/manager/$ref
188 |
189 | 204 No Content
190 | ```
191 |
192 | Delete the related entity.
193 |
194 | ```http
195 | DELETE /users/{id}/manager
196 |
197 | 204 No Content
198 | ```
199 |
--------------------------------------------------------------------------------
/graph/patterns/operations.md:
--------------------------------------------------------------------------------
1 | # Operations
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The operations pattern provides the ability to model a change that might impact multiple resources and can't be effectively modeled by using HTTP methods.*
6 |
7 | ## Problem
8 |
9 | Sometimes when modeling a complex business domain, API designers need to model a business operation that effects one or multiple resources and has additional semantic meaning that cannot be expressed by HTTP methods. Modeling the operation via HTTP methods on each individual resource might be either inefficient or expose internal implementation details.
10 |
11 | ## Solution
12 |
13 | To address these use cases, API designers can use operational resources such as functions or actions. If the operation doesn't have any side effects and MUST return a single instance of a type or a collection of instances, then the designer SHOULD use OData functions; otherwise, the designer can model the operation as an action.
14 |
15 | ## When to use this pattern
16 |
17 | The operation pattern might be justified when a modeling operation represents one or combination of the following:
18 |
19 | - a change of a resource (i.e., increment the value of a property) rather than a state (i.e., the final value of the property)
20 | - complex processing logic that shouldn't be exposed to the client
21 | - operation parameters might convey a restricted set of option (i.e., a report that has to specify a date range)
22 | - the operation leverage some service-side data not exposed to (or easily retrieved in context by) the user.
23 |
24 | You can consider related patterns such as [long running operations](./long-running-operations.md) and [change tracking](./change-tracking.md).
25 |
26 | ## Issues and considerations
27 |
28 | - Microsoft Graph does NOT support unbound actions or functions. Bound actions and functions MUST must have the `isBound="true"` attribute and a binding parameter. Bound operations are invoked on resources matching the type of the binding parameter.The first parameter of a bound operation is always the binding parameter.The binding parameter can be of any type, and parameter value MAY be Nullable.
29 |
30 | - Both actions and functions support overloading, meaning a schema might contain multiple actions or functions with the same name. The overload rules as per the OData [standard](http://docs.oasis-open.org/odata/odata-csdl-xml/v4.01/odata-csdl-xml-v4.01.html#sec_FunctionOverloads) apply when adding parameters to actions and functions.
31 |
32 | - Because Microsoft Graph only supports bound actions and functions, all must have at least one parameter where the first is the binding parameter. The MUSTS of parameters are as follows:
33 |
34 | - Each parameter must have a simple identifier name.
35 | - The parameter name must be unique within the overload.
36 | - The parameter must specify a type.
37 |
38 | - Microsoft Graph supports the use of optional parameters. The optional parameter annotation can be used instead of creating function or action overloads when unnecessary.
39 |
40 | - API designer **MUST** use POST to call actions on resources.
41 | - API designer **MUST** use GET to call functions on resources.
42 |
43 | - The addition of a new mandatory not-nullable parameter to an existing action or function is a breaking change and is not allowed without proper versioning that is in accordance with our [deprecation guidelines](https://github.com/microsoft/api-guidelines/blob/vNext/graph/deprecation.md).
44 |
45 | ## Examples
46 |
47 | ### A user wants to forward email
48 |
49 | ```
50 | POST https://graph.microsoft.com/v1.0/me/messages/AQMkADNkMmMxYzIwLWJkOTItNDczZC1hNmYyLWUwZjk2ZTljMDQyNQBGAAAD1dY5iRo4x0_pEqop6hOrQAcAeGCrbYV1-kiG-z9Rv6yHMgAAAgEJAAAAeGCrbYV1-kiG-z9Rv6yHMgABRxeUKgAAAA==/forward
51 |
52 | {
53 | "comment": "FYI",
54 | "toRecipients": [
55 | {
56 | "emailAddress": {
57 | "address": "alex.darrow@microsoft.com",
58 | "name": "Alex Darrow"
59 | }
60 | }
61 | ]
62 | }
63 | ```
64 | Response:
65 | ```
66 | HTTP/1.1 202 Accepted
67 |
68 | "cache-control": "private",
69 | "client-request-id": "ca2d0416-a2c1-05af-df60-0921547a86e9",
70 | "content-length": "0",
71 | "request-id": "8b53016f-cc2b-4d9f-9818-bd6f0a5e3cd0"
72 | ```
73 |
74 | `forward` operation is modeled as an asynchronous action bound to the Graph `message` entity type because the operation represents a complex business logic processed on the server side.
75 | ```
76 |
77 |
78 |
79 |
80 |
81 |
82 | ```
83 |
84 | ### A user wants to see recent application activities
85 |
86 | ```
87 | GET https://graph.microsoft.com/v1.0/me/activities/recent
88 | ```
89 |
90 | Response:
91 |
92 | ```
93 | HTTP/1.1 200 OK
94 |
95 | {
96 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#Collection(userActivity)",
97 | "value": []
98 | }
99 | ```
100 | `recent` function will query the most recent historyItems and then pull related activities therefore the operation represents a complex business logic processed on the server side. This operation doesn't change any server data and is a good fit for a function. The function is bound to the collection of `userActivity` entity type.
101 |
102 | ```
103 |
104 |
105 |
106 |
107 | ```
108 | ### Get a report that provides the number of active users using Microsoft Edge
109 |
110 | ```
111 | https://graph.microsoft.com/beta/reports/getBrowserUserCounts(period='D7')
112 | ```
113 |
114 | Response:
115 |
116 | ```
117 | HTTP/1.1 200 OK
118 | Content-Type: application/json
119 | Content-Length: 205
120 |
121 | {
122 | "value":[
123 | {
124 | "reportRefreshDate":"2021-04-17",
125 | "reportPeriod":7,
126 | "userCounts":[
127 | {
128 | "reportDate":"2021-04-17",
129 | "edge":413
130 | },
131 | {
132 | "reportDate":"2021-04-16",
133 | "edge":883
134 | }
135 | ]
136 | }
137 | ]
138 | }
139 | ```
140 |
141 | `getBrowserUserCounts` operation doesn't change any server data and is a good fit for a function.`period` operation parameter convey a restricted set of options representing the number of days over which the report is aggregated. The report supports only 7,30,90, or 180 days. In addition the function doesn't return a Graph resource but streams response data in JSON or CSV formats.
142 |
143 | ```
144 |
145 |
146 |
147 |
148 |
149 | ```
150 |
--------------------------------------------------------------------------------
/graph/patterns/subsets.md:
--------------------------------------------------------------------------------
1 | # Modeling collection subsets
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The modeling collection subsets pattern is the modeling state associated to a collection that may include all instances, an included subset, an excluded subset, no instances, or any combinations of the preceding items.*
6 |
7 | ## Problem
8 |
9 | A common pattern is to apply a policy or state to a collection of resources. With this, there also comes the question of how to model cases where we want to apply to `all` or `none` without having to special case these values within the collection set or introduce cross-property dependencies. Likewise, we'd like to model it in a way where it is easy to understand and interpret usage from just looking at the schema.
10 |
11 | An example is where you have a policy that you need to be able to apply to users in an organization. You might want to support the default **None**, enablement for **All**, or enablement for **Select** users where you only grant it to a few users.
12 |
13 | Existing patterns for this either have special-cased strings or have tightly coupled dependencies between two independent properties. Neither is intuitive, both require reading documentation, and neither can be inferred from the schema or within client libraries.
14 |
15 | ## Solution
16 |
17 | Have an abstract base class where all variants of the subset are derived types from the base subset. For more information, see the [general subtyping guidance](./subtypes.md).
18 |
19 | The abstract base class may also optionally hold an `enum` for the different variants. If it does, the `enum` must have a member for all possible variants. The purpose of including this is to allow for easier ways to do query and filter operations on variants like `all` and `none` without relying on `isof` functions.
20 |
21 | **Base type *without* an enum for the variants**
22 |
23 | ```xml
24 |
25 | ```
26 |
27 | **Base type *with* an enum for the variants**
28 |
29 | ```xml
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | ```
41 |
42 | **Derived types**
43 |
44 | ```xml
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | ```
57 |
58 | Be aware that the name values and types in the preceding examples are just examples and can be replaced with your scenario equivalent values. For example, type names don't really need to be `memberships`. The collection doesn't have to be a collection at all; it can be singular and doesn't have to be a string.
59 |
60 | These pattern type names should satisfy the following naming conventions:
61 |
62 | - The base type name should have the suffix `Base`, and the enumeration type name (if an `enum` is defined) should have the suffix `Kind`.
63 | - Derived child types should have names with enumeration values as the prefixes; for example, if the enumeration member value is `value1`, then the derived type name is `value1`.
64 |
65 | ```xml
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 | ```
82 |
83 | ## When to use this pattern
84 |
85 | Use this pattern when supporting two or more collection states of the following, where at least one of the states is a subset variant:
86 |
87 | - All targets
88 | - No targets
89 | - Subset of targets to be included
90 | - Subset of targets to be excluded
91 |
92 | If you only ever need to support two states—All or None—without using any subsets, it would be better to use a Boolean to toggle on and off.
93 |
94 | ## Issues and considerations
95 |
96 | Given that we are using an overarching subtype model, subtyping model limitations apply here as well; for more details, see the [subtyping documentation](./subtypes.md).
97 |
98 | ## Example
99 |
100 | ```http
101 | GET https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/
102 | ```
103 |
104 | _Note: Unrelated properties on entities are omitted for easier readability._
105 |
106 | ```json
107 | {
108 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#conditionalAccessPolicy",
109 | "values": [
110 | {
111 | "id": "66d36273-fe4c-d478-dc22-e0179d856ce7",
112 | "conditions": {
113 | "users": {
114 | "includeGuestsOrExternalUsers": {
115 | "externalTenants": {
116 | "@odata.type":"microsoft.graph.conditionalAccessAllExternalTenants",
117 | "membershipKind": "all"
118 | }
119 | }
120 | }
121 | }
122 | },
123 | {
124 | "id": "99d212f4-d94e-cde1-8e3c-208d78238277",
125 | "conditions": {
126 | "users": {
127 | "includeGuestsOrExternalUsers": {
128 | "externalTenants": {
129 | "@odata.type":"microsoft.graph.conditionalAccessEnumeratedExternalTenants",
130 | "membershipKind": "enumerated",
131 | "members": ["bd005e2a-876d-4bf0-92a1-ae9ff4276d54"]
132 | }
133 | }
134 | }
135 | }
136 | }
137 | ]
138 | }
139 | ```
140 |
141 | ```http
142 | POST https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/
143 | ```
144 |
145 | _Note: Unrelated properties on entities are omitted for easier readability._
146 |
147 | ```json
148 | {
149 | "id": "66d36273-fe4c-d478-dc22-e0179d856ce7",
150 | "conditions": {
151 | "users": {
152 | "includeGuestsOrExternalUsers": {
153 | "externalTenants": {
154 | "@odata.type":"microsoft.graph.conditionalAccessAllExternalTenants"
155 | }
156 | }
157 | }
158 | }
159 | }
160 | ```
161 |
162 | or
163 |
164 | ```http
165 | POST https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/
166 | ```
167 |
168 | _Note: Unrelated properties on entities are omitted for easier readability._
169 |
170 | ```json
171 | {
172 | "id": "66d36273-fe4c-d478-dc22-e0179d856ce7",
173 | "conditions": {
174 | "users": {
175 | "includeGuestsOrExternalUsers": {
176 | "externalTenants": {
177 | "@odata.type":"microsoft.graph.conditionalAccessEnumeratedExternalTenants",
178 | "members": ["bd005e2a-876d-4bf0-92a1-ae9ff4276d54"]
179 | }
180 | }
181 | }
182 | }
183 | }
184 | ```
185 |
186 | ### Filter when base type has the "kind" enum property
187 |
188 | ```http
189 | GET https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$filter=conditions/users/includeGuestsOrExternalUsers/externalTenants/membershipKind eq 'all'
190 |
191 | 200 OK
192 | {
193 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#conditionalAccessPolicy",
194 | "values": [
195 | {
196 | "id": "66d36273-fe4c-d478-dc22-e0179d856ce7",
197 | "conditions": {
198 | "users": {
199 | "includeGuestsOrExternalUsers": {
200 | "externalTenants": {
201 | "@odata.type":"microsoft.graph.conditionalAccessAllExternalTenants",
202 | "membershipKind": "all"
203 | }
204 | }
205 | }
206 | }
207 | }
208 | ]
209 | }
210 | ```
211 |
212 | ### Filter when base type lacks the "kind" enum property
213 |
214 | ```HTTP
215 | GET https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies?$filter=isof(conditions/users/includeGuestsOrExternalUsers/externalTenants, microsoft.graph.conditionalAccessAllExternalTenants)
216 |
217 | 200 OK
218 | {
219 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#conditionalAccessPolicy",
220 | "values": [
221 | {
222 | "id": "66d36273-fe4c-d478-dc22-e0179d856ce7",
223 | "conditions": {
224 | "users": {
225 | "includeGuestsOrExternalUsers": {
226 | "externalTenants": {
227 | "@odata.type":"microsoft.graph.conditionalAccessAllExternalTenants",
228 | "membershipKind": "all"
229 | }
230 | }
231 | }
232 | }
233 | }
234 | ]
235 | }
236 | ```
237 |
--------------------------------------------------------------------------------
/graph/patterns/subtypes.md:
--------------------------------------------------------------------------------
1 | # Type hierarchy
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *A frequent pattern in Microsoft Graph is to have a small type hierarchy, a base type with a few subtypes. This lets us model collections of resources that have slightly different properties and behavior.*
6 |
7 | ## Problem
8 |
9 | The API design requires that we model a set of resources based on a common concept
10 | that can be further grouped into *mutually exclusive variants* with specific
11 | properties and behaviors. The API design should be evolvable and allow the addition
12 | of new variants without breaking changes.
13 |
14 | ## Solution
15 |
16 | API designers might use a *type hierarchy*, where there is one base
17 | type (which might be abstract) with a few shared properties representing the common concept and one
18 | subtype for each variant of the resource. In the hierarchy, the interdependencies of properties, that is, which properties are relevant for which variants, is fully captured in the type system.
19 |
20 | ## When to use this pattern
21 |
22 | Use this pattern where each variant of a common concept has its own unique properties and behaviors,
23 | no combination of variants is anticipated, and it is acceptable that callers who need to query resources by variant are adequately served by filtering or partitioning using type casting.
24 |
25 | You can consider related patterns such as [facets](./facets.md) and [flat bag of properties](./flat-bag.md).
26 |
27 | ## Issues and considerations
28 |
29 | When introducing a new subtype to the hierarchy, developers need to ensure that
30 | the new subtype doesn't change the semantic of the type hierarchy or collections of the specified base type with implicit constraints.
31 |
32 | To reference properties specific to a derived type, an API request URL might need to include a segment casting to the derived type. If the type hierarchy is very deep, then the resulting URL might become very long and not easily readable.
33 |
34 | There are a few considerations to take into account when new subtypes are introduced:
35 |
36 | - *TODO add something about SDK dependencies and required actions*
37 | - *TODO* Client libraries for a strongly typed language might ignore some of the values
38 | in the @odata.type property without further configuration and need to be
39 | updated to be able to pick the right (client) type to deserialize into.
40 | - In the case of public APIs in GA versions, clients might develop their applications to support exclusively the current set of subtypes, and don’t expect new variations. To mitigate the risk of clients' disruption, when introducing a new subtype, allow ample time for communication and rollout.
41 |
42 | ## Example
43 |
44 | The directoryObject type is the main abstraction for many directory
45 | types such as users, organizational contacts, devices, service principals,
46 | and groups stored in Azure Active Directory. Because any directoryObject object is a unique entity, the directoryObject type itself is derived from the `graph.entity` base type.
47 |
48 | ```XML
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | ```
59 |
60 | Groups and users are derived types and modeled as follows:
61 |
62 | ```XML
63 |
64 |
65 | ...
66 |
67 |
68 |
69 | ...
70 |
71 | ```
72 |
73 | An API request to get members of a group returns a heterogeneous collection of
74 | users and groups where each element can be a user or a group, and has an
75 | additional `@odata.type` property that specifies the subtype:
76 |
77 | ```
78 | GET https://graph.microsoft.com/v1.0/groups/a94a666e-0367-412e-b96e-54d28b73b2db/members
79 |
80 | Response payload shortened for readability. The deletedDateTime property from the base type is a non-default property and is only returned if explicitly requested.
81 |
82 | {
83 | "@odata.context":
84 | "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
85 | "value": [
86 | {
87 | "@odata.type": "#microsoft.graph.user",
88 | "id": "37ca648a-a007-4eef-81d7-1127d9be34e8",
89 | "jobTitle": "CEO",
90 | ...
91 | },
92 | {
93 | "@odata.type": "#microsoft.graph.group",
94 | "id": "45f25951-d04f-4c44-b9b0-2a79e915658d",
95 | "description": "Microsoft Graph API Reviewers",
96 | ...
97 | },
98 | ...
99 | ]
100 | }
101 | ```
102 |
103 | Addressing a property of the subtype, for example, in `$filter` or `$select`, requires prefixing the property with the fully-qualified name of the subtype (or type derived from the subtype) on which it is defined. To filter on the `jobTitle` for the user type, you need to qualify the property with `microsoft.graph.user`.
104 |
105 | The following query returns all groups that are members of group a94a666e-0367-412e-b96e-54d28b73b2db, as well as users that are members and whose jobTitle is CEO.
106 |
107 | ```
108 | GET https://graph.microsoft.com/v1.0/groups/a94a666e-0367-412e-b96e-54d28b73b2db/members?$filter=microsoft.graph.user/jobTitle eq 'CEO'
109 |
110 | Response payload shortened for readability:
111 |
112 | {
113 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
114 | "value": [
115 | {
116 | "@odata.type": "#microsoft.graph.user",
117 | "id": "37ca648a-a007-4eef-81d7-1127d9be34e8",
118 | "jobTitle": "CEO",
119 | ...
120 | },
121 | {
122 | "@odata.type": "#microsoft.graph.group",
123 | "id": "45f25951-d04f-4c44-b9b0-2a79e915658d",
124 | "description": "Microsoft Graph API Reviewers",
125 | ...
126 | },
127 | ...
128 | ]
129 | }
130 | ```
131 |
132 | An entire collection can be cast to a particular subtype by appending the fully-qualified subtype name to the URL. Doing so filters the collection to members of (or derived from) that particular subtype, and makes the properties of that subtype available without casting. In this case, the `@odata.type` attribute is not returns for records of the specified subtype because the `@odata.context` indicates that the entire collection is consists of the particular subtype. Types derived from that subtype do still have the `@odata.type` attribute.
133 |
134 | The following query returns only users that are members of group a94a666e-0367-412e-b96e-54d28b73b2db and whose jobTitle is CEO.
135 |
136 | ```
137 | GET https://graph.microsoft.com/v1.0/groups/a94a666e-0367-412e-b96e-54d28b73b2db/members/microsoft.graph.user?$filter=jobTitle eq 'CEO'
138 |
139 | Response payload shortened for readability:
140 |
141 | {
142 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users",
143 | "value": [
144 | {
145 | "id": "37ca648a-a007-4eef-81d7-1127d9be34e8",
146 | "jobTitle": "CEO",
147 | ...
148 | },
149 | ...
150 | ]
151 | }
152 | ```
153 |
154 | An API request to create a subtype object in a polymorphic collection requires "@odata.type" specified in the request body.
155 |
156 | ```
157 | POST https://graph.microsoft.com/v1.0/directoryObjects
158 |
159 | {
160 | "@odata.type": "#microsoft.graph.group",
161 | "description": "Microsoft Graph API Reviewers",
162 | ...
163 | }
164 | ```
165 |
--------------------------------------------------------------------------------
/graph/patterns/upsert.md:
--------------------------------------------------------------------------------
1 | # Upsert
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 | *The `Upsert` pattern is a non-destructive idempotent operation using a client-provided key, that ensures that system resources can be deployed in a reliable, repeatable, and controlled way, typically used in Infrastructure as Code (IaC) scenarios.*
6 |
7 | ## Problem
8 |
9 | Infrastructure as code (IaC) defines system resources and topologies in a declarative manner that allows teams to manage those resources as they would code.
10 | Practicing IaC helps teams deploy system resources in a reliable, repeatable, and controlled way.
11 | IaC also helps automate deployment and reduces the risk of human error, especially for complex large environments.
12 | Customers want to adopt IaC practices for many of the resources managed through Microsoft Graph.
13 |
14 | Most resources' creation operations in Microsoft Graph are not idempotent in nature.
15 | As a consequence, API consumers that want to offer IaC solutions, must create compensation layers that can mimic idempotent behavior.
16 | For example, when creating a resource, the compensation layer must check whether the resource first exists, before trying to create or update the resource.
17 |
18 | Additionally, IaC code scripts or templates usually employ client-provided names (or keys) to track resources in a predictable manner, whereas [Microsoft Graph guidelines](../GuidelinesGraph.md#behavior-modeling) suggests use of `POST` to create new entities with service-generated keys.
19 |
20 | ## Solution
21 |
22 | The solution is to use an `Upsert` pattern, to solve for the non-idempotent creation and client-provided naming problems.
23 |
24 | * `Upsert` uses `PATCH` with a client-provided key in the URL:
25 | * If there is a natural client-provided key that can serve as the primary key, then the service should support `Upsert` with that key.
26 | * If the primary key is service-generated, the client-provided key should use an [alternate key](./alternate-key.md) to support idempotent creation.
27 | * For a non-existent resource (specified by the client-provided key) the service must handle this as a "create" (aka insert). As part of creation, the service must still generate the primary key value, if appropriate.
28 | * For an existing resource (specified by the client-provided key) the service must handle this as an "update".
29 | * If using an alternate key, then
30 | * for IaC scenarios, the alternate key should be called `uniqueName`, if there isn't already a more natural existing property that could be used as an alternate key.
31 | * the service must also support `GET` using the alternate key pattern.
32 | * Services should always support `POST` to the collection URL.
33 | * For service-generated keys, this should return the server generated key.
34 | * For client-provided keys, the client can provide the key as part of the request payload.
35 | * If a service does not support `Upsert`, then a `PATCH` call against a non-existent resource must result in an HTTP "404 not found" error.
36 |
37 | This solution allows for existing resources that follow Microsoft Graph conventions for CRUD operations to add `Upsert` without impacting existing apps or functionality.
38 |
39 | Ideally, all new entity types should support an `Upsert` mechanism, especially where they support control-plane APIs, or are used in admin style or IaC scenarios.
40 |
41 | ## When to use this pattern
42 |
43 | This pattern should be adopted for resources that are managed through infrastructure as code or desired state configuration.
44 |
45 | ## Issues and considerations
46 |
47 | * Services with existing APIs that use a client-defined key that want to start supporting the `Upsert` pattern may have concerns about backwards compatibility.
48 | API producers can require clients to opt-in to the `Upsert` pattern, by using the `Prefer: create-if-missing` HTTP request header.
49 | * `Upsert` can also be supported against singletons, using a `PATCH` to the singleton's URL.
50 | * Services that support `Upsert` should allow clients to use the:
51 | * `If-Match=*` request header to explicitly treat an `Upsert` request as an update and not an insert.
52 | * `If-None-Match=*` request header to explicitly treat an `Upsert` request as an insert and not an update.
53 | * The client-provided alternate key must be immutable after being set. If its value is null then it should be settable as a way to backfill existing resources for use in IaC scenarios.
54 | * API producers could use `PUT` operations to create or update, but generally this approach is not recommended due to the destructive nature of `PUT`'s replace semantics.
55 | * API producers may annotate entity sets, singletons and collections to indicate that entities can be "upserted". The example below shows this annotation for the `groups` entity set.
56 |
57 | ```xml
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | ```
66 |
67 | ## Examples
68 |
69 | For these examples we'll use the `group` entity type, which defines both a primary (service-generated) key (`id`) and an alternate (client-provided) key (`uniqueName`).
70 |
71 | ```xml
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | ```
96 |
97 | ### Upserting a record (creation path)
98 |
99 | Create a new group, with a `uniqueName` of "Group157". In this case, this group does not exist.
100 |
101 | ```http
102 | PATCH /groups(uniqueName='Group157')
103 | Prefer: return=representation
104 | ```
105 |
106 | ```json
107 | {
108 | "displayName": "My favorite group",
109 | "description": "All my favorite people in the world"
110 | }
111 | ```
112 |
113 | Response:
114 |
115 | ```http
116 | 201 created
117 | Preference-Applied: return=representation
118 | ```
119 |
120 | ```json
121 | {
122 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66",
123 | "displayName": "My favorite group",
124 | "description": "All my favorite people in the world",
125 | "uniqueName": "Group157"
126 | }
127 | ```
128 |
129 | ### Upserting a record (update path)
130 |
131 | Create a new group, with a `uniqueName` of "Group157", exactly like before. Except in this case, this group already exists. This is a common scenario in IaC, when a deployment template is re-run multiple times.
132 |
133 | ```http
134 | PATCH /groups(uniqueName='Group157')
135 | Prefer: return=representation
136 | ```
137 |
138 | ```json
139 | {
140 | "displayName": "My favorite group",
141 | "description": "All my favorite people in the world"
142 | }
143 | ```
144 |
145 | Response:
146 |
147 | ```http
148 | 200 ok
149 | Preference-Applied: return=representation
150 | ```
151 |
152 | ```json
153 | {
154 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66",
155 | "displayName": "My favorite group",
156 | "description": "All my favorite people in the world",
157 | "uniqueName": "Group157"
158 | }
159 | ```
160 |
161 | Notice how this operation is idempotent in nature, rather than returning a 409 conflict error.
162 |
163 | ### Updating a record
164 |
165 | Update "Group157" group with a new description.
166 |
167 | ```http
168 | PATCH /groups(uniqueName='Group157')
169 | Prefer: return=representation
170 | ```
171 |
172 | ```json
173 | {
174 | "description": "Some of my favorite people in the world."
175 | }
176 | ```
177 |
178 | Response:
179 |
180 | ```http
181 | 200 ok
182 | Preference-Applied: return=representation
183 | ```
184 |
185 | ```json
186 | {
187 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66",
188 | "displayName": "My favorite group",
189 | "description": "Some of my favorite people in the world.",
190 | "uniqueName": "Group157"
191 | }
192 | ```
193 |
194 | ### Upsert opt-in request
195 |
196 | In this case, the group API is a pre-existing API that supports `PATCH` with a client-provided alternate key. To enable `Upsert` behavior,
197 | the client must opt-in using an HTTP request header, to create a new group using `PATCH`.
198 |
199 | ```http
200 | PATCH /groups(uniqueName='Group157')
201 | Prefer: create-if-missing; return=representation
202 | ```
203 |
204 | ```json
205 | {
206 | "displayName": "My favorite group",
207 | "description": "All my favorite people in the world"
208 | }
209 | ```
210 |
211 | Response:
212 |
213 | ```http
214 | 201 created
215 | Preference-Applied: create-if-missing; return=representation
216 | ```
217 |
218 | ```json
219 | {
220 | "id": "1a89ade6-9f59-4fea-a139-23f84e3aef66",
221 | "displayName": "My favorite group",
222 | "description": "All my favorite people in the world",
223 | "uniqueName": "Group157"
224 | }
225 | ```
226 |
227 | ### Upsert (create) not supported
228 |
229 | Following on from the last example, the same request to create a new group, with a `uniqueName` of "Group157",
230 | without the opt-in header, results in a 404 HTTP response code.
231 |
232 | ```http
233 | PATCH /groups(uniqueName='Group157')
234 | Prefer: return=representation
235 | ```
236 |
237 | ```json
238 | {
239 | "displayName": "My favorite group",
240 | "description": "All my favorite people in the world"
241 | }
242 | ```
243 |
244 | Response:
245 |
246 | ```http
247 | 404 not found
248 | ```
249 |
--------------------------------------------------------------------------------
/graph/patterns/viewpoint.md:
--------------------------------------------------------------------------------
1 | # Viewpoint
2 |
3 | Microsoft Graph API Design Pattern
4 |
5 |
6 | *The viewpoint pattern provides the ability to manage properties of a shared object that have different values for different users.*
7 |
8 | ## Problem
9 | A shared resource, such as a website or a group message, may have different states for different users who access it at different times in an organizational context. For example, user1 may read and delete a message, while user2 may not have seen it yet. This usually happens when a shared item is presented in an individual context.
10 | ## Solution
11 |
12 | The viewpoint pattern provides a solution to how to model an individual user context on a shared resource using a `viewpoint` structural property on an API entity type.
13 | For example, the `viewpoint` property can indicate whether a message is read, deleted, or flagged for a given user.
14 | The consistent naming convention ensures that when a developer uses Graph APIs all `viewpoint` structural properties represent type specific user context across different M365 services and features.
15 |
16 | This pattern simplifies the API client logic by hiding the state transition details and providing state persistency on the server side. The server can manage the different viewpoints for the shared resource without exposing additional complexity to the client. To support queries for a user state the `viewpoint` property should support filtering.
17 | ## Issues and considerations
18 |
19 | - Because the `viewpoint` property reflects an individual user's context, it is null when accessed with application permissions.
20 | - Sometimes, the viewpoint can be computed on the server. In this case, an API producer should add OData annotations to the property to provide more information for downstream tools, such as SDKs and documentation generation.
21 | ```
22 |
23 |
24 |
25 | ```
26 | - An alternative to this design would be to store the user state on the client side. However, this may be problematic in some cases, because of the many devices that a user may have and the need to synchronize the state across them.
27 | - Often, updating the `viewpoint` property may cause a side effect, so you might consider an OData action to do the update. For some user scenarios, the `PATCH` method could be a better way to update a `viewpoint`.
28 |
29 | ## Examples
30 |
31 | ### Defining a viewpoint
32 |
33 | The following example demonstrates how to define the 'viewpoint' property for the `chat` entity, where a chat is a collection of chatMessages between one or more participants:
34 | ```
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ...
47 |
48 |
49 |
50 | ```
51 | ### Reading an entity with a viewpoint
52 |
53 | The following example shows reading a collection of chats for an identified user, with a viewpoint for each chat:
54 |
55 | ```http
56 | GET https://graph.microsoft.com/v1.0/users/8b081ef6-4792-4def-b2c9-c363a1bf41d5/chats
57 | ```
58 |
59 | ```http
60 |
61 | HTTP/1.1 200 OK
62 | Content-type: application/json
63 | ```
64 |
65 | ```
66 | {
67 | "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats",
68 | "@odata.count": 3,
69 | "value": [
70 | {
71 | "id": "19:meeting_MjdhNjM4YzUtYzExZi00OTFkLTkzZTAtNTVlNmZmMDhkNGU2@thread.v2",
72 | "topic": "Meeting chat sample",
73 | "createdDateTime": "2020-12-08T23:53:05.801Z",
74 | "lastUpdatedDateTime": "2022-12-08T23:58:32.511Z",
75 | "chatType": "meeting",
76 | "viewpoint":{
77 | "lastMessageReadDateTime": "2021-03-28T21:10:00.000Z"
78 | }
79 | },
80 | {
81 | "id": "19:561082c0f3f847a58069deb8eb300807@thread.v2",
82 | "topic": "Group chat sample",
83 | "createdDateTime": "2020-12-03T19:41:07.054Z",
84 | "lastUpdatedDateTime": "2020-12-08T23:53:11.012Z",
85 | "chatType": "group",
86 | "viewpoint":{
87 | "lastMessageReadDateTime": "0000-01-01T00:00:00.000Z"
88 | }
89 | }
90 | ]
91 | }
92 | ```
93 | ### Updating a viewpoint using an action
94 |
95 | The following example shows marking a chat `viewpoint` as read for a user using an action:
96 |
97 | ```http
98 |
99 | POST https://graph.microsoft.com/beta/chats/19:7d898072-792c-4006-bb10-5ca9f2590649_8ea0e38b-efb3-4757-924a-5f94061cf8c2@unq.gbl.spaces/markChatReadForUser
100 |
101 | {
102 | "user": {
103 | "id" : "d864e79f-a516-4d0f-9fee-0eeb4d61fdc2",
104 | "tenantId": "2a690434-97d9-4eed-83a6-f5f13600199a"
105 | }
106 | }
107 | ```
108 |
109 | The server responds with a success status code and no payload:
110 |
111 | ```http
112 | HTTP/1.1 204 No Content
113 | ```
114 | ### Updating a viewpoint using `PATCH` method
115 |
116 | The following example shows how to mark a topic with the `viewpoint` label as reviewed for a user by using the `PATCH` method (this example does not represent an actual API, but only an illustration):
117 |
118 | ```http
119 | PATCH https://graph.microsoft.com/beta/sampleTopics/19:7d898072-792c-4006-bb10-5ca9f259
120 |
121 | {
122 | "title": "Announcements: Changes to PowerPoint and Word to open files faster",
123 | ...
124 | "viewpoint": {
125 | "isReviewed" : "true"
126 | }
127 | }
128 | ```
129 |
130 | The server responds with a success status code and no payload:
131 |
132 | ```http
133 | HTTP/1.1 204 No Content
134 | ```
135 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | This work is licensed under the Creative Commons Attribution 4.0 International License.
2 | To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
3 |
--------------------------------------------------------------------------------