Skip to content

Commit e449c4f

Browse files
authored
Add documentation explaining the different cases for required properties, nullable properties, and properties with default values (#460)
1 parent abdd48a commit e449c4f

File tree

2 files changed

+317
-1
lines changed

2 files changed

+317
-1
lines changed

graph/GuidelinesGraph.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ Following are a few pros and cons to decide which pattern to use:
222222
> **Note:**
223223
> As can be seen in a few of the pros and cons, one of the important aspects discussed here is that the API design goes beyond the syntactical aspects of the API. Therefore, it is important to plan ahead how the API evolves, lay the foundation, and allow users to form a good understanding of the semantics of the API. **Changing the semantics is always a breaking change.** The different modeling patterns differ in how they express syntax and semantics and how they allow the API to evolve without breaking compatibility. For more information, see [API contract and non-backward compatible changes](#api-contract-and-non-backward-compatible-changes) later in this article.
224224
225+
#### Nullable properties
226+
227+
The facet and flat bag approaches often require nullable properties, so it is important to still use non-nullable properties where appropriate.
228+
Since inheritance can often remove the use of nullable properties completely, it is also important to know when nullable properties are necessary.
229+
See [Nullable properties](./nullable.md) for more details.
230+
225231
### Query support
226232

227233
Microsoft Graph APIs should support basic query options in conformance with OData specifications and [Microsoft REST API Guidelines for error condition responses](https://github.com/microsoft/api-guidelines/blob/vNext/Guidelines.md#7102-error-condition-responses).
@@ -241,7 +247,6 @@ The query options part of an OData URL can be quite long, potentially exceeding
241247

242248
Another way to avoid this is to use JSON batch as described in the [Microsoft Graph batching documentation](https://docs.microsoft.com/graph/json-batching#bypassing-url-length-limitations-with-batching).
243249

244-
245250
### Behavior modeling
246251

247252
The HTTP operations dictate how your API behaves. The URL of an API, along with its request/response bodies, establishes the overall contract that developers have with your service. As an API provider, how you manage the overall request/response pattern should be one of the first implementation decisions you make.

graph/nullable.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
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+
<EntitySet Name="servicePrincipals" Type="self.servicePrincipal" />
13+
...
14+
<EntityType Name="servicePrincipal">
15+
<Key>
16+
<PropertyRef Name="id" />
17+
</Key>
18+
<Property Name="id" Type="Edm.String" Nullable="false" />
19+
<Property Name="appId" Type="Edm.String" Nullable="false" /> <!--required for creation-->
20+
<Property Name="displayName" Type="Edm.String" Nullable="false" />
21+
<Property Name="foo" Type="Edm.String" Nullable="true" DefaultValue="testval" />
22+
<Property Name="bar" Type="Edm.String" Nullable="false" DefaultValue="differentvalue" />
23+
...
24+
</EntityType>
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"`

0 commit comments

Comments
 (0)