Skip to content

Commit 1fe7137

Browse files
authored
feat(AIP-164): add Expunge pattern
1 parent 82b4727 commit 1fe7137

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

aip/general/0164.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,66 @@ rpc UndeleteBook(UndeleteBookRequest) returns (google.longrunning.Operation) {
9898
be if the RPC was not long-running).
9999
- Both the `response_type` and `metadata_type` fields **must** be specified.
100100

101+
### Expunge
102+
103+
Resources that support soft delete **may** provide an `Expunge` custom method to
104+
allow users to trigger immediate permanent deletion of a ready or soft-deleted
105+
resource. This method can operate on resources that are currently in a ready or
106+
soft-deleted state (e.g., `delete_time` is set).
107+
108+
```proto
109+
// Permanently deletes a soft-deleted Book.
110+
rpc ExpungeBook(ExpungeBookRequest) returns (google.protobuf.Empty) {
111+
option (google.api.http) = {
112+
post: "/v1/{name=publishers/*/books/*}:expunge"
113+
body: "*"
114+
};
115+
option (google.api.method_signature) = "name";
116+
}
117+
```
118+
119+
- The URI must use a custom method with the :expunge suffix.
120+
- The HTTP verb must be POST and the body clause must be "*".
121+
- The response message must be `google.protobuf.Empty` or a `google.longrunning.Operation`.
122+
123+
### Long-running expunge
124+
125+
If the expunge process takes significant time, the method **may** be a
126+
`google.longrunning.Operation` (AIP-151) instead:
127+
128+
```proto
129+
// Permanently deletes a soft-deleted Book.
130+
rpc ExpungeBook(ExpungeBookRequest) returns (google.longrunning.Operation) {
131+
option (google.api.http) = {
132+
post: "/v1/{name=publishers/*/books/*}:expunge"
133+
body: "*"
134+
};
135+
option (google.longrunning.operation_info) = {
136+
response_type: "google.protobuf.Empty"
137+
metadata_type: "OperationMetadata"
138+
};
139+
option (google.api.method_signature) = "name";
140+
}
141+
```
142+
143+
### Expunge request message
144+
145+
Expunge methods implement a common request message pattern:
146+
147+
```proto
148+
message ExpungeBookRequest {
149+
// The name of the soft-deleted book to expunge.
150+
// Format: publishers/{publisher}/books/{book}
151+
string name = 1 [
152+
(google.api.field_behavior) = REQUIRED,
153+
(google.api.resource_reference).type = "library.googleapis.com/Book"
154+
];
155+
}
156+
```
157+
158+
- The request message **must** refer to the resource to be expunged by name.
159+
- There **should not** be any other request fields.
160+
101161
### List and Get
102162

103163
Soft-deleted resources **should not** be returned in `List` (AIP-132) responses
@@ -139,6 +199,16 @@ If the user calling `Undelete` has proper permission, but the requested
139199
resource is not deleted, the service **must** respond with `ALREADY_EXISTS`
140200
(HTTP 409).
141201

202+
If the user calling `Expunge` requests a resource that does not exist (was never
203+
created or already expunged), the method **must** return `NOT_FOUND` (HTTP 404).
204+
205+
If the resource exists but is not in a ready or soft-deleted state, the method
206+
**must** return `FAILED_PRECONDITION` (HTTP 400).
207+
208+
Standard permission errors (`PERMISSION_DENIED`) apply. Services **must** require
209+
an explicit expunge permission that is separate from standard delete permissions
210+
(e.g., `<service>.<resource>.expunge`).
211+
142212
## Further reading
143213

144214
- For the `Delete` standard method, see AIP-135.
@@ -148,6 +218,7 @@ resource is not deleted, the service **must** respond with `ALREADY_EXISTS`
148218

149219
## Changelog
150220

221+
- **2026-04-28**: Added guidance for the `Expunge` custom method.
151222
- **2024-09-24**: Included missing requirement for `delete_time`.
152223
- **2023-07-13**: Renamed overloaded `expire_time` to `purge_time`.
153224
- **2021-07-12**: Added error behavior when soft deleting a deleted resource.

0 commit comments

Comments
 (0)