Skip to content

Commit 0246e8d

Browse files
author
Luke Sneeringer
committed
Merge branch 'main' into aip-147
2 parents a3251f0 + 353e9af commit 0246e8d

6 files changed

Lines changed: 359 additions & 0 deletions

File tree

aip/general/0126/aip.md.j2

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# Enumerations
2+
3+
It is common for a field to only accept or provide a discrete and limited set
4+
of values. In these cases, it can be useful to use enumerations (generally
5+
abbreviated "enums") in order to clearly communicate what the set of allowed
6+
values are.
7+
8+
## Guidance
9+
10+
APIs **may** expose enum objects for sets of values that are expected to change
11+
infrequently:
12+
13+
```typescript
14+
// Possible formats in which a book may be published.
15+
enum Format {
16+
// The printed format, in hardback.
17+
Hardback = 'HARDBACK',
18+
19+
// The printed format, in paperback.
20+
Paperback = 'PAPERBACK',
21+
22+
// An electronic book format.
23+
Ebook = 'EBOOK',
24+
25+
// An audio recording.
26+
Audiobook = 'AUDIOBOOK',
27+
}
28+
```
29+
30+
- All enum values **should** use a consistent case format across an
31+
organization. In many cases, this is dictated by the IDL the organization
32+
uses.
33+
- Enums **should** document whether the enum is frozen or they expect to add
34+
values in the future.
35+
36+
### When to use enums
37+
38+
Enums can be more accessible and readable than strings or booleans in many
39+
cases, but they do add overhead when they change. Therefore, enums **should**
40+
receive new values infrequently. While the definition of "infrequently" may
41+
change based on individual use cases, a good rule of thumb is no more than once
42+
a year. For enums that change frequently, the API **should** use a string and
43+
document the format.
44+
45+
**Note:** If an enumerated value needs to be shared across APIs, an enum
46+
**may** be used, but the assignment between enum values and their wire
47+
representation **must** match.
48+
49+
### Alternatives
50+
51+
Enums **should not** be used when there is a competing, widely-adopted standard
52+
representation (such as with [language codes][bcp-47] or [media types][]).
53+
Instead, that standard representation **should** be used. This is true even if
54+
only a small subset of values are permitted, because using enums in this
55+
situation often leads to frustrating lookup tables when trying to use multiple
56+
APIs together.
57+
58+
For enumerated values where the set of allowed values changes frequently, APIs
59+
**should** use a `string` field instead, and **must** document the allowed
60+
values. String fields with enumerated values **should** use a uniform case
61+
system (`snake_case`, `kebab-case`, etc.) throughout an organization.
62+
63+
Boolean fields **may** be used in situations where it is clear that no further
64+
flexibility will be needed. The default value **must** be `false`.
65+
66+
### Compatibility
67+
68+
Adding values to an enum has the potential to be disruptive to existing
69+
clients. Consider code written against the `Format` enum in an earlier version
70+
where only the first two options were available:
71+
72+
```typescript
73+
switch (book.format) {
74+
case Format.Hardback:
75+
// Do something...
76+
break;
77+
case Format.Paperback:
78+
// Do something...
79+
break;
80+
default:
81+
// When new enum values are introduced, pre-existing client code may
82+
// throw errors or act in unexpected ways.
83+
throw new Error('Unrecognized value.');
84+
}
85+
```
86+
87+
Services **may** add new values to existing enums; however, they **should** add
88+
enums carefully; think about what will happen if a client system does not know
89+
about a new value.
90+
91+
Additionally, in IDLs where enum values are presented in a specific order,
92+
services **should** only add new values to the end. An exception to this rule
93+
is if the enum conforms to an external standard (for example, an enum
94+
representing HTTP status codes would add a new 3xx value alongside the others,
95+
not at the end).
96+
97+
## Interface Definitions
98+
99+
{% tab proto %}
100+
101+
{% sample 'enum.proto', 'enum Format' %}
102+
103+
- The zero value of the enum **should** be the name of the enum itself followed
104+
by the suffix `_UNSPECIFIED`. The service **may** either allow or prohibit
105+
use of this value.
106+
- Enums which will only be used in a single message **should** be nested within
107+
that message. In this case, the enum **should** be declared immediately
108+
before it is used.
109+
- If multiple enums are in the same namespace, they **must not** share any
110+
values. (This is because enums do not provide their own namespace for their
111+
values in some languages.)
112+
- If an enumerated value needs to be shared across APIs, an enum **may** be
113+
used, but the assignment between the value names and the tag numbers **must**
114+
match.
115+
116+
**Note:** When using protocol buffers, it is impossible to distinguish between
117+
`false` and unset. If this is a requirement, an enum **may** be a better design
118+
choice (although `google.protobuf.BoolValue` is also available).
119+
120+
{% tab oas %}
121+
122+
{% sample 'enum.oas.yaml', 'format' %}
123+
124+
- Enumerated fields **should** be strings.
125+
- If the enum is optional, The `null` value **should** be used as the empty
126+
value, and **should** be the first value specified.
127+
128+
**Note:** If `null` is a valid value, OpenAPI 3.0 also requires that
129+
`nullable: true` is specified for the field.
130+
131+
{% endtabs %}
132+
133+
## Further reading
134+
135+
- For states, a special type of enum, see AIP-216.
136+
137+
[bcp-47]: https://en.wikipedia.org/wiki/IETF_language_tag
138+
[media types]: https://en.wikipedia.org/wiki/Media_type

aip/general/0126/aip.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
id: 126
3+
state: approved
4+
created: 2019-07-24
5+
placement:
6+
category: resource-design
7+
order: 60

aip/general/0126/enum.oas.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
components:
7+
schema:
8+
Book:
9+
description: A representation of a single book.
10+
properties:
11+
name:
12+
type: string
13+
description: |
14+
The name of the book.
15+
Format: publishers/{publisher}/books/{book}
16+
isbn:
17+
type: string
18+
description: |
19+
The ISBN (International Standard Book Number) for this book.
20+
title:
21+
type: string
22+
description: The title of the book.
23+
authors:
24+
type: array
25+
items:
26+
type: string
27+
description: The author or authors of the book.
28+
rating:
29+
type: float
30+
description: The rating assigned to the book.
31+
format:
32+
type: string
33+
description: The format of the book.
34+
nullable: true
35+
enum:
36+
- null
37+
- HARDCOVER
38+
- PAPERBACK
39+
- EBOOK
40+
- AUDIOBOOK

aip/general/0126/enum.proto

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2020 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
import "google/api/resource.proto";
18+
19+
// A representation of a book.
20+
message Book {
21+
option (google.api.resource) = {
22+
type: "library.googleapis.com/Book"
23+
pattern: "publishers/{publisher}/books/{book}"
24+
};
25+
26+
// The name of the book.
27+
// Format: publishers/{publisher}/books/{book}
28+
string name = 1;
29+
30+
// The ISBN (International Standard Book Number) for this book.
31+
string isbn = 2;
32+
33+
// The title of the book.
34+
string title = 3;
35+
36+
// The author or authors of the book.
37+
repeated string authors = 4;
38+
39+
// The rating assigned to the book.
40+
float rating = 5;
41+
42+
// Possible formats in which the book may be published.
43+
enum Format {
44+
// Default value. This value is unused.
45+
FORMAT_UNSPECIFIED = 0;
46+
47+
// The printed format, in hardback.
48+
HARDBACK = 1;
49+
50+
// The printed format, in paperback.
51+
PAPERBACK = 2;
52+
53+
// An electronic book format.
54+
EBOOK = 3;
55+
56+
// An audio recording.
57+
AUDIOBOOK = 4;
58+
}
59+
60+
// The format of the book.
61+
Format format = 6;
62+
}

