Skip to content

Commit 3bed112

Browse files
author
Luke Sneeringer
authored
Merge branch 'main' into aip-159
2 parents 8b58693 + c7ec4d9 commit 3bed112

File tree

7 files changed

+417
-1
lines changed

7 files changed

+417
-1
lines changed

aip/general/0136/aip.md.j2

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Custom operations
2+
3+
Services use custom operations to provide a means to express arbitrary actions
4+
that are difficult to model using only the standard operations. Custom
5+
operations are important because they provide a means for an API's vocabulary
6+
to adhere to user intent.
7+
8+
## Guidance
9+
10+
Custom operations **should** only be used for functionality that can not be
11+
easily expressed via standard operations; prefer standard operations if
12+
possible, due to their consistent semantics. (Of course, this only applies if
13+
the functionality in question actually conforms to the normal semantics; it is
14+
_not_ a good idea to contort things to endeavor to make the standard operations
15+
"sort of work".)
16+
17+
While custom operations vary widely in how they are designed, many principles
18+
apply consistently:
19+
20+
{% tab proto %}
21+
22+
{% sample 'library.proto', 'rpc ArchiveBook' %}
23+
24+
- The name of the RPC **should** be a verb followed by a noun.
25+
- The name **must not** contain prepositions ("for", "with", etc.).
26+
- The HTTP method for custom operations **should** usually be `POST`, unless
27+
the custom method maps more strongly to another HTTP verb.
28+
- Custom operations that serve as an alternative to get or list operations
29+
(such as `Search`) **should** use `GET`. These operations **must** be
30+
idempotent and have no state changes or side effects (they should be safe
31+
as defined in [RFC 7231][]).
32+
- Custom operations **should not** use `PATCH` or `DELETE`.
33+
- The HTTP URI **must** use a `:` character followed by the custom verb
34+
(`:archive` in the above example), and the verb in the URI **must** match the
35+
verb in the name of the RPC.
36+
- If word separation is required, `camelCase` **must** be used.
37+
- The `body` clause in the `google.api.http` annotation **should** be `"*"`.
38+
- However, if using `GET` or `DELETE`, the `body` clause **must** be absent.
39+
- Custom operations **should** usually take a request message matching the RPC
40+
name, with a -`Request` suffix.
41+
- Custom operations **should** usually return a response message matching the
42+
RPC name, with a -`Response` suffix.
43+
- When operating on a specific resource, a custom method **may** return the
44+
resource itself.
45+
46+
{% tab oas %}
47+
48+
{% sample 'library.oas.yaml', '/publishers/{publisherId}/books/{bookId}:archive' %}
49+
50+
- The `operationId` **should** be a verb followed by a noun.
51+
- The `operationId` **must not** contain prepositions ("for", "with", etc.).
52+
- The HTTP method for custom operations **should** usually be `POST`, unless
53+
the custom method maps more strongly to another HTTP verb.
54+
- Custom operations that serve as an alternative to get or list operations
55+
(such as `Search`) **should** use `GET`, and require no request body. These
56+
operations **must** be idempotent and have no state changes or side effects
57+
(they should be safe as defined in [RFC 7231][]).
58+
- Custom operations **should not** use `PATCH` or `DELETE`.
59+
- The HTTP URI **must** use a `:` character followed by the custom verb
60+
(`:archive` in the above example), and the verb in the URI **must** match the
61+
verb in the `operationId`.
62+
- If word separation is required, `camelCase` **must** be used.
63+
64+
{% endtabs %}
65+
66+
**Note:** The pattern above shows a custom method that operates on a specific
67+
resource. Custom operations can be associated with resources, collections, or
68+
services.
69+
70+
### Collection-based custom operations
71+
72+
While most custom operations operate on a single resource, some custom
73+
operations **may** operate on a collection instead:
74+
75+
{% tab proto %}
76+
77+
{% sample 'library.proto', 'rpc SortBooks' %}
78+
79+
{% tab oas %}
80+
81+
{% sample 'library.oas.yaml', '/publishers/{publisherId}/books:sort' %}
82+
83+
{% endtabs %}
84+
85+
- If the collection has a parent, the field name in the request message
86+
**should** be the target resource's singular noun (`publisher` in the above
87+
example). If word separators are necessary, `snake_case` **must** be used.
88+
- The collection key (`books` in the above example) **must** be literal.
89+
90+
### Stateless operations
91+
92+
Some custom operations are not attached to resources at all. These operations
93+
are generally _stateless_: they accept a request and return a response, and
94+
have no permanent effect on data within the API.
95+
96+
{% tab proto %}
97+
98+
{% sample 'translate.proto', 'rpc TranslateText' %}
99+
100+
{% tab oas %}
101+
102+
{% sample 'translate.oas.yaml', '/projects/{projectId}:translateText' %}
103+
104+
{% endtabs %}
105+
106+
- If the method runs in a particular scope (such as a project, as in the above
107+
example), the field name in the request message **should** be the name of the
108+
scope resource. If word separators are necessary, `snake_case` **must** be
109+
used.
110+
- The URI **should** place both the verb and noun after the `:` separator
111+
(avoid a "faux collection key" in the URI in this case, as there is no
112+
collection). For example, `:translateText` is preferable to `text:translate`.
113+
- Stateless operations **must** use `POST` if they involve billing.
114+
115+
### Declarative-friendly resources
116+
117+
Declarative-friendly resources usually **should not** employ custom operations
118+
(except specific declarative-friendly custom operations discussed in other
119+
AIPs), because declarative-friendly tools are unable to automatically determine
120+
what to do with them.
121+
122+
An exception to this is for rarely-used, fundamentally imperative operations,
123+
such as a `Move`, `Rename`, or `Restart` operation, for which there would not
124+
be an expectation of declarative support.
125+
126+
[rfc 7231]: https://datatracker.ietf.org/doc/html/rfc7231#section-4.2.1

