From 431d7802a96baed3533150fc46bdd975d04f7f21 Mon Sep 17 00:00:00 2001 From: wenmh-coder Date: Tue, 28 Apr 2026 15:27:19 -0700 Subject: [PATCH 1/4] Update 0164.md Update AIP-164 to include Expunge AIP (go/api-expunge). --- aip/general/0164.md | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/aip/general/0164.md b/aip/general/0164.md index b19793b1ef..1de27c1e2f 100644 --- a/aip/general/0164.md +++ b/aip/general/0164.md @@ -98,6 +98,62 @@ rpc UndeleteBook(UndeleteBookRequest) returns (google.longrunning.Operation) { be if the RPC was not long-running). - Both the `response_type` and `metadata_type` fields **must** be specified. +### Expunge + +Resources that support soft delete **may** provide an `Expunge` custom method to allow users to trigger immediate permanent deletion of a ready or soft-deleted resource. This method can operate on resources that are currently in a ready or soft-deleted state (e.g., `delete_time` is set). + +```proto +// Permanently deletes a soft-deleted Book. +rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/{name=publishers/*/books/*}:expunge" + body: "*" + }; + option (google.api.method_signature) = "name"; +} +``` + +- The URI must use a custom method with the :expunge suffix. +- The HTTP verb must be POST and the body clause must be "*". +- The response message must be google.protobuf.Empty or a Long-Running Operation. + +### Expunge request message + +Expunge methods implement a common request message pattern: + +```proto +message ExpungeBookRequest { + // The name of the soft-deleted book to expunge. + // Format: publishers/{publisher}/books/{book} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference).type = "library.googleapis.com/Book" + ]; +} +``` + +- The request message must include the resource name to be expunged. +- No other fields are typically necessary. + +### Long-running expunge + +If the expunge process takes significant time, the method may be a Long-Running Operation (AIP-151) instead: + +```proto +// Permanently deletes a soft-deleted Book. +rpc ExpungeBook(ExpungeBookRequest) returns (google.longrunning.Operation) { + option (google.api.http) = { + post: "/v1/{name=publishers/*/books/*}:expunge" + body: "*" + }; + option (google.longrunning.operation_info) = { + response_type: "google.protobuf.Empty" + metadata_type: "OperationMetadata" + }; + option (google.api.method_signature) = "name"; +} +``` + ### List and Get Soft-deleted resources **should not** be returned in `List` (AIP-132) responses @@ -139,6 +195,16 @@ If the user calling `Undelete` has proper permission, but the requested resource is not deleted, the service **must** respond with `ALREADY_EXISTS` (HTTP 409). +If the user calling `Expunge` requests a resource that does not exist (was never +created or already expunged), the method **must** return `NOT_FOUND` (HTTP 404). + +If the resource exists but is not in a ready or soft-deleted state, the method +**must** return `FAILED_PRECONDITION` (HTTP 400). + +Standard permission errors (`PERMISSION_DENIED`) apply. Services **must** require +an explicit expunge permission that is separate from standard delete permissions +(e.g., `..expunge`). + ## Further reading - For the `Delete` standard method, see AIP-135. @@ -148,6 +214,7 @@ resource is not deleted, the service **must** respond with `ALREADY_EXISTS` ## Changelog +- **2026-04-28**: Added guidance for the `Expunge` custom method. - **2024-09-24**: Included missing requirement for `delete_time`. - **2023-07-13**: Renamed overloaded `expire_time` to `purge_time`. - **2021-07-12**: Added error behavior when soft deleting a deleted resource. From 3ea8404b8c6c54fb6ef702b605f56777d1261e45 Mon Sep 17 00:00:00 2001 From: wenmh-coder Date: Tue, 28 Apr 2026 15:41:38 -0700 Subject: [PATCH 2/4] Update 0164.md Move the section of ### Expunge request message after ### Long-Running Version. --- aip/general/0164.md | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/aip/general/0164.md b/aip/general/0164.md index 1de27c1e2f..ebe5b0f46e 100644 --- a/aip/general/0164.md +++ b/aip/general/0164.md @@ -100,7 +100,10 @@ rpc UndeleteBook(UndeleteBookRequest) returns (google.longrunning.Operation) { ### Expunge -Resources that support soft delete **may** provide an `Expunge` custom method to allow users to trigger immediate permanent deletion of a ready or soft-deleted resource. This method can operate on resources that are currently in a ready or soft-deleted state (e.g., `delete_time` is set). +Resources that support soft delete **may** provide an `Expunge` custom method to +allow users to trigger immediate permanent deletion of a ready or soft-deleted +resource. This method can operate on resources that are currently in a ready or +soft-deleted state (e.g., `delete_time` is set). ```proto // Permanently deletes a soft-deleted Book. @@ -117,24 +120,6 @@ rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) { - The HTTP verb must be POST and the body clause must be "*". - The response message must be google.protobuf.Empty or a Long-Running Operation. -### Expunge request message - -Expunge methods implement a common request message pattern: - -```proto -message ExpungeBookRequest { - // The name of the soft-deleted book to expunge. - // Format: publishers/{publisher}/books/{book} - string name = 1 [ - (google.api.field_behavior) = REQUIRED, - (google.api.resource_reference).type = "library.googleapis.com/Book" - ]; -} -``` - -- The request message must include the resource name to be expunged. -- No other fields are typically necessary. - ### Long-running expunge If the expunge process takes significant time, the method may be a Long-Running Operation (AIP-151) instead: @@ -154,6 +139,24 @@ rpc ExpungeBook(ExpungeBookRequest) returns (google.longrunning.Operation) { } ``` +### Expunge request message + +Expunge methods implement a common request message pattern: + +```proto +message ExpungeBookRequest { + // The name of the soft-deleted book to expunge. + // Format: publishers/{publisher}/books/{book} + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference).type = "library.googleapis.com/Book" + ]; +} +``` + +- The request message must include the resource name to be expunged. +- No other fields are typically necessary. + ### List and Get Soft-deleted resources **should not** be returned in `List` (AIP-132) responses From 5f4a1f9abfe70557749dc3131ea868eebfd63b37 Mon Sep 17 00:00:00 2001 From: wenmh-coder Date: Tue, 28 Apr 2026 15:51:02 -0700 Subject: [PATCH 3/4] Update 0164.md Applied the suggested changes from reviewer @noahdietz --- aip/general/0164.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/aip/general/0164.md b/aip/general/0164.md index ebe5b0f46e..b6a77b8c93 100644 --- a/aip/general/0164.md +++ b/aip/general/0164.md @@ -118,11 +118,11 @@ rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) { - The URI must use a custom method with the :expunge suffix. - The HTTP verb must be POST and the body clause must be "*". -- The response message must be google.protobuf.Empty or a Long-Running Operation. +- The response message must be `google.protobuf.Empty` or a `google.longrunning.Operation`. ### Long-running expunge -If the expunge process takes significant time, the method may be a Long-Running Operation (AIP-151) instead: +If the expunge process takes significant time, the method **may** be a `google.longrunning.Operation` (AIP-151) instead: ```proto // Permanently deletes a soft-deleted Book. @@ -154,8 +154,8 @@ message ExpungeBookRequest { } ``` -- The request message must include the resource name to be expunged. -- No other fields are typically necessary. +- The request message **must** refer to the resource to be expunged by name. +- There **should not** be any other request fields. ### List and Get From 456b72822f53337c05ff9714677f46a6343cc647 Mon Sep 17 00:00:00 2001 From: wenmh-coder Date: Tue, 28 Apr 2026 16:01:30 -0700 Subject: [PATCH 4/4] Update 0164.md Update the format and break it to two lines for `If the expunge process takes significant time, the method **may** be a `google.longrunning.Operation` (AIP-151) instead:` --- aip/general/0164.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/aip/general/0164.md b/aip/general/0164.md index b6a77b8c93..9d4e21a31d 100644 --- a/aip/general/0164.md +++ b/aip/general/0164.md @@ -122,7 +122,8 @@ rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) { ### Long-running expunge -If the expunge process takes significant time, the method **may** be a `google.longrunning.Operation` (AIP-151) instead: +If the expunge process takes significant time, the method **may** be a +`google.longrunning.Operation` (AIP-151) instead: ```proto // Permanently deletes a soft-deleted Book.