diff --git a/aep/general/0121/aep.md.j2 b/aep/general/0121/aep.md.j2 index 47d2fc57..e0cb3dc3 100644 --- a/aep/general/0121/aep.md.j2 +++ b/aep/general/0121/aep.md.j2 @@ -83,16 +83,21 @@ patterns, such as database transactions, import and export, or data analysis. ### Strong Consistency -For methods that operate on the [management plane][], the completion of those -operations (either successful or with an error, -[long-running][long-running-requests] or synchronous) **must** mean that the -state of the resource's existence and all user-settable values have reached a -steady-state. +A method is strongly consistent if, upon completion of a request of that method +(regardless of whether the request is successful, +[long-running][long-running-requests] or synchronous), all user-settable fields +will return the same value on any subsequent requests until another request +which mutates the resource is completed. -[Output only][output only] values unrelated to the resource [state][] -**should** also have reached a steady-state. +[Output only][output only] fields unrelated to the resource [state][] **should** +also return the same value on each subsequent request until another request +which mutates the resource is completed. -Examples include: +- An output-only field that takes so long to reach a steady-state that it would + negatively impact the user experience of the request (e.g. an hour-long instantiation + of a VM cluster). + +Examples of strong consistency include: - Following a successful create that is is latest mutation on a resource, a get request for a resource **must** return the resource. @@ -141,7 +146,6 @@ turn do not increase resource management complexity. [get]: ./0131.md [list]: ./0132.md [long-running-requests]: ./0151.md -[management plane]: ./0111.md#management-plane [output only]: ./0203.md#output-only [rest]: https://en.wikipedia.org/wiki/Representational_state_transfer [resource references]: ./0122.md#fields-representing-another-resource diff --git a/aep/general/0133/aep.md.j2 b/aep/general/0133/aep.md.j2 index f3c7020a..726b1172 100644 --- a/aep/general/0133/aep.md.j2 +++ b/aep/general/0133/aep.md.j2 @@ -57,10 +57,7 @@ rpc CreateBook(CreateBookRequest) returns (Book) { - There **should** be exactly one `google.api.method_signature` annotation, with a value of `"parent,{resource},id"`, or "`"parent,{resource}"` if the resource ID is not required. -- If the API is operating on the [management plane][], the operation should have - [strong consistency][]: the completion of a create operation **must** mean - that all user-settable values and the existence of the resource have reached a - steady-state and reading resource state returns a consistent response. +- The operation **must** have [strong consistency][]. {% tab oas %} @@ -72,8 +69,7 @@ rpc CreateBook(CreateBookRequest) returns (Book) { Create methods implement a common request message pattern: -- An `id` field **must** be included for management plane resources, and - **should** be included for data plane resources. +- An `id` field **should** be supported. - The resource field **must** be included and **must** map to the POST body. - The request message **must not** contain any other required fields and **should not** contain other optional fields except those described in this @@ -148,16 +144,15 @@ rpc CreateBook(CreateBookRequest) returns (aep.api.Operation) { ### User-specified IDs -An API **must** allow a user to specify the ID component of a resource (the -last segment of the resource path) on creation if the API is operating on the -[management plane][]. +An API **should** allow a user to specify the ID component of a resource: not +doing so introduces a non-idempotent request in the API, as sending the same +payload results in creating a new resource each time. -On the [data plane][], an API **should** allow a user to specify the ID. Exceptional cases should have the following behavior: -- The data plane resource allows identical records without a need to - disambiguate between the two (e.g. rows in a table with no primary key). -- The data plane resource will not be exposed in [Declarative clients][]. +- The resource allows identical records without a need to disambiguate between + the two (e.g. rows in a table with no primary key). +- The resource will not be exposed in [Declarative clients][]. An API **may** allow the `id` field to be optional, and give the resource a system-generated ID if one is not specified. @@ -233,7 +228,6 @@ path and use it in references from other resources. [aep-203]: ./0203.md [aep-210]: ./0210.md [data plane]: ./0111.md#data-plane -[management plane]: ./0111.md#management-plane [errors]: ./0193.md [field_behavior]: ./203.md [Declarative clients]: ./0003.md#declarative-clients diff --git a/aep/general/0134/aep.md.j2 b/aep/general/0134/aep.md.j2 index 628be0dc..327ce791 100644 --- a/aep/general/0134/aep.md.j2 +++ b/aep/general/0134/aep.md.j2 @@ -29,6 +29,7 @@ Update methods are specified using the following pattern: itself. - The method **should** support partial resource update, and the HTTP verb **should** be `PATCH`. +- The operation **must** have [strong consistency][]. {% tab proto %} @@ -190,7 +191,6 @@ rpc UpdateBook(UpdateBookRequest) returns (aep.api.Operation) { {% endtabs %} -### Create or update If the service uses client-assigned resource paths, `Update` methods **may** expose a `bool allow_missing` field, which will cause the method to succeed in @@ -229,6 +229,50 @@ More specifically, the `allow_missing` flag triggers the following behavior: The user **must** have the update permissions to call `Update` even with `allow_missing` set to `true`. +If the service uses client-assigned resource paths, `Update` methods **may** +expose a `bool allow_missing` field, which will cause the method to succeed in +the event that the user attempts to update a resource that is not present (and +will create the resource in the process): + +{% tab proto %} + +```proto +message UpdateBookRequest { + // The book to update. + // + // The book's `path` field is used to identify the book to be updated. + // Format: publishers/{publisher}/books/{book} + Book book = 1 [(google.api.field_behavior) = REQUIRED]; + + // The list of fields to be updated. + google.protobuf.FieldMask update_mask = 2; + + // If set to true, and the book is not found, a new book will be created. + // In this situation, `update_mask` is ignored. + bool allow_missing = 3; +} +``` + +{% tab oas %} + +**Note:** OAS example not yet written. + +{% endtabs %} + +More specifically, the `allow_missing` flag triggers the following behavior: + +- If the method call is on a resource that does not exist, the resource is + created. All fields are applied regardless of any provided field mask. + - However, if any required fields are missing or fields have invalid values, + an `INVALID_ARGUMENT` error is returned. +- If the method call is on a resource that already exists, and all fields + match, the existing resource is returned unchanged. +- If the method call is on a resource that already exists, only fields declared + in the field mask are updated. + +The user **must** have the update permissions to call `Update` even with +`allow_missing` set to `true`. + ### Etags An API may sometimes need to allow users to send update requests which are @@ -312,7 +356,6 @@ unless `allow_missing` is set to `true`. [aep-203]: ./0203.md [create]: ./0133.md [errors]: ./0193.md -[management plane]: ./0111.md#management-plane [permission-denied]: ./0193.md#permission-denied [state fields]: ./0216.md [strong consistency]: ./0121.md#strong-consistency diff --git a/aep/general/0135/aep.md.j2 b/aep/general/0135/aep.md.j2 index aa956ac1..fab1edda 100644 --- a/aep/general/0135/aep.md.j2 +++ b/aep/general/0135/aep.md.j2 @@ -17,10 +17,10 @@ The Delete method **should** succeed if and only if a resource was present and was successfully deleted. If the resource did not exist, the method **should** send a `404 Not found` (`NOT_FOUND`) error. -If the API is operating on the [Management Plane][], the method should have -[strong consistency][]: the completion of a delete method **must** mean that -the existence of the resource has reached a steady-state and reading resource -state returns a consistent `404 Not found` (`NOT_FOUND`) response. +The method **must** have [strong consistency][]: the completion of a delete +method **must** mean that the existence of the resource has reached a +steady-state and reading resource state returns a consistent `404 Not found` +(`NOT_FOUND`) response. Delete methods are specified using the following pattern: @@ -230,7 +230,6 @@ exist, the service **must** error with `404 Not found` (`NOT_FOUND`). [aep-203]: ./0203.md [aep-214]: ./0214.md [aep-216]: ./0216.md -[management plane]: ./0111.md#management-plane [strong consistency]: ./0121.md#strong-consistency [etag]: ./0134.md#etags [RFC 9110]: https://www.rfc-editor.org/rfc/rfc9110.html#name-delete diff --git a/aep/general/0154/aep.md.j2 b/aep/general/0154/aep.md.j2 index 8e6c3f68..8b615a50 100644 --- a/aep/general/0154/aep.md.j2 +++ b/aep/general/0154/aep.md.j2 @@ -51,7 +51,7 @@ current ETag. If the `If-Match` header value does not match the ETag, the service **must** reply with an HTTP 412 error. If the user omits the `If-Match` header, the service **should** permit the -request. However, services with strong consistency or parallelism requirements +request. However, services with [strong consistency][] or parallelism requirements **may** require users to send ETags all the time and reject the request with an HTTP 400 error if it does not contain an ETag. @@ -113,3 +113,4 @@ not equivalent to the old one). - **2019-09-23**: Changed the title to "resource freshness validation". [rfc 7232]: https://tools.ietf.org/html/rfc7232#section-2.3 +[strong consistency]: ./0121.md#strong-consistency