aip/general/0136/aip.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
id: 136
3+
state: approved
4+
created: 2019-01-25
5+
placement:
6+
category: operations
7+
order: 100

aip/general/0136/library.oas.yaml

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
paths:
7+
/publishers/{publisherId}/books/{bookId}:archive:
8+
post:
9+
operationId: archiveBook
10+
description: Archives the given book.
11+
requestBody:
12+
description:
13+
content:
14+
application/json:
15+
schema:
16+
description: Request structure to archive a book.
17+
properties:
18+
mime_type:
19+
type: string
20+
description: |
21+
The target format for the archived book.
22+
Must be "application/pdf", "application/rtf", or
23+
"application/epub+zip"
24+
required: true
25+
enum:
26+
- application/pdf
27+
- application/rtf
28+
- application/epub+zip
29+
responses:
30+
200:
31+
description: OK
32+
content:
33+
application/json:
34+
schema:
35+
description: Response structure for the archiveBook operation.
36+
properties:
37+
uri:
38+
type: string
39+
description: The location of the archived book.
40+
/publishers/{publisherId}/books:sort:
41+
post:
42+
operationId: sortBooks
43+
description: Sorts the books from this publisher.
44+
requestBody:
45+
description: Request structure to sort a collection of books.
46+
properties:
47+
field:
48+
type: string
49+
description: |
50+
The property of the book to sort by.
51+
If not provided, "title" is used.
52+
responses:
53+
200:
54+
description: OK
55+
components:
56+
schema:
57+
Book:
58+
description: A representation of a single book.
59+
properties:
60+
name:
61+
type: string
62+
description: |
63+
The name of the book.
64+
Format: publishers/{publisher}/books/{book}
65+
isbn:
66+
type: string
67+
description: |
68+
The ISBN (International Standard Book Number) for this book.
69+
title:
70+
type: string
71+
description: The title of the book.
72+
authors:
73+
type: array
74+
items:
75+
type: string
76+
description: The author or authors of the book.
77+
rating:
78+
type: float
79+
description: The rating assigned to the book.

