AIP-4231

Parsing resource names

In resource-oriented design (AIP-121), resources represent the primary nouns within APIs, and often have resource names (AIP-122). These resource names convey information about the structure and hierarchy of the resource structure in that API. APIs accept these resource names as parameters when retrieving and modifying resources, and when referencing them in other objects. However, users may struggle to piece together resource names to send, and client libraries have the opportunity to make this easier through the use of helper components.

Guidance

Client libraries may provide helper classes or functions to make constructing resource names more straightforward. However, client libraries that choose to implement this feature must always accept the plain strings also, for two reasons:

  1. An existing API that adds resource descriptor annotations should be able to do so without incurring a breaking change.
  2. Resource name patterns occasionally evolve, and users need to be able to send and receive resource names that a statically-compiled client library may not yet acknowledge.

Resource messages

A resource in an API always has a message that describes the representation of that resource in that API. Client library generators are able to recognize these messages when they are annotated with the google.api.resource annotation:

// A representation of a Topic in Pub/Sub.
message Topic {
  option (google.api.resource) = {
    type: "pubsub.googleapis.com/Topic"
    pattern: "projects/{project}/topics/{topic}"
  };

  // The resource's name.
  string name = 1;

  // And so on...
}
  • The type field provides the unified resource type name.
    • Client libraries should name their helper component based on this value.
    • The universal resource type usually (but not always) ends with a name that matches the name of the message.
  • The pattern field provides the structure of this resource type’s names.
    • The components in braces represent variable substitutions. Client libraries implementing this feature must accept variables based on these names when building resource name strings.
    • Variable substitution names are usually specified in snake_case, but this is not guaranteed. Client libraries should be able to accept any annotation that uses any sane case system.
    • Patterns are usually slash-separated, but this is not guaranteed. Client libraries should use string interpolation to piece together the resource name.
  • The defining message is expected to contain a field called name, which is the field holding the resource name.
    • APIs are able to override the name field’s name by setting the name_field property on the google.api.resource annotation.
    • Code generators should fail with an error if a message is annotated as a resource and has no name field (either the default of name or the field provided in the name_field property of the annotation). Code generators should also fail with an error if the field is not a string).

Multi-pattern resources

Occasionally, a resource may have more than one pattern. The common case for this is when a resource can live under more than one parent type. In this situation, the pattern field on the annotation can be specified more than once:

message LogEntry {
  option (google.api.resource) = {
    type: "logging.googleapis.com/Log"
    pattern: "projects/{project}/logs/{log}"
    pattern: "organizations/{organization}/logs/{log}"
    pattern: "folders/{folder}/logs/{log}"
    pattern: "billingAccounts/{billing_account}/logs/{log}"
  }

  // The resource's name.
  string name = 1;

  // And so on...
}

If necessary, client libraries may create a separate helper component for each pattern, and may provide a rollup component.

Resources without messages

Occasionally, a resource may be implicitly defined by an API service, but not have an explicit message representing that resource. (For example, the Firestore API defines databases as a common ancestor to its resources, but does not define a database message.)

In this situation, APIs annotate the resource on the file instead of on a message, using the google.api.resource_definition annotation:

option (google.api.resource_definition) = {
  type: "firestore.googleapis.com/Database"
  pattern: "projects/{project}/databases/{database}"
};

Client library generators implementing this feature must generate the same utility components that would be generated when encountering a resource message.

Referencing other resources

APIs often use resource names for referencing fields defined elsewhere. This is particularly common with the request messages for the standard methods, such as Get and Update; however, resources and other structures use resource name strings as references also.

Client libraries implementing this feature should also provide their helper components when resources are being referenced. Client libraries are able to recognize these fields when they are annotated with the google.api.resource_reference annotation:

message GetTopicRequest {
  // The name of the topic to retrieve.
  string name = 1 [(google.api.resource_reference) = {
    type: "pubsub.googleapis.com/Topic"
  }];
}

The resource reference references the unified resource type name.

Some methods also refer to the parent of a type, and in situations where there are multiple parents, it is repetitive and error-prone to refer to each individual parent type. In these situations, API producers specify child_type rather than type:

message ListLogEntriesRequest {
  // The collection of log entries to list.
  string parent = 1 [(google.api.resource_reference) = {
    child_type: "logging.googleapis.com/Log"
  }];
}

In this situation, client library generators implementing this feature must derive the set of parent resources from the child type.

Referencing an arbitrary resource

Occasionally, a field may reference an arbitrary resource. In this case, APIs use the special value * in their resource reference.

message GetIamPolicyRequest {
  message resource = 1 [(google.api.resource_reference) = {
    type: "*"
  }];
}

In this situation, client library generators implementing this feature may provide a generic utility class or function to address that resource name.

Backwards compatibility

Client libraries implementing helper components for resources must conform to the following backwards-compatibility expectations:

  • The addition of a google.api.resource annotation on an existing message must be a backwards-compatible change.
  • An existing resource must be able to add new patterns without breaking changes as long as the following conditions are met:
    • New patterns must always be appended to the list.
    • If migrating from one pattern to two patterns, the ORIGINALLY_SINGLE_PATTERN flag is also added.
    • If migrating from one pattern with the the FUTURE_MULTI_PATTERN flag set, the ORIGINALLY_SINGLE_PATTERN flag is not added.
  • The addition of a google.api.resource_reference annotation on an existing field must be a backwards-compatible change.

Further reading

  • For more on resource names and patterns, see AIP-122.
  • For more on unified resource types, see AIP-123.

Changelog

  • 2019-09-16: Added guidance for resources without messages.