aip/general/0154/aip.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Resource freshness validation
2+
3+
APIs often need to validate that a client and server agree on the current state
4+
of a resource before taking some kind of action on that resource. For example,
5+
two processes updating the same resource in parallel could create a race
6+
condition, where the latter process "stomps over" the effort of the former one.
7+
8+
ETags provide a way to deal with this, by allowing the server to send a
9+
checksum based on the current content of a resource; when the client sends that
10+
checksum back, the server can ensure that the checksums match before acting on
11+
the request.
12+
13+
## Guidance
14+
15+
A resource **may** provide an `ETag` header when retrieving a single resource
16+
when it is important to ensure that the client has an up to date resource
17+
before acting on certain requests:
18+
19+
```
20+
200 OK
21+
Content-type: application/json
22+
ETag: "55cc0347-66fc-46c3-a26f-98a9a7d61d0e"
23+
```
24+
25+
The ETag **must** be provided by the server on output, and values **should**
26+
conform to [RFC 7232][]. Resources **must** support the `If-Match` header (and
27+
**may** support the `If-None-Match` header) if and only if resources provide
28+
the ETag.
29+
30+
**Note:** ETag values **must** include quotes as described in [RFC 7232][]. For
31+
example, a valid ETag is `"foo"`, not `foo`.
32+
33+
ETags **must** be based on an opaque checksum or hash of the resource that
34+
guarantees it will change if the resource changes.
35+
36+
### Condition headers
37+
38+
If the service receives a request to modify a resource that includes an
39+
`If-Match` header, the service **must** validate that the value matches the
40+
current ETag. If the `If-Match` header value does not match the ETag, the
41+
service **must** reply with an HTTP 412 error.
42+
43+
If the user omits the `If-Match` header, the service **should** permit the
44+
request. However, services with strong consistency or parallelism requirements
45+
**may** require users to send ETags all the time and reject the request with an
46+
HTTP 400 error if it does not contain an ETag.
47+
48+
If any conditional headers are supported for any operation within a service,
49+
the same conditional headers **must** be supported for all mutation methods
50+
(`POST`, `PATCH`, `PUT`, and `DELETE`) of any path that supports them, and
51+
**should** be supported uniformly for all operations across the service.
52+
53+
If any validator or conditional headers are supported for any operations in the
54+
service, the use of unsupported conditional headers **must** result in an
55+
error. (In other words, once a service gives the client reason to believe it
56+
understands conditional headers, it **must not** ever ignore them.)
57+
58+
### Read requests
59+
60+
If a service receives a `GET` or `HEAD` request with an `If-Match` header, the
61+
service **must** proceed with the request if the ETag matches, or send a
62+
`412 Precondition Failed` error if the ETag does not match.
63+
64+
If a service receives a `GET` or `HEAD` request with an `If-None-Match` header,
65+
the service **must** proceed with the request if the ETag does not match, or
66+
return a `304 Not Modified` response if the ETag does match.
67+
68+
### Strong and weak ETags
69+
70+
ETags can be either "strongly validated" or "weakly validated":
71+
72+
- A strongly validated ETag means that two resources bearing the same ETag are
73+
byte-for-byte identical.
74+
- A weakly validated ETag means that two resources bearing the same ETag are
75+
equivalent, but may differ in ways that the service does not consider to be
76+
important.
77+
78+
Resources **may** use either strong or weak ETags, as it sees fit, but
79+
**should** document the behavior. Additionally, weak ETags **must** have a `W/`
80+
prefix as mandated by [RFC 7232][]:
81+
82+
```
83+
200 OK
84+
Content-type: application/json
85+
ETag: W/"55cc0347-66fc-46c3-a26f-98a9a7d61d0e"
86+
```
87+
88+
Strong ETags **must** and weak ETags **should** be guaranteed to change if any
89+
properties on the resource change that are directly mutable by the client.
90+
Additionally, strong ETags **should** be guaranteed to change if the resource's
91+
representation changes in a meaningful way (meaning the new representation is
92+
not equivalent to the old one).
93+
94+
## Further reading
95+
96+
- For how to retry on errors in client libraries, see AIP-194.
97+
98+
## Changelog
99+
100+
- **2020-09-02**: Clarified that other errors may take precedence over
101+
`FAILED_PRECONDITION` for ETag mismatches.
102+
- **2020-09-02**: Add guidance for ETags on request messages.
103+
- **2019-09-23**: Changed the title to "resource freshness validation".
104+
105+
[rfc 7232]: https://tools.ietf.org/html/rfc7232#section-2.3

aip/general/0154/aip.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
id: 154
3+
state: approved
4+
created: 2019-07-24
5+
placement:
6+
category: design-patterns
7+
order: 30

0 commit comments

Comments
 (0)