This AIP is currently a draft. This means that it is being actively debated and discussed, and it may change in non-trivial ways.

AIP-162

Resource Revisions

Some APIs need to have resources with a revision history, where users can reason about the state of the resource over time. There are several reasons for this:

  • Users may want to be able to roll back to a previous revision, or diff against a previous revision.
  • An API may create data which is derived in some way from a resource at a given point in time. In these cases, it may be desirable to snapshot the resource for reference later.

Note: We use the word revision to refer to a historical reference for a particular resource, and intentionally avoid the term version, which refers to the version of an API as a whole.

Guidance

APIs may store a revision history for a resource. Examples of when it is useful include:

  • When it is valuable to expose older versions of a resource via an API. This can avoid the overhead of the customers having to write their own API to store and enable retrieval of revisions.
  • Other resources depend on different revisions of a resource.
  • There is a need to represent the change of a resource over time.

APIs implementing resources with a revision history should abstract resource revisions as nested collection of the resource. Sometimes, the revisions collection can be a top level collection, exceptions include:

  • If resource revisions are meant to have longer lifespan than the parent resource. In other words, resource revisions exist after resource deletion.
message BookRevision {
  // The name of the book revision.
  string name = 1;

  // The snapshot of the book
  Book snapshot = 2
    [(google.api.field_behavior) = OUTPUT_ONLY];

  // The timestamp that the revision was created.
  google.protobuf.Timestamp create_time = 3
    [(google.api.field_behavior) = OUTPUT_ONLY];

  // Other revision IDs that share the same snapshot.
  repeated string alternate_ids = 4
    [(google.api.field_behavior) = OUTPUT_ONLY];
}
  • The message must be annotated as a resource (AIP-123).
  • The message name must be named {ResourceType}Revision.
  • The resource revision must contain a field with a message type of the parent resource, with a field name of snapshot.
    • The value of snapshot must be the configuration of the parent at the point in time the revision was created.
  • The resource revision must contain a create_time field (see AIP-142).
  • The resource revision may contain a repeated field alternate_ids, which would contain a list of resource IDs that the revision is also known by (e.g. latest)

Creating Revisions

Depending on the resource, different APIs may have different strategies for

  • Create a new revision any time that there is a change to the parent resource
  • Create a new revision when important system state changes
  • Create a new revision when specifically requested

APIs may use any of these strategies. APIs must document their revision creation strategy.

Resource names for revisions

When referring to specific revision of a resource, the subcollection name must be named revisions. Resource revisions have names with the format {resource_name}/revisions/{revision_id}. For example:

publishers/123/books/les-miserables/revisions/c7cfa2a8

Server-specified Aliases

Services may reserve specific IDs to be aliases (e.g. latest). These are read-only and managed by the service.

GET /v1/publishers/{publisher}/books/{book}/revisions/{revision_id}
  • If a latest ID exists, it must represent the most recently created revision. The content of publishers/{publisher}/books/{book}/revisions/latest and publishers/{publisher}/books/{book} can differ, as the latest revision may be different from the current state of the resource.

User-Specified Aliases

APIs may provide a mechanism for users to assign an alias ID to an existing revision with a custom method "alias":

rpc AliasBookRevision(AliasBookRevisionRequest) returns (Book) {
  option (google.api.http) = {
    post: "/v1/{name=publishers/*/books/*/revisions/*}:alias"
    body: "*"
  };
}
message AliasBookRevisionRequest {
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "library.googleapis.com/BookRevision"
    }];

  // The ID of the revision to alias to, e.g. `CURRENT` or a semantic
  // version.
  string alias_id = 2 [(google.api.field_behavior) = REQUIRED];
}
  • The request message must have a name field:
  • The request message must have a alias_id field: