Skip to content

Commit

Permalink
fix(AIP-203): lint non-maps and same package (#1175)
Browse files Browse the repository at this point in the history
Although all request messages **must** be annotated, this linter only verifies
messages that are in the same package as some upstream protos (e.g. common
protos) may be difficult to modify.
  • Loading branch information
toumorokoshi authored Jun 7, 2023
1 parent ca24295 commit 4e9eb29
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 10 deletions.
4 changes: 4 additions & 0 deletions docs/rules/0203/field-behavior-required.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ This rule looks at all fields and ensures they have a
one of the options `OUTPUT_ONLY`, `REQUIRED`, or `OPTIONAL`: all fields must
fall into one of these categories.

Although all request messages **must** be annotated, this linter only verifies
messages that are in the same package as some upstream protos (e.g. common
protos) may be difficult to modify.

## Examples

**Incorrect** code for this rule:
Expand Down
9 changes: 5 additions & 4 deletions rules/aip0203/field_behavior_required.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ var fieldBehaviorRequired = &lint.MethodRule{
Name: lint.NewRuleName(203, "field-behavior-required"),
LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
req := m.GetInputType()
ps := problems(req)
p := m.GetFile().GetPackage()
ps := problems(req, p)
if len(ps) == 0 {
return nil
}
Expand All @@ -40,7 +41,7 @@ var fieldBehaviorRequired = &lint.MethodRule{
},
}

func problems(m *desc.MessageDescriptor) []lint.Problem {
func problems(m *desc.MessageDescriptor, pkg string) []lint.Problem {
var ps []lint.Problem

for _, f := range m.GetFields() {
Expand All @@ -53,8 +54,8 @@ func problems(m *desc.MessageDescriptor) []lint.Problem {
ps = append(ps, *p)
}

if mt := f.GetMessageType(); mt != nil {
ps = append(ps, problems(mt)...)
if mt := f.GetMessageType(); mt != nil && !mt.IsMapEntry() && mt.GetFile().GetPackage() == pkg {
ps = append(ps, problems(mt, pkg)...)
}
}

Expand Down
42 changes: 36 additions & 6 deletions rules/aip0203/field_behavior_required_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ func TestFieldBehaviorRequired_SingleFile_SingleMessage(t *testing.T) {
"int32 page_count = 1 [(google.api.field_behavior) = OPTIONAL];",
nil,
},
// Maps should not be recursed to MapEntries, as they have no field
// behavior.
{
"ValidMap",
"map<string, string> page_count = 1 [(google.api.field_behavior) = OPTIONAL];",
nil,
},
{
"ValidOutputOnly",
"int32 page_count = 1 [(google.api.field_behavior) = OUTPUT_ONLY];",
Expand All @@ -64,6 +71,8 @@ func TestFieldBehaviorRequired_SingleFile_SingleMessage(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
f := testutils.ParseProto3Tmpl(t, `
package apilinter.test.field_behavior_required;
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
Expand Down Expand Up @@ -93,7 +102,6 @@ func TestFieldBehaviorRequired_SingleFile_SingleMessage(t *testing.T) {
string name = 1;
}
`, tc)

field := f.GetMessageTypes()[0].GetFields()[0]

if diff := tc.problems.SetDescriptor(field).Diff(fieldBehaviorRequired.Lint(f)); diff != "" {
Expand Down Expand Up @@ -226,6 +234,12 @@ func TestFieldBehaviorRequired_NestedMessages_MultipleFile(t *testing.T) {
"annotated",
nil,
},
{
"ValidAnnotatedAndChildInOtherPackageUnannotated",
"unannotated.NonAnnotated",
"non_annotated",
nil,
},
{
"InvalidChildNotAnnotated",
"NonAnnotated",
Expand All @@ -236,8 +250,11 @@ func TestFieldBehaviorRequired_NestedMessages_MultipleFile(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
f1 := `
package apilinter.test.field_behavior_required;
import "google/api/field_behavior.proto";
import "resource.proto";
import "unannotated.proto";
service Library {
rpc UpdateBook(UpdateBookRequest) returns (UpdateBookResponse) {
Expand All @@ -256,6 +273,8 @@ func TestFieldBehaviorRequired_NestedMessages_MultipleFile(t *testing.T) {
`

f2 := `
package apilinter.test.field_behavior_required;
import "google/api/field_behavior.proto";
message NonAnnotated {
Expand All @@ -267,9 +286,18 @@ func TestFieldBehaviorRequired_NestedMessages_MultipleFile(t *testing.T) {
}
`

f3 := `
package apilinter.test.unannotated;
message NonAnnotated {
string nested = 1;
}
`

srcs := map[string]string{
"service.proto": f1,
"resource.proto": f2,
"service.proto": f1,
"resource.proto": f2,
"unannotated.proto": f3,
}

ds := testutils.ParseProto3Tmpls(t, srcs, tc)
Expand All @@ -281,9 +309,11 @@ func TestFieldBehaviorRequired_NestedMessages_MultipleFile(t *testing.T) {
t.Errorf(diff)
}

want := "resource.proto"
if got := fd.GetFile().GetName(); got != want {
t.Fatalf("got file name %q for location of field but wanted %q", got, want)
if tc.problems != nil {
want := "resource.proto"
if got := fd.GetFile().GetName(); got != want {
t.Fatalf("got file name %q for location of field but wanted %q", got, want)
}
}
})
}
Expand Down

0 comments on commit 4e9eb29

Please sign in to comment.