Skip to content

Commit 83c133c

Browse files
docs: update aip-4222 with explicit routing headers (#840)
* chore: update aip-4222 to inlcude information about explicit routing headers * address the cr comments * update PR reviews * address PR comments * fixup * PR comments
1 parent 0e0e2a8 commit 83c133c

1 file changed

Lines changed: 161 additions & 13 deletions

File tree

aip/client-libraries/4222.md

Lines changed: 161 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,154 @@ routing.
1313

1414
## Guidance
1515

16-
Code generators **should** look at URI-based variables declared in the
16+
Code generators **must** use the annotations to generate client libraries that,
17+
on a per-RPC basis, extract routing information from the request payload and
18+
add that information to the routing header.
19+
20+
There are two annotations that specify how to extract routing information from
21+
the request payload:
22+
23+
* the [`google.api.routing`][routing] annotation that specifies how to construct
24+
routing headers explicitly
25+
* the [`google.api.http`][http] annotation that may specify how to construct
26+
routing headers implicitly.
27+
28+
For any given RPC, if the explicit routing headers annotation is present, the code
29+
generators **must** use it and ignore any routing headers that might be implicitly
30+
specified in the [`google.api.http`][http] annotation. If the explicit routing
31+
headers annotation is absent, the code generators **must** parse the
32+
[`google.api.http`][http] annotation to see if it specifies routing headers
33+
implicitly, and use that specification.
34+
35+
## Explicit Routing Headers (`google.api.routing`)
36+
37+
For an unary or server-streaming RPC the code generator **must** look at the routing
38+
parameters specified in the [`google.api.routing`][routing] annotation, if present.
39+
Any given routing parameter specifies a field name and a pattern with exactly one
40+
named resource ID path segment. For example:
41+
42+
```proto
43+
rpc CreateTopic(CreateTopicRequest) {
44+
option (google.api.routing) = {
45+
routing_parameters {
46+
field: "parent"
47+
path_template: "{project=projects/*}/**"
48+
}
49+
}
50+
}
51+
```
52+
**Note:** An empty `google.api.routing` annotation is acceptable. It means that no
53+
routing headers should be generated for the RPC, when they otherwise would be
54+
e.g. implicitly from the `google.api.http` annotation.
55+
56+
**Note:** It is acceptable to omit the pattern in the resource ID segment, `{parent}`
57+
for example, is equivalent to `{parent=*}` and **must** be parsed, e.g.:
58+
```proto
59+
routing_parameters {
60+
field: "parent"
61+
path_template: "projects/{parent}"
62+
}
63+
```
64+
65+
is the same as
66+
67+
```proto
68+
routing_parameters {
69+
field: "parent"
70+
path_template: "projects/{parent=*}"
71+
}
72+
```
73+
74+
**Note** It is acceptable to omit the `path_template` field altogether. An omitted
75+
`path_template` is equivalent to a `path_template` with the same resource ID name as
76+
the field and the pattern `**`, and **must** be parsed, e.g.:
77+
78+
```proto
79+
routing_parameters {
80+
field: "parent"
81+
}
82+
```
83+
84+
is the same as
85+
86+
```proto
87+
routing_parameters {
88+
field: "parent"
89+
path_template: "{parent=**}"
90+
}
91+
```
92+
NB: an omitted `path_template` field does not indicate that key-value pairs with empty values can be sent. It's merely a shorthand.
93+
94+
When the user supplies an instance of `CreateTopicRequest` to the method, the
95+
client library **must** match all the routing parameters in the order specified
96+
to the fields of that instance. For each routing parameter, the pattern in the
97+
`path_template` **must** be matched to the input message field specified by the
98+
routing parameter's `field` field. In case of a match, the name of the resource ID
99+
path segment must be used as a key, and the value of the resource ID path segment match
100+
must be used as a value of a key-value pair to be appended to the `x-goog-request-params`
101+
header.
102+
103+
Both the key and the value **must** be URL-encoded per [RFC 6570 §3.2.2][].
104+
This can be done with standard library URL encoding. For example, adding this header
105+
to a gRPC request in Ruby:
106+
107+
```ruby
108+
header_params = {}
109+
if (pattern_matches("{project=projects/*}/**", request.parent))
110+
header_params["project"] = extract_match_value("{project=projects/*}/**", request.parent)
111+
end
112+
request_params_header = URI.encode_www_form header_params
113+
metadata[:"x-goog-request-params"] = request_params_header
114+
```
115+
116+
In cases when multiple routing parameters have the same resource ID path segment name,
117+
thus referencing the same header key, the "last one wins" rule is used to determine
118+
which value to send. The "last" here is meant in terms of the order in which they're specified in the annotation. If some of the routing parameters with the same resource ID segment
119+
name have failed to match the field, or if the field was unset, or if the extracted matched value
120+
is an empty string, these parameters are not considered when determining which value
121+
to send.
122+
123+
Example:
124+
125+
```proto
126+
option (google.api.routing) = {
127+
routing_parameters {
128+
field: "parent"
129+
path_template: "{project=projects/*}/**"
130+
}
131+
routing_parameters {
132+
field: "parent"
133+
path_template: "{project=projects/*/subprojects/*}/**"
134+
}
135+
routing_parameters {
136+
field: "billing_project"
137+
path_template: "{project=**}"
138+
}
139+
}
140+
```
141+
In this case if in a given request the `billing_project` field is set to an non-empty value,
142+
its value will be sent with the `project` key because the routing parameter looking at `billing_project` field is specified last. If the `billing_project` field is not set, the `parent` field will be considered, first trying to send a
143+
project with a subproject specified, and then without. Note that if a given request has a
144+
`parent` field with a value e.g. `projects/100/subprojects/200/foo`, patterns in both first and second `routing_parameters` will match it, but the second one will "win" since it is specified "last".
145+
146+
If all the routing parameters with the same resource ID segment name have failed
147+
to match the field, the key-value pair corresponding to those routing parameters'
148+
resource ID path segment name **must not** be sent.
149+
150+
If none of the routing parameters matched their respective fields, the routing header
151+
**must not** be sent.
152+
153+
Much like URL parameters, if there is more than one key-value pair to be sent, the `&`
154+
character is used as the separator.
155+
156+
## Implicit Routing Headers (`google.api.http`)
157+
158+
**Note:** For an RPC annotated with the [`google.api.routing`][routing] annotation,
159+
the [`google.api.http`][http] annotation must be ignored for the purpose of adding
160+
routing headers.
161+
162+
If an unary or server-streaming RPC is not annotated with the [`google.api.routing`][routing]
163+
annotation, code generators **must** look at URI-based variables declared in the
17164
[`google.api.http`][http] annotation and transcribe these into the
18165
`x-goog-request-params` header in unary calls. A URI-based variable is a
19166
variable declared as a key in curly braces in the URI string. For example:
@@ -24,15 +171,15 @@ rpc CreateTopic(CreateTopicRequest) {
24171
}
25172
```
26173

27-
**Note:** the ommission of a path segment in the variable template, `{parent}`
28-
for example, is equivalent to `{parent=*}` and **should** be parsed.
174+
**Note:** It is acceptable to omit the pattern in the resource ID segment, `{parent}`
175+
for example, is equivalent to `{parent=*}` and **must** be parsed.
29176

30177
In this case, the applicable variable is `parent`, and it refers to the
31178
`parent` field in `CreateTopicRequest`. When the user provides an instance of
32179
`CreateTopicRequest` to the method (or once the client library has built it, in
33180
the case of method overloads), the client library must extract the key and
34181
value, and append them to the `x-goog-request-params` header. Both the key and
35-
the value **must** be URL-encoded per [RFC 6570 §3.2.2][]. This can be done
182+
the value **must** be URL-encoded per [RFC 6570 §3.2.2][rfc 6570 §3.2.2]. This can be done
36183
with standard library URL encoding. For example, adding this header to a gRPC
37184
request in Go:
38185

@@ -41,25 +188,26 @@ md := metadata.Pairs("x-goog-request-params",
41188
url.QueryEscape("parent") + "=" + url.QueryEscape(req.GetParent()))
42189
```
43190

44-
Much like URL parameters, if there is more than one key-value pair, the `&`
45-
character is used as the separator. At runtime, if a named parameter is unset
46-
on the request message, it **should not** be included in the headers.
191+
At runtime, if a field with the same name as the named parameter is unset on the
192+
request message, the key-value pair corresponding to that parameter **must not**
193+
be included in the routing header. If none of the parameters must be included in
194+
the routing header, the routing header **must not** be sent.
47195

48196
If the [`google.api.http`][http] annotation contains `additional_bindings`,
49-
these patterns **should** be parsed for additional request parameters. Fields
50-
not duplicated in the top-level (or `additional_bindings`) pattern **should**
197+
these patterns **must** be parsed for additional request parameters. Fields
198+
not duplicated in the top-level (or `additional_bindings`) pattern **must**
51199
be included in request parameters, encoded in the same way.
52200

53-
**Note:** There is no additional annotation used for this; the presence of the
54-
key in any of the standard fields (`get`, `post`, `put`, `patch`, `delete`) on
55-
the [`google.api.http`][http] annotation is sufficient.
201+
Much like URL parameters, if there is more than one key-value pair, the `&`
202+
character is used as the separator.
56203

57204
<!-- prettier-ignore -->
58205
[http]: https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
206+
[routing]: https://github.com/googleapis/googleapis/blob/master/google/api/routing.proto
59207
[rfc 6570 §3.2.2]: https://tools.ietf.org/html/rfc6570#section-3.2.2
60208

61209
## Changelog
62-
210+
- **2022-07-13**: Updated to include the new `google.api.routing` annotation.
63211
- **2020-04-21**: Explicitly parse path variables missing a trailing segment.
64212
- **2019-11-27**: Include `additional_bindings` as a request parameter source.
65213
- **2019-06-26**: Fix wording and example of key-value pair encoding.

0 commit comments

Comments
 (0)