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}"
  };

  // name 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 coherent 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}"
  };

  // name 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. Client library generators must fail with an error if both type and child_type are provided.

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 {
  string 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.

Complex resource ID path segments

Warning: Complex resource ID path segments should not generally be used in new APIs. AIP-124 contains advice on handling many-to-many associations.

Resource patterns may contain resource ID path segments which contain multiple pattern variables separated by a variable separator:

message FeedItemTarget {
  option (google.api.resource) = {
    type: "googleads.googleapis.com/FeedItemTarget"
    pattern: "customers/{customer}/feedItemTargets/{feed}~{feed_item}"
  };

  // name and other fields...
}

This is only used when the resource ID is naturally in multiple parts and it is useful for the user to be able to manipulate the separate ID parts.

  • A variable separator is one character long, and must only one of: _, -, ., ~ (underscore, hyphen, period, tilde).
  • A variable separator must not appear before the first pattern variable or after the last pattern variable.

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 (including the "*" wildcard pattern) without breaking changes as long as the following conditions are met:
    • New patterns must always be appended to the list.
    • New patterns must use a distinct sequence of collection identifiers (see AIP-122) compared with all existing patterns within this resource.
  • The identifiers within the pattern variables are final - they must not be changed. These identifiers are used in the surface of generated client libraries.
  • The addition of a google.api.resource_reference annotation on an existing field must be a backwards-compatible change.
  • Changing a google.api.resource_reference from child_type to type must be a backwards-compatible change when the child_type referenced has a single pattern and the newly referenced type is the matching parent resource pattern (see the Referencing other resources section for more info).
  • For references that appear in a request, changing from type to child_type must be a backwards-compatible change when the resource pattern originally referenced via the type is included in the set of possible resource patterns derived through child_type resolution (see the above Multi-pattern resources and Referencing other resources sections for more examples).

Note: The ORIGINALLY_SINGLE_PATTERN and FUTURE_MULTI_PATTERN flags are deprecated, and must not be used.

Further reading

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

Changelog

  • 2023-03-25: Include compatibility guidance for type and child_type.
  • 2022-10-28: Pattern variables are considered final
  • 2020-09-14: Disallow simultaneous use of both type and child_type.
  • 2020-05-14: Added complex resource ID path segments.
  • 2020-05-07: Updated backwards compatibility guidance.
  • 2019-09-16: Added guidance for resources without messages.