aip/general/0136/library.proto

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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/annotations.proto";
18+
import "google/api/field_behavior.proto";
19+
import "google/api/resource.proto";
20+
21+
// A service representing a library.
22+
service Library {
23+
// Archives the given book.
24+
rpc ArchiveBook(ArchiveBookRequest) returns (ArchiveBookResponse) {
25+
option (google.api.http) = {
26+
post: "/v1/{name=publishers/*/books/*}:archive"
27+
body: "*"
28+
};
29+
}
30+
31+
// Sorts the books from this publisher.
32+
rpc SortBooks(SortBooksRequest) returns (SortBooksResponse) {
33+
option (google.api.http) = {
34+
post: "/v1/{publisher=publishers/*}/books:sort"
35+
body: "*"
36+
};
37+
}
38+
}
39+
40+
// Request message to archive a book.
41+
message ArchiveBookRequest {
42+
// The name of the book to retrieve.
43+
string name = 1 [
44+
(google.api.field_behavior) = REQUIRED,
45+
(google.api.resource_reference) = {
46+
type: "library.googleapis.com/Book"
47+
}];
48+
49+
// The target format for the archived book.
50+
// Must be "application/pdf", "application/rtf", or "application/epub+zip"
51+
string mime_type = 2;
52+
}
53+
54+
// Response message for archiving a book.
55+
message ArchiveBookResponse {
56+
// The location of the archived book
57+
string uri = 1;
58+
}
59+
60+
// Request message to sort a collection of books.
61+
message SortBooksRequest {
62+
// The name of the publisher to sort books for.
63+
string publisher = 1 [
64+
(google.api.field_behavior) = REQUIRED,
65+
(google.api.resource_reference) = {
66+
type: "library.googleapis.com/Publisher"
67+
}];
68+
69+
// The property of the book to sort by.
70+
// If not provided, "title" is used.
71+
string field = 2;
72+
}
73+
74+
// Response message for a sorted collection of books.
75+
message SortBooksResponse {}
76+
77+
// A representation of a single book.
78+
message Book {
79+
option (google.api.resource) = {
80+
type: "library.googleapis.com/Book"
81+
pattern: "publishers/{publisher}/books/{book}"
82+
};
83+
84+
// The name of the book.
85+
// Format: publishers/{publisher}/books/{book}
86+
string name = 1;
87+
88+
// The ISBN (International Standard Book Number) for this book.
89+
string isbn = 2;
90+
91+
// The title of the book.
92+
string title = 3;
93+
94+
// The author or authors of the book.
95+
repeated string authors = 4;
96+
97+
// The rating assigned to the book.
98+
float rating = 5;
99+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
paths:
7+
/projects/{projectId}:translateText:
8+
post:
9+
operationId: translateText
10+
description: Translates the provided text from one language to another.
11+
requestBody:
12+
description:
13+
content:
14+
application/json:
15+
schema:
16+
description: Request structure to translate text.
17+
properties:
18+
contents:
19+
type: array
20+
items:
21+
type: string
22+
description: The contents of the input, as a string.
23+
source_language_code:
24+
type: string
25+
description: |
26+
The BCP-47 language code of the input text (e.g. "en-US").
27+
If the source language is not specified, the service will
28+
attempt to infer it.
29+
target_language_code:
30+
type: string
31+
description: |
32+
The BCP-47 language code of the output text (e.g. "en-US").
33+
responses:
34+
200:
35+
description: OK
36+
content:
37+
application/json:
38+
schema:
39+
description: |
40+
Response structure for the translateText operation.
41+
properties:
42+
translated_text:
43+
type: string
44+
description: |
45+
Text translated into the target language.
46+
detected_language_code:
47+
type: string
48+
description: |
49+
The BCP-47 language code of source text in the initial
50+
request, if it was detected automatically.

0 commit comments

Comments
 (0)