Skip to content

Commit 6710c15

Browse files
authored
132/133 should use resource_reference_child_type (#114)
1 parent 525ea24 commit 6710c15

15 files changed

+141
-93
lines changed

docs/rules/0132/resource-reference-type.md

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
rule:
33
aep: 132
44
name: [core, '0132', resource-reference-type]
5-
summary: List should use a `child_type` reference to the paginated resource.
5+
summary: List should use `resource_reference_child_type` to reference the paginated resource.
66
permalink: /132/resource-reference-type
77
redirect_from:
88
- /0132/resource-reference-type
@@ -11,15 +11,15 @@ redirect_from:
1111
# List methods: Parent field resource reference
1212

1313
This rule enforces that all `List` standard methods with a `string parent`
14-
field use a proper `(aep.api.field_info).resource_reference`, that being either a
15-
`child_type` referring to the pagianted resource or a `type` referring directly
16-
to the parent resource, as mandated in [AEP-132][].
14+
field use a proper `(aep.api.field_info).resource_reference_child_type` to refer to the
15+
paginated resource, as mandated in [AEP-132][].
1716

1817
## Details
1918

20-
This rule looks at any message matching `List*Request` and complains if the
21-
`(aep.api.field_info).resource_reference` on the `parent` field refers to the wrong
22-
resource.
19+
This rule looks at any message matching `List*Request` and complains if the
20+
`(aep.api.field_info).resource_reference_child_type` or `(aep.api.field_info).resource_reference`
21+
on the `parent` field refers to the wrong resource. The preferred approach is to use
22+
`resource_reference_child_type` to reference the child resource being paginated.
2323

2424
## Examples
2525

@@ -28,9 +28,8 @@ resource.
2828
```proto
2929
// Incorrect.
3030
message ListBooksRequest {
31-
// `child_type` should be used instead of `type` when referring to the
32-
// paginated resource on a parent field.
33-
string parent = 1 [(aep.api.field_info).resource_reference.type = "library.googleapis.com/Book"];
31+
// Should reference the correct child resource type.
32+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Shelf"];
3433
int32 page_size = 2;
3534
string page_token = 3;
3635
}
@@ -41,7 +40,7 @@ message ListBooksRequest {
4140
```proto
4241
// Correct.
4342
message ListBooksRequest {
44-
string parent = 1 [(aep.api.field_info).resource_reference.child_type = "library.googleapis.com/Book"];
43+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Book"];
4544
int32 page_size = 2;
4645
string page_token = 3;
4746
}
@@ -56,7 +55,7 @@ Remember to also include an [aep.dev/not-precedent][] comment explaining why.
5655
message ListBooksRequest {
5756
// (-- api-linter: core::0132::resource-reference-type=disabled
5857
// aep.dev/not-precedent: We need to do this because reasons. --)
59-
string parent = 1 [(aep.api.field_info).resource_reference.type = "library.googleapis.com/Book"];
58+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Shelf"];
6059
int32 page_size = 2;
6160
string page_token = 3;
6261
}

docs/rules/0133/resource-reference-type.md

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
rule:
33
aep: 133
44
name: [core, '0133', resource-reference-type]
5-
summary: Create should use a `child_type` reference to the created resource.
5+
summary:
6+
Create should use `resource_reference_child_type` to reference the created
7+
resource.
68
permalink: /133/resource-reference-type
79
redirect_from:
810
- /0133/resource-reference-type
@@ -11,15 +13,17 @@ redirect_from:
1113
# Create methods: Parent field resource reference
1214

1315
This rule enforces that all `Create` standard methods with a `string parent`
14-
field use a proper `(aep.api.field_info).resource_reference`, that being either a
15-
`child_type` referring to the created resource or a `type` referring directly
16-
to the parent resource, as mandated in [AEP-133][].
16+
field use a proper `(aep.api.field_info).resource_reference_child_type` or
17+
`(aep.api.field_info.).resource_reference` to refer to the created resource, as
18+
mandated in [AEP-133][].
1719

1820
## Details
1921

20-
This rule looks at any message matching `Create*Request` and complains if the
21-
`(aep.api.field_info).resource_reference` on the `parent` field refers to the wrong
22-
resource.
22+
This rule looks at any message matching `Create*Request` and complains if the
23+
`(aep.api.field_info).resource_reference_child_type` or
24+
`(aep.api.field_info).resource_reference` on the `parent` field refers to the
25+
wrong resource. The preferred approach is to use
26+
`resource_reference_child_type` to reference the child resource being created.
2327

2428
## Examples
2529

@@ -28,9 +32,8 @@ resource.
2832
```proto
2933
// Incorrect.
3034
message CreateBooksRequest {
31-
// `child_type` should be used instead of `type` when referring to the
32-
// created resource on a parent field.
33-
string parent = 1 [(aep.api.field_info).resource_reference.type = "library.googleapis.com/Book"];
35+
// Should reference the correct child resource type.
36+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Shelf"];
3437
Book book = 2;
3538
}
3639
```
@@ -40,7 +43,7 @@ message CreateBooksRequest {
4043
```proto
4144
// Correct.
4245
message CreateBooksRequest {
43-
string parent = 1 [(aep.api.field_info).resource_reference.child_type = "library.googleapis.com/Book"];
46+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Book"];
4447
Book book = 2;
4548
}
4649
```
@@ -54,7 +57,7 @@ Remember to also include an [aep.dev/not-precedent][] comment explaining why.
5457
message CreateBooksRequest {
5558
// (-- api-linter: core::0133::resource-reference-type=disabled
5659
// aep.dev/not-precedent: We need to do this because reasons. --)
57-
string parent = 1 [(aep.api.field_info).resource_reference.type = "library.googleapis.com/Book"];
60+
string parent = 1 [(aep.api.field_info).resource_reference_child_type = "library.googleapis.com/Shelf"];
5861
Book book = 2;
5962
}
6063
```

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.24.0
44

55
require (
66
bitbucket.org/creachadair/stringset v0.0.14
7-
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251016045117-f9844266f27f.1
7+
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251102152130-5f3e69139afa.1
88
buf.build/go/bufplugin v0.9.0
99
cloud.google.com/go/longrunning v0.7.0
1010
github.com/bmatcuk/doublestar/v4 v4.9.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ bitbucket.org/creachadair/stringset v0.0.14 h1:t1ejQyf8utS4GZV/4fM+1gvYucggZkfhb
22
bitbucket.org/creachadair/stringset v0.0.14/go.mod h1:Ej8fsr6rQvmeMDf6CCWMWGb14H9mz8kmDgPPTdiVT0w=
33
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251016045117-f9844266f27f.1 h1:jK64CGldQTGX2ESi+v41uERp+z28OFqGyAn/T/Qs8Q0=
44
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251016045117-f9844266f27f.1/go.mod h1:JOZpZ+zS3G2OKO02hKlEazAGR5X9uVpFyeCamXBXg5c=
5+
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251102152130-5f3e69139afa.1 h1:SEKgDODwWdsVL9nnOPkFBlZ9wAuO7kRQNobSZb5JR7I=
6+
buf.build/gen/go/aep/api/protocolbuffers/go v1.36.10-20251102152130-5f3e69139afa.1/go.mod h1:JOZpZ+zS3G2OKO02hKlEazAGR5X9uVpFyeCamXBXg5c=
57
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.10-20250718181942-e35f9b667443.1 h1:FzJGrb8r7vir+P3zJ5Ebey8p54LYTYtQsrM/U35YO9Q=
68
buf.build/gen/go/bufbuild/bufplugin/protocolbuffers/go v1.36.10-20250718181942-e35f9b667443.1/go.mod h1:E6HwqUm4Ag7bXtg/tX7jHWO7CgpknbmeACgDax0icV0=
79
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.10-20250912141014-52f32327d4b0.1 h1:31on4W/yPcV4nZHL4+UCiCvLPsMqe/vJcNg8Rci0scc=

rules/aep0121/no_mutable_cycles.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,23 @@ func findCycles(start string, node *desc.MessageDescriptor, seen stringset.Set,
4747
}
4848
ref := utils.GetResourceReference(f)
4949
// Skip indirect references for now.
50-
if ref.GetChildType() != "" {
50+
if len(ref.GetChildType()) > 0 {
5151
continue
5252
}
53-
if ref.GetType() == start {
53+
types := ref.GetType()
54+
if len(types) == 0 {
55+
continue
56+
}
57+
refType := types[0]
58+
if refType == start {
5459
cycle := strings.Join(append(chain, start), " > ")
5560
problems = append(problems, lint.Problem{
5661
Message: "mutable resource reference introduces a reference cycle:\n" + cycle,
5762
Descriptor: f,
5863
Location: locations.FieldResourceReference(f),
5964
})
60-
} else if !seen.Contains(ref.GetType()) {
61-
next := utils.FindResourceMessage(ref.GetType(), node.GetFile())
65+
} else if !seen.Contains(refType) {
66+
next := utils.FindResourceMessage(refType, node.GetFile())
6267
// Skip unresolvable references.
6368
if next == nil {
6469
continue

rules/aep0127/http_template_pattern.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,21 @@ func httpResourceReferences(httpRule *utils.HTTPRule, msg *desc.MessageDescripto
6666

6767
// Extract the name of the resource referenced by this field.
6868
ref := utils.GetResourceReference(field)
69-
if ref == nil || ref.GetChildType() != "" {
69+
if ref == nil || len(ref.GetChildType()) > 0 {
7070
// TODO(#1047): Support the case where a resource has
7171
// multiple parent resources.
7272
continue
7373
}
7474

75+
types := ref.GetType()
76+
if len(types) == 0 {
77+
continue
78+
}
79+
7580
resourceRefs = append(resourceRefs, resourceReference{
7681
fieldPath: fieldPath,
7782
pathTemplate: template,
78-
resourceRefName: ref.GetType(),
83+
resourceRefName: types[0],
7984
})
8085
}
8186
return resourceRefs

rules/aep0131/request_path_reference_type.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var requestPathReferenceType = &lint.FieldRule{
2828
return utils.IsGetRequestMessage(f.GetOwner()) && f.GetName() == "path" && utils.GetResourceReference(f) != nil
2929
},
3030
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
31-
if ref := utils.GetResourceReference(f); ref.GetType() == "" {
31+
if ref := utils.GetResourceReference(f); len(ref.GetType()) == 0 || ref.GetType()[0] == "" {
3232
return []lint.Problem{{
3333
Message: fmt.Sprintf("The `%s` field `(aep.api.field_info).resource_reference` annotation should be a direct `type` reference.", f.GetName()),
3434
Descriptor: f,

rules/aep0132/request_parent_valid_reference.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@ var requestParentValidReference = &lint.FieldRule{
2929
RuleType: lint.NewRuleType(lint.MustRule),
3030
OnlyIf: func(f *desc.FieldDescriptor) bool {
3131
ref := utils.GetResourceReference(f)
32-
return utils.IsListRequestMessage(f.GetOwner()) && f.GetName() == "parent" && ref != nil && ref.GetType() != ""
32+
types := ref.GetType()
33+
return utils.IsListRequestMessage(f.GetOwner()) && f.GetName() == "parent" && ref != nil && len(types) > 0
3334
},
3435
LintField: func(f *desc.FieldDescriptor) []lint.Problem {
3536
p := f.GetParent()
3637
msg, _ := p.(*desc.MessageDescriptor)
37-
res := utils.GetResourceReference(f).GetType()
38+
types := utils.GetResourceReference(f).GetType()
39+
res := types[0]
3840

3941
response := utils.FindMessage(f.GetFile(), strings.Replace(msg.GetName(), "Request", "Response", 1))
4042
if response == nil {

rules/aep0132/resource_reference_type.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,26 +45,26 @@ var resourceReferenceType = &lint.MethodRule{
4545
parent := m.GetInputType().FindFieldByName("parent")
4646
ref := utils.GetResourceReference(parent)
4747

48-
// In AEP format, resource_reference is just a string. When used in List methods,
49-
// it should match the child resource type. The old Google API format distinguishes
50-
// between `type` and `child_type`, but AEP format just uses the string value.
51-
// If child_type is set (Google API format), check it. Otherwise, check the type field
52-
// and treat it as an implicit child_type reference.
53-
if ref.GetChildType() != "" {
54-
// Google API format with explicit child_type
55-
if resource.GetType() != ref.GetChildType() {
48+
// Check resource reference matches the child resource type.
49+
// In AEP format, use resource_reference_child_type to reference the child resource.
50+
// For backwards compatibility, resource_reference (type) is also supported.
51+
childTypes := ref.GetChildType()
52+
types := ref.GetType()
53+
54+
if len(childTypes) > 0 {
55+
// AEP format with resource_reference_child_type
56+
if resource.GetType() != childTypes[0] {
5657
return []lint.Problem{{
57-
Message: "List should use a `child_type` reference to the paginated resource.",
58+
Message: "List should use `resource_reference_child_type` to reference the paginated resource.",
5859
Descriptor: parent,
5960
Location: locations.FieldResourceReference(parent),
6061
}}
6162
}
62-
} else if ref.GetType() != "" {
63-
// AEP format or Google API format with only type set
64-
// In AEP format, this should match the child resource type
65-
if resource.GetType() != ref.GetType() {
63+
} else if len(types) > 0 {
64+
// AEP format with resource_reference only (backwards compatibility)
65+
if resource.GetType() != types[0] {
6666
return []lint.Problem{{
67-
Message: "List should use a `child_type` reference to the paginated resource.",
67+
Message: "List should use `resource_reference_child_type` to reference the paginated resource.",
6868
Descriptor: parent,
6969
Location: locations.FieldResourceReference(parent),
7070
}}

rules/aep0132/resource_reference_type_test.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ option (google.api.resource) = {
3030

3131
// Set up testing permutations.
3232
tests := []struct {
33-
testName string
34-
TypeName string
33+
testName string
34+
Annotation string
3535
ResourceAnnotation string
3636
problems testutils.Problems
3737
}{
38-
{"ValidMatch", "library.googleapis.com/Book", bookResource, nil},
39-
{"InvalidMismatch", "library.googleapis.com/Shelf", bookResource, testutils.Problems{{Message: "`child_type`"}}},
40-
{"SkipNoResource", "library.googleapis.com/Book", "", nil},
38+
{"ValidMatch_resource_reference", `resource_reference = "library.googleapis.com/Book"`, bookResource, nil},
39+
{"InvalidMismatch_resource_reference", `resource_reference = "library.googleapis.com/Shelf"`, bookResource, testutils.Problems{{Message: "`resource_reference_child_type`"}}},
40+
{"ValidMatch_resource_reference_child_type", `resource_reference_child_type = "library.googleapis.com/Book"`, bookResource, nil},
41+
{"InvalidMismatch_resource_reference_child_type", `resource_reference_child_type = "library.googleapis.com/Shelf"`, bookResource, testutils.Problems{{Message: "`resource_reference_child_type`"}}},
42+
{"SkipNoResource", `resource_reference = "library.googleapis.com/Book"`, "", nil},
4143
}
4244

4345
// Run each test.
@@ -50,7 +52,7 @@ option (google.api.resource) = {
5052
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {}
5153
}
5254
message ListBooksRequest {
53-
string parent = 1 [(aep.api.field_info).resource_reference = "{{ .TypeName }}"];
55+
string parent = 1 [(aep.api.field_info).{{ .Annotation }}];
5456
}
5557
message ListBooksResponse {
5658
repeated string unreachable = 2;

0 commit comments

Comments
 (0)