Skip to content

Commit

Permalink
fix(AIP-135): allow etag & force in signature (#1197)
Browse files Browse the repository at this point in the history
Allows `etag` and `force` in the Standard Delete `google.api.method_signature`. If there is a field that doesn't belong, it will recommend a `method_signature` that includes `etag` and/or `force` if they are present in the request message, but otherwise, will ensure that the first `method_signature` contains exclusively fields from the set `name` `etag` and `force`, as well as enforce that it always includes `name`.

Fixes #1140
  • Loading branch information
noahdietz authored Jul 14, 2023
1 parent 183ff95 commit 9e002e6
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 10 deletions.
27 changes: 21 additions & 6 deletions rules/aip0135/method_signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ package aip0135

import (
"fmt"
"reflect"
"strings"

"bitbucket.org/creachadair/stringset"
"github.com/googleapis/api-linter/lint"
"github.com/googleapis/api-linter/locations"
"github.com/googleapis/api-linter/rules/internal/utils"
Expand All @@ -29,27 +30,41 @@ var methodSignature = &lint.MethodRule{
OnlyIf: isDeleteMethod,
LintMethod: func(m *desc.MethodDescriptor) []lint.Problem {
signatures := utils.GetMethodSignatures(m)
in := m.GetInputType()

fields := []string{"name"}
if etag := in.FindFieldByName("etag"); etag != nil {
fields = append(fields, etag.GetName())
}
if force := in.FindFieldByName("force"); force != nil {
fields = append(fields, force.GetName())
}
want := strings.Join(fields, ",")

// Check if the signature is missing.
if len(signatures) == 0 {
return []lint.Problem{{
Message: fmt.Sprintf(
"Delete methods should include `(google.api.method_signature) = %q`",
"name",
want,
),
Descriptor: m,
}}
}

// Check if the signature is wrong.
if !reflect.DeepEqual(signatures[0], []string{"name"}) {
// Check if the signature contains a disallowed field or doesn't contain
// "name".
first := signatures[0]
fieldSet := stringset.New(fields...)
if !fieldSet.Contains(first...) || !stringset.New(first...).Contains("name") {
return []lint.Problem{{
Message: `The method signature for Delete methods should be "name".`,
Suggestion: `option (google.api.method_signature) = "name";`,
Message: fmt.Sprintf("The method signature for Delete methods should be %q.", want),
Suggestion: fmt.Sprintf("option (google.api.method_signature) = %q;", want),
Descriptor: m,
Location: locations.MethodSignature(m, 0),
}}
}

return nil
},
}
18 changes: 14 additions & 4 deletions rules/aip0135/method_signature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,24 @@ func TestMethodSignature(t *testing.T) {
name string
MethodName string
Signature string
Etag string
Force string
problems testutils.Problems
}{
{"Valid", "DeleteBook", `option (google.api.method_signature) = "name";`, testutils.Problems{}},
{"Missing", "DeleteBook", "", testutils.Problems{{Message: `(google.api.method_signature) = "name"`}}},
{"Valid", "DeleteBook", `option (google.api.method_signature) = "name";`, "", "", testutils.Problems{}},
{"Missing", "DeleteBook", "", "", "", testutils.Problems{{Message: `(google.api.method_signature) = "name"`}}},
{
"Wrong",
"DeleteBook",
`option (google.api.method_signature) = "book";`,
"", "",
testutils.Problems{{Suggestion: `option (google.api.method_signature) = "name";`}},
},
{"Irrelevant", "BurnBook", "", testutils.Problems{}},
{"Irrelevant", "RemoveBook", "", "", "", testutils.Problems{}},
{"WithEtag", "DeleteBook", `option (google.api.method_signature) = "name,etag";`, "string etag = 2;", "", testutils.Problems{}},
{"WithForce", "DeleteBook", `option (google.api.method_signature) = "name,force";`, "", "bool force = 3;", testutils.Problems{}},
{"WithBoth", "DeleteBook", `option (google.api.method_signature) = "name,etag,force";`, "string etag = 2;", "bool force = 3;", testutils.Problems{}},
{"MissingNameWithBoth", "DeleteBook", `option (google.api.method_signature) = "etag,force";`, "string etag = 2;", "bool force = 3;", testutils.Problems{{Suggestion: `option (google.api.method_signature) = "name,etag,force";`}}},
} {
t.Run(test.name, func(t *testing.T) {
f := testutils.ParseProto3Tmpl(t, `
Expand All @@ -46,7 +53,10 @@ func TestMethodSignature(t *testing.T) {
{{.Signature}}
}
}
message {{.MethodName}}Request {}
message {{.MethodName}}Request {
{{.Etag}}
{{.Force}}
}
`, test)
m := f.GetServices()[0].GetMethods()[0]
if diff := test.problems.SetDescriptor(m).Diff(methodSignature.Lint(f)); diff != "" {
Expand Down

0 comments on commit 9e002e6

Please sign in to comment.