Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define an additional pattern for resource references. #162

Merged
merged 7 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 67 additions & 8 deletions aep/general/0124/aep.md.j2
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ A resource **must** have at most one canonical parent, and `List` requests
### Multiple many-to-one associations

If a resource has a many-to-one relationship with multiple resource types, it
**must** choose at most one of them to be the parent. The resource
**may** be associated with other resources through other fields on the
resource.
**must** choose at most one of them to be the parent. The resource **may** be
associated with other resources through other fields on the resource.

{% tab proto %}

Expand All @@ -33,7 +32,8 @@ treat the `string parent` field as required as discussed in [list](/list), and
`string filter` field that allows users to filter by other resource
associations as discussed in [filtering](/filtering).

**Note:** Resource reference fields **must** accept the [resource path](/resource-path) of the referenced resource.
**Note:** Resource reference fields **must** accept the
[resource path](/resource-path) of the referenced resource.

### Many-to-many associations

Expand All @@ -45,7 +45,6 @@ An API **may** contain many-to-many relationships, and **should** use a
repeated field containing a list of resource paths, following the principles
described for repeated fields in [arrays][/arrays].


{% tab proto %}

{% sample 'many_to_many_repeated.proto', 'message Book' %}
Expand All @@ -56,8 +55,8 @@ described for repeated fields in [arrays][/arrays].

{% endtabs %}

**Note:** See [arrays](/arrays) for more information on repeated fields, including
how to handle common issues such as atomic changes.
**Note:** See [arrays](/arrays) for more information on repeated fields,
including how to handle common issues such as atomic changes.

If the use of a repeated field is too restrictive, or if more metadata is
required along with the association, an API **may** model a many-to-many
Expand All @@ -78,8 +77,68 @@ recommended if additional metadata is required in the relationship, or if the
restrictions around the use of a repeated field preclude the use of that
approach.

### Embedded resources
rofrankel marked this conversation as resolved.
Show resolved Hide resolved

Resource references as described use string references rather than embedding
one resource inside another because embedding resources can lead to a number of
issues. Retrieving resources with arbitrarily deep nesting may require multiple
serial internal RPCs, leading to complex service dependencies, latency, and
reliability issues.

However, sometimes dereferencing resource references is useful, or even
necessary.

For example, an API may wish to allow filtering of a resource based on a field
of a resource it references. This can be necessary in order to satisfy a query
expressing something like "list books written by authors who were born before
1950", where the author's birth date is a field on the `Author` resource
referenced by the `Book` resource.

In other cases, it may be much cheaper and/or faster for the server to perform
the dereferencing than for the client to make multiple serial requests.

In these cases, an alternative resource association pattern **may** be used,
where the resource reference field is not a string, but is instead the
referenced resource itself.

The default behavior **should** be to populate only the `path` field of the
rofrankel marked this conversation as resolved.
Show resolved Hide resolved
referenced resource, making it equivalent to a string-based resource reference.
Additional fields **may** be included based on the request: for example, if the
request includes a [view or read mask](./partial-responses) that specifies that
additional fields should be included, or if the request contains a filter that
specifies fields other than `path` within the referenced resource.

Fields other than `path` in embedded resource references **must** be treated as
[output-only](./field-behavior); mutating API methods **must not** allow
creating or mutation of embedded resources. For example, if a `Book` resource
has an embedded resource reference to `Author`, the `UpdateBook` method **must
not** allow updating any fields of the `Author` resource other than
`Author.path` (which updates the reference itself, rather than the referenced
resource).

APIs **must** always fully document their behavior with embedded resources,
even if the only supported behavior is populating only the `path` field. If the
referenced resource itself contains embedded resources, the API **must**
include clear documentation about the depth to which embedded resources may be
dereferenced.

**Note:** There is no need for the _referenced_ resource to define a
`PATH_ONLY` [view](./partial-responses) corresponding to the default behavior
of populating only the `path` field. This is because a resource should not have
to define a view enum simply because another resource references it.

{% tab proto %}

{% sample 'embedded_resource.proto', 'message Book' %}

{% tab oas %}

{% sample 'embedded_resource.oas.yaml', 'Book' %}

{% endtabs %}

<!-- prettier-ignore-start -->
[aep-132]: ./0132.md
[aep-144]: ./0144.md
[aep-160]: ./0160.md
<!-- prettier-ignore-end -->
<!-- prettier-ignore-end -->
25 changes: 25 additions & 0 deletions aep/general/0124/embedded_resource.oas.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
openapi: 3.0.3
info:
title: Library
version: 1.0.0
components:
schema:
Book:
description: A representation of a single book.
properties:
path:
type: string
description: |
The path of the book.
Format: publishers/{publisher}/books/{book}
author:
$ref: '#/components/schemas/Author'
description: |
The author or authors of the book.

By default, only the `path` field of the author is populated.
However, the full author can be retrieved by specifying the
FULL_WITH_AUTHOR view. This **will not** dereference resource
references within the author, such as `Author.pen_names`; only
the `path` subfield of these fields will be populated.
38 changes: 38 additions & 0 deletions aep/general/0124/embedded_resource.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
syntax = "proto3";

import "google/api/resource.proto";

message Book {
// The resource path pattern for Book indicates that Publisher is the
// canonical parent.
option (google.api.resource) = {
type: "library.googleapis.com/Book"
pattern: "publishers/{publisher}/books/{book}"
};

// The resource path for the book.
string path = 1 [(google.api.field_behavior) = IDENTIFIER];

// The resource path of the book's author.
//
// By default, only the `path` field of the author is populated.
// However, the full author can be retrieved by specifying the
// FULL_WITH_AUTHOR view. This **will not** dereference resource
// references within the author, such as `Author.pen_names`; only
// the `path` subfield of these fields will be populated.
Author author = 2 [(google.api.resource_reference) = {
type: "library.googleapis.com/Author"
}];

// Different views of a book.
enum View {
// The default view.
VIEW_UNSPECIFIED = 0;

// Includes all fields, but does not derefefence the author.
FULL = 1;

// Includes all fields and also fully populates author.
FULL_WITH_AUTHOR = 2;
}
}
Loading