diff --git a/docs/rules/0162/delete-revision-request-name-behavior.md b/docs/rules/0162/delete-revision-request-name-behavior.md new file mode 100644 index 000000000..1f7210db3 --- /dev/null +++ b/docs/rules/0162/delete-revision-request-name-behavior.md @@ -0,0 +1,69 @@ +--- +rule: + aip: 162 + name: [core, '0162', delete-revision-request-name-behavior] + summary: | + Delete Revision requests should annotate the `name` field with `google.api.field_behavior`. +permalink: /162/delete-revision-request-name-behavior +redirect_from: + - /0162/delete-revision-request-name-behavior +--- + +# Delete Revision requests: Name field behavior + +This rule enforces that all Delete Revision requests have +`google.api.field_behavior` set to `REQUIRED` on their `string name` field, as +mandated in [AIP-162][]. + +## Details + +This rule looks at any message matching `Delete*RevisionRequest` and complains if the +`name` field does not have a `google.api.field_behavior` annotation with a +value of `REQUIRED`. + +## Examples + +**Incorrect** code for this rule: + +```proto +// Incorrect. +message DeleteBookRevisionRequest { + // The `google.api.field_behavior` annotation should also be included. + string name = 1 [ + (google.api.resource_reference).type = "library.googleapis.com/Book" + ]; +} +``` + +**Correct** code for this rule: + +```proto +// Correct. +message DeleteBookRevisionRequest { + string name = 1 [ + (google.api.field_behavior) = REQUIRED, + (google.api.resource_reference).type = "library.googleapis.com/Book" + ]; +} +``` + +## Disabling + +If you need to violate this rule, use a leading comment above the field. +Remember to also include an [aip.dev/not-precedent][] comment explaining why. + +```proto +message DeleteBookRevisionRequest { + // (-- api-linter: core::0162::delete-revision-request-name-behavior=disabled + // aip.dev/not-precedent: We need to do this because reasons. --) + string name = 1 [ + (google.api.resource_reference).type = "library.googleapis.com/Book" + ]; +} +``` + +If you need to violate this rule for an entire file, place the comment at the +top of the file. + +[aip-162]: https://aip.dev/162 +[aip.dev/not-precedent]: https://aip.dev/not-precedent diff --git a/rules/aip0162/aip0162.go b/rules/aip0162/aip0162.go index 6e6c8f17d..09f4eeed2 100644 --- a/rules/aip0162/aip0162.go +++ b/rules/aip0162/aip0162.go @@ -39,6 +39,7 @@ func AddRules(r lint.RuleRegistry) error { deleteRevisionHTTPMethod, deleteRevisionHTTPURISuffix, deleteRevisionRequestMessageName, + deleteRevisionRequestNameBehavior, deleteRevisionRequestNameField, rollbackHTTPBody, rollbackHTTPMethod, diff --git a/rules/aip0162/delete_revision_request_name_behavior.go b/rules/aip0162/delete_revision_request_name_behavior.go new file mode 100644 index 000000000..184305ee9 --- /dev/null +++ b/rules/aip0162/delete_revision_request_name_behavior.go @@ -0,0 +1,29 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aip0162 + +import ( + "github.com/googleapis/api-linter/lint" + "github.com/googleapis/api-linter/rules/internal/utils" + "github.com/jhump/protoreflect/desc" +) + +var deleteRevisionRequestNameBehavior = &lint.FieldRule{ + Name: lint.NewRuleName(162, "delete-revision-request-name-behavior"), + OnlyIf: func(f *desc.FieldDescriptor) bool { + return isDeleteRevisionRequestMessage(f.GetOwner()) && f.GetName() == "name" + }, + LintField: utils.LintRequiredField, +} diff --git a/rules/aip0162/delete_revision_request_name_behavior_test.go b/rules/aip0162/delete_revision_request_name_behavior_test.go new file mode 100644 index 000000000..30471ca67 --- /dev/null +++ b/rules/aip0162/delete_revision_request_name_behavior_test.go @@ -0,0 +1,49 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package aip0162 + +import ( + "testing" + + "github.com/googleapis/api-linter/rules/internal/testutils" +) + +func TestDeleteRevisionRequestNameBehavior(t *testing.T) { + for _, test := range []struct { + name string + RPC string + Field string + FieldOpts string + problems testutils.Problems + }{ + {"Valid", "DeleteBookRevision", "name", " [(google.api.field_behavior) = REQUIRED]", nil}, + {"Missing", "DeleteBookRevision", "name", "", testutils.Problems{{Message: "(google.api.field_behavior) = REQUIRED"}}}, + {"IrrelevantMessage", "PurgeBooks", "name", "", nil}, + {"IrrelevantField", "DeleteBookRevision", "something_else", "", nil}, + } { + t.Run(test.name, func(t *testing.T) { + f := testutils.ParseProto3Tmpl(t, ` + import "google/api/field_behavior.proto"; + message {{.RPC}}Request { + string {{.Field}} = 1{{.FieldOpts}}; + } + `, test) + field := f.GetMessageTypes()[0].GetFields()[0] + if diff := test.problems.SetDescriptor(field).Diff(deleteRevisionRequestNameBehavior.Lint(f)); diff != "" { + t.Errorf(diff) + } + }) + } +}