Skip to content

Commit 093a1e3

Browse files
feat(124): Resource Associations (#146)
This adds AEP-0124 along with proto and OpenAPI examples. --------- Co-authored-by: Yusuke Tsutsumi <[email protected]>
1 parent 319a84f commit 093a1e3

8 files changed

+204
-5
lines changed

aep/general/0124/aep.md.j2

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,85 @@
11
# Resource association
22

3-
**Note:** This AEP has not yet been adopted. See
4-
[this GitHub issue](https://github.com/aep-dev/aep.dev/issues/57) for more
5-
information.
3+
APIs sometimes have resource relationships that can not be cleanly expressed in
4+
a hierarchical structure. For example, a resource may have a many-to-one
5+
relationship with two other resource types instead of just one. Alternatively,
6+
a resource may have a many-to-many relationship with another resource type.
7+
8+
## Guidance
9+
10+
A resource **must** have at most one canonical parent, and `List` requests
11+
**must not** require two distinct "parents".
12+
13+
### Multiple many-to-one associations
14+
15+
If a resource has a many-to-one relationship with multiple resource types, it
16+
**must** choose at most one of them to be the parent. The resource
17+
**may** be associated with other resources through other fields on the
18+
resource.
19+
20+
{% tab proto %}
21+
22+
{% sample 'multiple_many_to_one.proto', 'message Book' %}
23+
24+
{% tab oas %}
25+
26+
{% sample 'multiple_many_to_one.oas.yaml', 'Book' %}
27+
28+
{% endtabs %}
29+
30+
When listing resources with multiple associations in this way, the RPC **must**
31+
treat the `string parent` field as required as discussed in [list](/list), and
32+
**must not** add additional required arguments. The RPC **should** include a
33+
`string filter` field that allows users to filter by other resource
34+
associations as discussed in [filtering](/filtering).
35+
36+
**Note:** Resource reference fields **must** accept the [resource path](/resource-path) of the referenced resource.
37+
38+
### Many-to-many associations
39+
40+
Many-to-many associations are less common in APIs than they are in relational
41+
databases, in part because they are more difficult to model and present over
42+
network interfaces.
43+
44+
An API **may** contain many-to-many relationships, and **should** use a
45+
repeated field containing a list of resource paths, following the principles
46+
described for repeated fields in [arrays][/arrays].
47+
48+
49+
{% tab proto %}
50+
51+
{% sample 'many_to_many_repeated.proto', 'message Book' %}
52+
53+
{% tab oas %}
54+
55+
{% sample 'many_to_many_repeated.oas.yaml', 'Book' %}
56+
57+
{% endtabs %}
58+
59+
**Note:** See [arrays](/arrays) for more information on repeated fields, including
60+
how to handle common issues such as atomic changes.
61+
62+
If the use of a repeated field is too restrictive, or if more metadata is
63+
required along with the association, an API **may** model a many-to-many
64+
relationship using a sub-resource with two one-to-many associations.
65+
66+
{% tab proto %}
67+
68+
{% sample 'many_to_many_subresource.proto', 'message BookAuthor' %}
69+
70+
{% tab oas %}
71+
72+
{% sample 'many_to_many_subresource.oas.yaml', 'BookAuthor' %}
73+
74+
{% endtabs %}
75+
76+
**Note:** Using subresources to model an association between resources is only
77+
recommended if additional metadata is required in the relationship, or if the
78+
restrictions around the use of a repeated field preclude the use of that
79+
approach.
80+
81+
<!-- prettier-ignore-start -->
82+
[aep-132]: ./0132.md
83+
[aep-144]: ./0144.md
84+
[aep-160]: ./0160.md
85+
<!-- prettier-ignore-end -->

aep/general/0124/aep.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
id: 124
3-
state: reviewing
3+
state: approved
44
slug: resource-association
5-
created: 2023-01-22
5+
created: 2024-03-15
66
placement:
77
category: resources
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
components:
7+
schema:
8+
Book:
9+
description: A representation of a single book.
10+
properties:
11+
path:
12+
type: string
13+
description: |
14+
The path of the book.
15+
Format: publishers/{publisher}/books/{book}
16+
authors:
17+
type: array
18+
items:
19+
type: string
20+
description: The author or authors of the book.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
syntax = "proto3";
2+
3+
import "google/api/field_behavior.proto";
4+
import "google/api/resource.proto";
5+
6+
message Book {
7+
option (google.api.resource) = {
8+
type: "library.googleapis.com/Book"
9+
pattern: "publishers/{publisher}/books/{book}"
10+
};
11+
12+
string path = 1 [(google.api.field_behavior) = IDENTIFIER];
13+
14+
// The resource paths for the book's authors.
15+
repeated string authors = 2 [(google.api.resource_reference) = {
16+
type: "library.googleapis.com/Author"
17+
}];
18+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
components:
7+
schema:
8+
BookAuthor:
9+
description: A representation of a book being written by an author.
10+
properties:
11+
path:
12+
type: string
13+
description: |
14+
The path of the book.
15+
Format: publishers/{publisher}/books/{book}
16+
authors:
17+
type: string
18+
description: |
19+
The author of the book.
20+
Format publishers/{publishers}/authors/{author}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
syntax = "proto3";
2+
3+
import "google/api/field_behavior.proto";
4+
import "google/api/resource.proto";
5+
6+
message BookAuthor {
7+
// The resource pattern for BookAuthor indicates that Book is the
8+
// canonical parent.
9+
option (google.api.resource) = {
10+
type: "library.googleapis.com/BookAuthor"
11+
pattern: "publishers/{publisher}/books/{book}/authors/{book_author}"
12+
};
13+
14+
// The resource path for the book-author association.
15+
string path = 1 [(google.api.field_behavior) = IDENTIFIER];
16+
17+
// The resource path for the author.
18+
string author = 2 [(google.api.resource_reference) = {
19+
type: "library.googleapis.com/Author"
20+
}];
21+
22+
// Other fields...
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
openapi: 3.0.3
3+
info:
4+
title: Library
5+
version: 1.0.0
6+
components:
7+
schema:
8+
Book:
9+
description: A representation of a single book.
10+
properties:
11+
path:
12+
type: string
13+
description: |
14+
The path of the book.
15+
Format: publishers/{publisher}/books/{book}
16+
author:
17+
type: string
18+
description: The author or authors of the book.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
syntax = "proto3";
2+
3+
import "google/api/resource.proto";
4+
5+
message Book {
6+
// The resource path pattern for Book indicates that Publisher is the
7+
// canonical parent.
8+
option (google.api.resource) = {
9+
type: "library.googleapis.com/Book"
10+
pattern: "publishers/{publisher}/books/{book}"
11+
};
12+
13+
// The resource path for the book.
14+
string path = 1 [(google.api.field_behavior) = IDENTIFIER];
15+
16+
// The resource name for the book's author.
17+
string author = 2 [(google.api.resource_reference) = {
18+
type: "library.googleapis.com/Author"
19+
}];
20+
}

0 commit comments

Comments
 (0)