From 8a3d4fd1724bd43a7e692a3375575b395052abdb Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Fri, 20 Nov 2020 16:45:01 -0800 Subject: [PATCH 01/10] =?UTF-8?q?AIP-151=20=E2=80=93=20Long-running=20oper?= =?UTF-8?q?ations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This AIP presented some interesting issues around wording: At @mkistler's request, we were using the term operation where AIPs previously used method. I think we might need to use the term endpoint for that purpose so that we can use operation for this one. --- aip/general/0151/aip.md.j2 | 171 +++++++++++++++++++++++++++++++++++++ aip/general/0151/aip.yaml | 7 ++ 2 files changed, 178 insertions(+) create mode 100644 aip/general/0151/aip.md.j2 create mode 100644 aip/general/0151/aip.yaml diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 new file mode 100644 index 00000000..5885fe94 --- /dev/null +++ b/aip/general/0151/aip.md.j2 @@ -0,0 +1,171 @@ +# Long-running operations + +Occasionally, an service may need to expose an endpoint that takes a +significant amount of time to complete. In these situations, it is often a poor +user experience to simply block while the task runs; rather, it is better to +return some kind of promise to the user and allow the user to check back in +later. + +The long-running operations pattern is roughly analogous to a [Future][] in +Python or Java, or a [Node.js Promise][]. Essentially, the user is given a +token that can be used to track progress and retrieve the result. + +## Guidance + +Endpoints that might take a significant amount of time to complete **should** +return a `202 Accepted` response along with an identifier that can be used to +track the status of the operation and ultimately retrieve the result. + +Any single endpoint defined in an API surface **must** either _always_ return +`202 Accepted` along with an operation identifier, or _never_ do so. A service +**must not** return a `200 OK` response with the result if it is "fast enough", +and `202 Accepted` if it is not fast enough, because such behavior adds +significant burdens for clients. + +**Note:** User expectations can vary on what is considered "a significant +amount of time" depending on what work is being done. A good rule of thumb is +10 seconds. + +### Operation representation + +Operations **should** use a common format: + +```typescript +interface Operation { + // The identifier for this operation. + name: string; + + // Whether the operation is done. + done: boolean; + + // The result of the operation. + // Only populated if the operation is done and was successful. + response: any; + + // The error that arose from the operation. + // Only populated if the operation is done and was unsuccessful. + error: Error; + + // Metadata associated with the operation. + // Populated throughout the life of the operation, including after + // it completes. + metadata: any; +} +``` + +- If the `done` field is `true`, then one and exactly one of the `response` and + `error` fields **must** be populated. + - If the `done` field is `false`, then the `response` and `error` fields + **must not** be populated. +- The `response` and `metadata` fields **may** be any type that the service + determines to be appropriate, but **must** always be the same type for any + particular operation. + - The `response` and `metadata` types **should** be defined in the same API + surface as the operation itself. + - The `response` and `metadata` types that need no data **should** use a + custom-defined empty struct rather than a common void or empty type, to + permit future extensibility. + +### Querying an operation + +The service **must** provide an endpoint to query the status of the operation, +which **must** accept the operation identifier and **should not** include other +parameters: + +```http +GET /v1/operations/{operation} HTTP/2 +Host: library.googleapis.com +Accept: application/json +``` + +The endpoint **must** return an `Operation` as described above. + +### Standard methods + +APIs **may** return an `Operation` from the [`Create`][aip-133], +[`Update`][aip-134], or [`Delete`][aip-135] standard methods if appropriate. In +this case, the `response` field **must** be the standard and expected response +type for that standard method. + +When creating or deleting a resource with a long-running operation, the +resource **should** be included in [`List`][aip-132] and [`Get`][aip-131] +calls; however, the resource **should** indicate that it is not usable, +generally with a [state enum][aip-216]. + +### Parallel operations + +A resource **may** accept multiple operations that will work on it in parallel, +but is not obligated to do so: + +- Resources that accept multiple parallel operations **may** place them in a + queue rather than work on the operations simultaneously. +- Resource that does not permit multiple operations in parallel (denying any + new operation until the one that is in progress finishes) **must** return + `ABORTED` if a user attempts a parallel operation, and include an error + message explaining the situation. + +### Expiration + +APIs **may** allow their operation resources to expire after sufficient time +has elapsed after the operation completed. + +**Note:** A good rule of thumb for operation expiry is 30 days. + +### Errors + +Errors that prevent a long-running operation from _starting_ **must** return an +error response (AIP-193), similar to any other method. + +Errors that occur over the course of an operation **may** be placed in the +metadata message. The errors themselves **must** still be represented with a +canonical error object. + +## Interface Definitions + +{% tab proto %} + +When using protocol buffers, the well-known type `google.longrunning.Operation` +is used: + +```proto +// Write a book. +rpc WriteBook(WriteBookRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=publishers/*}/books}:write" + body: "*" + }; + option (google.longrunning.operation_info) = { + response_type: "WriteBookResponse" + metadata_type: "WriteBookMetadata" + }; +} +``` + +- The response type **must** be `google.longrunning.Operation`. The `Operation` + proto definition **must not** be copied into individual APIs. + - The response **must not** be a streaming response. +- The method **must** include a `google.longrunning.operation_info` annotation, + which **must** define both response and metadata types. + - The response and metadata types **must** be defined in the file where the + RPC appears, or a file imported by that file. + - If the response and metadata types are defined in another package, the + fully-qualified message name **must** be used. + - The response type **should not** be `google.protobuf.Empty` (except for + [`Delete`][aip-135] methods), unless it is certain that response data will + _never_ be needed. If response data might be added in the future, define an + empty message for the RPC response and use that. + - The metadata type is used to provide information such as progress, partial + failures, and similar information on each `GetOperation` call. The metadata + type **should not** be `google.protobuf.Empty`, unless it is certain that + metadata will _never_ be needed. If metadata might be added in the future, + define an empty message for the RPC metadata and use that. +- APIs with messages that return `Operation` **must** implement the + [`Operations`][lro] service. Individual APIs **must not** define their own + interfaces for long-running operations to avoid inconsistency. {% endtabs %} + + +[google.rpc.Status]: https://github.com/googleapis/api-common-protos/blob/master/google/rpc/S.proto +[lro]: https://github.com/googleapis/api-common-protos/blob/master/google/longrunning/operations.proto +[node.js promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises +[future]: https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future + diff --git a/aip/general/0151/aip.yaml b/aip/general/0151/aip.yaml new file mode 100644 index 00000000..03ca076e --- /dev/null +++ b/aip/general/0151/aip.yaml @@ -0,0 +1,7 @@ +--- +id: 151 +state: reviewing +created: 2019-07-25 +placement: + category: operations + order: 70 From 587091be0c761e0548b7c6fb09e1d26820911743 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Mon, 23 Nov 2020 13:19:26 -0800 Subject: [PATCH 02/10] Shift to a complete example. --- aip/general/0151/aip.md.j2 | 18 +++----- aip/general/0151/lro.proto | 85 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 13 deletions(-) create mode 100644 aip/general/0151/lro.proto diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index 5885fe94..1becd08c 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -127,19 +127,7 @@ canonical error object. When using protocol buffers, the well-known type `google.longrunning.Operation` is used: -```proto -// Write a book. -rpc WriteBook(WriteBookRequest) returns (google.longrunning.Operation) { - option (google.api.http) = { - post: "/v1/{parent=publishers/*}/books}:write" - body: "*" - }; - option (google.longrunning.operation_info) = { - response_type: "WriteBookResponse" - metadata_type: "WriteBookMetadata" - }; -} -``` +{% sample 'lro.proto', 'rpc WriteBook' %} - The response type **must** be `google.longrunning.Operation`. The `Operation` proto definition **must not** be copied into individual APIs. @@ -163,6 +151,10 @@ rpc WriteBook(WriteBookRequest) returns (google.longrunning.Operation) { [`Operations`][lro] service. Individual APIs **must not** define their own interfaces for long-running operations to avoid inconsistency. {% endtabs %} +{% tab oas %} + +{% endtabs %} + [google.rpc.Status]: https://github.com/googleapis/api-common-protos/blob/master/google/rpc/S.proto [lro]: https://github.com/googleapis/api-common-protos/blob/master/google/longrunning/operations.proto diff --git a/aip/general/0151/lro.proto b/aip/general/0151/lro.proto new file mode 100644 index 00000000..8357cf38 --- /dev/null +++ b/aip/general/0151/lro.proto @@ -0,0 +1,85 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +import "google/api/annotations.proto"; +import "google/api/resource.proto"; +import "google/longrunning/operations.proto"; +import "google/protobuf/timestamp.proto"; + +service Library { + // Write a book. + rpc WriteBook(WriteBookRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{parent=publishers/*}/books}:write" + body: "*" + }; + option (google.longrunning.operation_info) = { + response_type: "WriteBookResponse" + metadata_type: "WriteBookMetadata" + }; + } +} + +// The request message for the WriteBook endpoint. +// Unlike the response, this message is accepted directly. +message WriteBookRequest { + // The publisher for which the book is to be written. + string parent = 1 [(google.api.resource_reference) = { + child_type: "library.googleapis.com/Book" + }]; + + // The title of the new book. + string title = 2; +} + +// The response message for the WriteBook endpoint. +// When WriteBook is called, it will not send this response directly; instead, +// it sends a long-running operation; the operation will contain this in +// the `response` field once it completes. +message WriteBookResponse { + // The text that was written. + string text = 1; +} + +// The metadata message for the WriteBook endpoint. +// This is populated in the `metadata` field for all WriteBook LROs. +message WriteBookMetadata { + // The time the operation started. + google.protobuf.Timestamp start_time = 1; + + // The current progress, expressed as an integer: [0, 100]. + int32 progress_percent = 2; + + enum State { + STATE_UNSPECIFIED = 0; + + // The operation is running. + RUNNING = 1; + + // The operation is still running, but cancellation has been requested + // and accepted, and is in progress. + CANCELLING = 2; + + // The operation was cancelled. + CANCELLED = 3; + + // The operation failed. + FAILED = 4; + } + + // The current state of the operation. + State state = 3; +} From 4ba40b9aad71ae48c1bbfa4a289a86ec64c709c0 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Mon, 23 Nov 2020 13:29:00 -0800 Subject: [PATCH 03/10] Fix bug in the sample. --- aip/general/0151/lro.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aip/general/0151/lro.proto b/aip/general/0151/lro.proto index 8357cf38..0c29160e 100644 --- a/aip/general/0151/lro.proto +++ b/aip/general/0151/lro.proto @@ -23,7 +23,7 @@ service Library { // Write a book. rpc WriteBook(WriteBookRequest) returns (google.longrunning.Operation) { option (google.api.http) = { - post: "/v1/{parent=publishers/*}/books}:write" + post: "/v1/{parent=publishers/*}/books:write" body: "*" }; option (google.longrunning.operation_info) = { From f26ddf9cfdcc89c8240930092c280783ee280034 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 15 Dec 2020 07:48:06 -0800 Subject: [PATCH 04/10] Rewording. --- aip/general/0151/aip.md.j2 | 93 +++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 46 deletions(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index 1becd08c..2feda23e 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -1,23 +1,23 @@ -# Long-running operations +# Long-running requests -Occasionally, an service may need to expose an endpoint that takes a +Occasionally, an service may need to expose an operation that takes a significant amount of time to complete. In these situations, it is often a poor user experience to simply block while the task runs; rather, it is better to -return some kind of promise to the user and allow the user to check back in +return some kind of promise to the user, and allow the user to check back in later. -The long-running operations pattern is roughly analogous to a [Future][] in -Python or Java, or a [Node.js Promise][]. Essentially, the user is given a -token that can be used to track progress and retrieve the result. +The long-running request pattern is roughly analogous to a [Future][] in Python +or Java, or a [Node.js Promise][]. Essentially, the user is given a token that +can be used to track progress and retrieve the result. ## Guidance -Endpoints that might take a significant amount of time to complete **should** +Operations that might take a significant amount of time to complete **should** return a `202 Accepted` response along with an identifier that can be used to -track the status of the operation and ultimately retrieve the result. +track the status of the request and ultimately retrieve the result. -Any single endpoint defined in an API surface **must** either _always_ return -`202 Accepted` along with an operation identifier, or _never_ do so. A service +Any single operation defined in an API surface **must** either _always_ return +`202 Accepted` along with a request identifier, or _never_ do so. A service **must not** return a `200 OK` response with the result if it is "fast enough", and `202 Accepted` if it is not fast enough, because such behavior adds significant burdens for clients. @@ -26,28 +26,28 @@ significant burdens for clients. amount of time" depending on what work is being done. A good rule of thumb is 10 seconds. -### Operation representation +### Promise representation -Operations **should** use a common format: +Long-running requests **should** use a common format: ```typescript -interface Operation { - // The identifier for this operation. - name: string; +interface Promise { + // The identifier for this promise. + id: string; - // Whether the operation is done. + // Whether the request is done. done: boolean; - // The result of the operation. - // Only populated if the operation is done and was successful. + // The result of the request. + // Only populated if the request is done and was successful. response: any; - // The error that arose from the operation. - // Only populated if the operation is done and was unsuccessful. + // The error that arose from the request. + // Only populated if the request is done and was unsuccessful. error: Error; - // Metadata associated with the operation. - // Populated throughout the life of the operation, including after + // Metadata associated with the request. + // Populated throughout the life of the request, including after // it completes. metadata: any; } @@ -66,57 +66,57 @@ interface Operation { custom-defined empty struct rather than a common void or empty type, to permit future extensibility. -### Querying an operation +### Querying an promise The service **must** provide an endpoint to query the status of the operation, which **must** accept the operation identifier and **should not** include other parameters: ```http -GET /v1/operations/{operation} HTTP/2 +GET /v1/promises/{promise} HTTP/2 Host: library.googleapis.com Accept: application/json ``` -The endpoint **must** return an `Operation` as described above. +The endpoint **must** return a `Promise` as described above. ### Standard methods -APIs **may** return an `Operation` from the [`Create`][aip-133], +APIs **may** return an `Promise` from the [`Create`][aip-133], [`Update`][aip-134], or [`Delete`][aip-135] standard methods if appropriate. In this case, the `response` field **must** be the standard and expected response type for that standard method. -When creating or deleting a resource with a long-running operation, the -resource **should** be included in [`List`][aip-132] and [`Get`][aip-131] -calls; however, the resource **should** indicate that it is not usable, -generally with a [state enum][aip-216]. +When creating or deleting a resource with a long-running request, the resource +**should** be included in [`List`][aip-132] and [`Get`][aip-131] calls; +however, the resource **should** indicate that it is not usable, generally with +a [state enum][aip-216]. -### Parallel operations +### Parallel requests -A resource **may** accept multiple operations that will work on it in parallel, +A resource **may** accept multiple requests that will work on it in parallel, but is not obligated to do so: -- Resources that accept multiple parallel operations **may** place them in a - queue rather than work on the operations simultaneously. -- Resource that does not permit multiple operations in parallel (denying any - new operation until the one that is in progress finishes) **must** return - `ABORTED` if a user attempts a parallel operation, and include an error +- Resources that accept multiple parallel requests **may** place them in a + queue rather than work on the requests simultaneously. +- Resource that does not permit multiple requests in parallel (denying any new + request until the one that is in progress finishes) **must** return + `409 Conflict` if a user attempts a parallel request, and include an error message explaining the situation. ### Expiration -APIs **may** allow their operation resources to expire after sufficient time -has elapsed after the operation completed. +APIs **may** allow their promise resources to expire after sufficient time has +elapsed after the request completed. -**Note:** A good rule of thumb for operation expiry is 30 days. +**Note:** A good rule of thumb for promise expiry is 30 days. ### Errors -Errors that prevent a long-running operation from _starting_ **must** return an +Errors that prevent a long-running request from _starting_ **must** return an error response (AIP-193), similar to any other method. -Errors that occur over the course of an operation **may** be placed in the +Errors that occur over the course of an request **may** be placed in the metadata message. The errors themselves **must** still be represented with a canonical error object. @@ -125,7 +125,10 @@ canonical error object. {% tab proto %} When using protocol buffers, the well-known type `google.longrunning.Operation` -is used: +is used. + +**Note:** For historical reasons, Google uses the term `Operation` to represent +what this document describes as a `Promise`. {% sample 'lro.proto', 'rpc WriteBook' %} @@ -149,9 +152,7 @@ is used: define an empty message for the RPC metadata and use that. - APIs with messages that return `Operation` **must** implement the [`Operations`][lro] service. Individual APIs **must not** define their own - interfaces for long-running operations to avoid inconsistency. {% endtabs %} - -{% tab oas %} + interfaces for long-running operations to avoid inconsistency. {% endtabs %} From 1a8fb700b97f3c2f4dc69b6b1ea06ccd209e0572 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 5 Jan 2021 10:58:27 -0800 Subject: [PATCH 05/10] Status monitor --- aip/general/0151/aip.md.j2 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index 2feda23e..b8d21b82 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -31,8 +31,8 @@ amount of time" depending on what work is being done. A good rule of thumb is Long-running requests **should** use a common format: ```typescript -interface Promise { - // The identifier for this promise. +interface StatusMonitor { + // The identifier for this status monitor. id: string; // Whether the request is done. @@ -66,23 +66,23 @@ interface Promise { custom-defined empty struct rather than a common void or empty type, to permit future extensibility. -### Querying an promise +### Querying a status monitor The service **must** provide an endpoint to query the status of the operation, which **must** accept the operation identifier and **should not** include other parameters: ```http -GET /v1/promises/{promise} HTTP/2 +GET /v1/statusMonitors/{status_monitor} HTTP/2 Host: library.googleapis.com Accept: application/json ``` -The endpoint **must** return a `Promise` as described above. +The endpoint **must** return a `StatusMonitor` as described above. ### Standard methods -APIs **may** return an `Promise` from the [`Create`][aip-133], +APIs **may** return an `StatusMonitor` from the [`Create`][aip-133], [`Update`][aip-134], or [`Delete`][aip-135] standard methods if appropriate. In this case, the `response` field **must** be the standard and expected response type for that standard method. @@ -106,10 +106,10 @@ but is not obligated to do so: ### Expiration -APIs **may** allow their promise resources to expire after sufficient time has -elapsed after the request completed. +APIs **may** allow their status monitor resources to expire after sufficient +time has elapsed after the request completed. -**Note:** A good rule of thumb for promise expiry is 30 days. +**Note:** A good rule of thumb for status monitor expiry is 30 days. ### Errors @@ -128,7 +128,7 @@ When using protocol buffers, the well-known type `google.longrunning.Operation` is used. **Note:** For historical reasons, Google uses the term `Operation` to represent -what this document describes as a `Promise`. +what this document describes as a `StatusMonitor`. {% sample 'lro.proto', 'rpc WriteBook' %} From 8c0438a2ae18d2486bf64fbd60f1cddacf84667e Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Tue, 5 Jan 2021 10:58:58 -0800 Subject: [PATCH 06/10] Status monitor redux --- aip/general/0151/aip.md.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index b8d21b82..facb8505 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -26,7 +26,7 @@ significant burdens for clients. amount of time" depending on what work is being done. A good rule of thumb is 10 seconds. -### Promise representation +### Status monitor representation Long-running requests **should** use a common format: From 1d0246ebef4a5198e81b28de783d5b9cf0577900 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Mon, 18 Jan 2021 16:04:15 -0600 Subject: [PATCH 07/10] Add oas interface defintion --- aip/general/0151/aip.md.j2 | 34 ++++++++++++++++++++++ aip/general/0151/lro.oas.yaml | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 aip/general/0151/lro.oas.yaml diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index facb8505..c2c84098 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -154,6 +154,40 @@ what this document describes as a `StatusMonitor`. [`Operations`][lro] service. Individual APIs **must not** define their own interfaces for long-running operations to avoid inconsistency. +{% tab oas %} + +{% sample 'lro.oas.yaml', 'paths' %} + +- The response body schema **must** be an object with `` + - `name` (or `id`): the server-assigned name or id for the operation, + which is only unique within the same service that originally returns it. + - `done`: a boolean that indicates if the operation has completed (`true`) + or is still in progress (`false`). + - `result`: an object field that contains either the result of operation or + error information when the operation has completed, and otherwise is empty. +- The response may contain additional properties containing service-specific + metadata associated with the operation, for example progress information and + common metadata such as create time. +- The method **must** include a `google.longrunning.operation_info` annotation, + which **must** define both response and metadata types. + - The response and metadata types **must** be defined in the file where the + RPC appears, or a file imported by that file. + - If the response and metadata types are defined in another package, the + fully-qualified message name **must** be used. + - The response type **should not** be `google.protobuf.Empty` (except for + [`Delete`][aip-135] methods), unless it is certain that response data will + _never_ be needed. If response data might be added in the future, define an + empty message for the RPC response and use that. + - The metadata type is used to provide information such as progress, partial + failures, and similar information on each `GetOperation` call. The metadata + type **should not** be `google.protobuf.Empty`, unless it is certain that + metadata will _never_ be needed. If metadata might be added in the future, + define an empty message for the RPC metadata and use that. +- APIs with messages that return `Operation` **must** implement the + [`Operations`][lro] service. Individual APIs **must not** define their own + interfaces for long-running operations to avoid inconsistency. + + {% endtabs %} diff --git a/aip/general/0151/lro.oas.yaml b/aip/general/0151/lro.oas.yaml new file mode 100644 index 00000000..5f1131ed --- /dev/null +++ b/aip/general/0151/lro.oas.yaml @@ -0,0 +1,53 @@ +--- +openapi: 3.0.3 +info: + title: Library + version: 1.0.0 +paths: + /v1/resources: + post: + operationId: create_resource + description: Create a resource. + responses: + 202: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ResourceStatus' + +components: + schemas: + ResourceStatus: + description: The status of a create_resource operation + properties: + name: + type: string + description: The server-assigned name, which is only unique within the same service that originally returns it. + done: + type: boolean + description: >- + If the value is false, it means the operation is still in progress. If true, the operation is completed, + and either error or response is available. + result: + description: >- + The operation result, which can be either an error or a valid response. If done == false, neither error + nor response is set. If done == true, exactly one of error or response is set. + oneOf: + - type: object + properties: + error: + $ref: '#/components/schemas/Error' + - type: object + properties: + response: + $ref: '#/components/schemas/Resource' + required: + - name + - done + - result + additionalProperties: + description: >- + Service-specific metadata associated with the operation. It typically contains progress information + and common metadata such as create time. Some services might not provide such metadata. Any method that + returns a long-running operation should document the additional properties that may be returned. From 98012705a8e5c0b3f20dda7e68ed487ee3f4b908 Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Tue, 19 Jan 2021 06:32:09 -0600 Subject: [PATCH 08/10] More oas changes --- aip/general/0151/aip.md.j2 | 47 +++++++++++++++----------------------- 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index c2c84098..0d2135cb 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -1,6 +1,6 @@ # Long-running requests -Occasionally, an service may need to expose an operation that takes a +Occasionally, a service may need to expose an operation that takes a significant amount of time to complete. In these situations, it is often a poor user experience to simply block while the task runs; rather, it is better to return some kind of promise to the user, and allow the user to check back in @@ -28,7 +28,8 @@ amount of time" depending on what work is being done. A good rule of thumb is ### Status monitor representation -Long-running requests **should** use a common format: +The response to a long-running request **should** be a "status monitor" having +the following common format: ```typescript interface StatusMonitor { @@ -158,35 +159,23 @@ what this document describes as a `StatusMonitor`. {% sample 'lro.oas.yaml', 'paths' %} -- The response body schema **must** be an object with `` - - `name` (or `id`): the server-assigned name or id for the operation, - which is only unique within the same service that originally returns it. - - `done`: a boolean that indicates if the operation has completed (`true`) - or is still in progress (`false`). - - `result`: an object field that contains either the result of operation or - error information when the operation has completed, and otherwise is empty. -- The response may contain additional properties containing service-specific +- `202` **must** be the only success status code defined. +- The `202` response **must** define an `application/json` response body and no other + response content types. +- The response body schema **must** be an object with `name`, `done`, and `result` + properties as described above for a StatusMonitor +- The response body schema **may** contain additional properties to hold service-specific metadata associated with the operation, for example progress information and common metadata such as create time. -- The method **must** include a `google.longrunning.operation_info` annotation, - which **must** define both response and metadata types. - - The response and metadata types **must** be defined in the file where the - RPC appears, or a file imported by that file. - - If the response and metadata types are defined in another package, the - fully-qualified message name **must** be used. - - The response type **should not** be `google.protobuf.Empty` (except for - [`Delete`][aip-135] methods), unless it is certain that response data will - _never_ be needed. If response data might be added in the future, define an - empty message for the RPC response and use that. - - The metadata type is used to provide information such as progress, partial - failures, and similar information on each `GetOperation` call. The metadata - type **should not** be `google.protobuf.Empty`, unless it is certain that - metadata will _never_ be needed. If metadata might be added in the future, - define an empty message for the RPC metadata and use that. -- APIs with messages that return `Operation` **must** implement the - [`Operations`][lro] service. Individual APIs **must not** define their own - interfaces for long-running operations to avoid inconsistency. - +- The `response` property of `result` **must** be a schema that defines the success + response for the operation. For an operation that typically gives a `204 No Content` + response, such as a `Delete`, `response` should be defined as an empty object schema. + For a standard `Get/Create/Update` operation, `response` should be a representation + of the resource. +- If a service has any long running operations, the service **must** define an + `StatusMonitor` resource with a `list` operation to retrieve a potentially filtered + list of status monitors and a `get` operation to retrieve a specific status monitor + by its `name`. {% endtabs %} From c79ac73ddc032d50c79d14d14b47c94efaf00c52 Mon Sep 17 00:00:00 2001 From: Luke Sneeringer Date: Mon, 25 Jan 2021 15:25:25 -0800 Subject: [PATCH 09/10] Incorporate @tinnou feedback. --- aip/general/0151/aip.md.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index 0d2135cb..d495c64d 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -117,7 +117,7 @@ time has elapsed after the request completed. Errors that prevent a long-running request from _starting_ **must** return an error response (AIP-193), similar to any other method. -Errors that occur over the course of an request **may** be placed in the +Errors that occur over the course of a request **may** be placed in the metadata message. The errors themselves **must** still be represented with a canonical error object. From ed5b4d597fcd246183eeac63290cc4d28c02545d Mon Sep 17 00:00:00 2001 From: Mike Kistler Date: Tue, 9 Feb 2021 08:35:07 -0600 Subject: [PATCH 10/10] Update oas example for consistency w/ protobuf --- aip/general/0151/aip.md.j2 | 10 +++--- aip/general/0151/lro.oas.yaml | 64 +++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/aip/general/0151/aip.md.j2 b/aip/general/0151/aip.md.j2 index d495c64d..4b017f75 100644 --- a/aip/general/0151/aip.md.j2 +++ b/aip/general/0151/aip.md.j2 @@ -164,10 +164,12 @@ what this document describes as a `StatusMonitor`. response content types. - The response body schema **must** be an object with `name`, `done`, and `result` properties as described above for a StatusMonitor -- The response body schema **may** contain additional properties to hold service-specific - metadata associated with the operation, for example progress information and - common metadata such as create time. -- The `response` property of `result` **must** be a schema that defines the success +- The response body schema **may** contain an object property named `metadata` to + hold service-specific metadata associated with the operation, for example progress + information and common metadata such as create time. The service **should** define + the contents of the `metadata` object in a separate schema, which **should** specify + `additionalProperties: true` to allow for future extensibility. +- The `response` property **must** be a schema that defines the success response for the operation. For an operation that typically gives a `204 No Content` response, such as a `Delete`, `response` should be defined as an empty object schema. For a standard `Get/Create/Update` operation, `response` should be a representation diff --git a/aip/general/0151/lro.oas.yaml b/aip/general/0151/lro.oas.yaml index 5f1131ed..18fc61d4 100644 --- a/aip/general/0151/lro.oas.yaml +++ b/aip/general/0151/lro.oas.yaml @@ -1,4 +1,3 @@ ---- openapi: 3.0.3 info: title: Library @@ -6,20 +5,20 @@ info: paths: /v1/resources: post: - operationId: create_resource - description: Create a resource. + operationId: write_book + description: Write a book. responses: 202: description: OK content: application/json: schema: - $ref: '#/components/schemas/ResourceStatus' + $ref: '#/components/schemas/WriteBookStatus' components: schemas: - ResourceStatus: - description: The status of a create_resource operation + StatusMonitor: + description: The status of a long running operation. properties: name: type: string @@ -28,26 +27,39 @@ components: type: boolean description: >- If the value is false, it means the operation is still in progress. If true, the operation is completed, - and either error or response is available. - result: - description: >- - The operation result, which can be either an error or a valid response. If done == false, neither error - nor response is set. If done == true, exactly one of error or response is set. - oneOf: - - type: object - properties: - error: - $ref: '#/components/schemas/Error' - - type: object - properties: - response: - $ref: '#/components/schemas/Resource' + and either response or error is available. + error: + $ref: '#/components/schemas/Error' required: - name - done - - result - additionalProperties: - description: >- - Service-specific metadata associated with the operation. It typically contains progress information - and common metadata such as create time. Some services might not provide such metadata. Any method that - returns a long-running operation should document the additional properties that may be returned. + + WriteBookStatus: + description: The status of a write_book operation. + allOf: + - $ref: '#/components/schemas/StatusMonitor' + - type: object + properties: + response: + type: string + description: The text that was written. + metadata: + type: object + properties: + start_time: + type: string + format: date-time + description: The time the operation started. + progress_percent: + type: integer + format: int32 + description: The current progress, expressed as an integer. + state: + type: string + description: The current state of the operation. + enum: + - STATE_UNSPECIFIED + - RUNNING + - CANCELLING + - CANCELLED + - FAILED