Repository: googleapis/api-linter
Pull request #222 (2019-10-10 19:04:12)

[AIP-203] Run the input_only rule only if no annotation.


Patch:
diff --git a/rules/aip0203/input_only.go b/rules/aip0203/input_only.go
index 8c891f4..e982b7c 100644
--- a/rules/aip0203/input_only.go
+++ b/rules/aip0203/input_only.go
@@ -4,7 +4,9 @@ import (
 	"regexp"
 
 	"github.com/googleapis/api-linter/lint"
+	"github.com/googleapis/api-linter/rules/internal/utils"
 	"github.com/jhump/protoreflect/desc"
+	"google.golang.org/genproto/googleapis/api/annotations"
 )
 
 // This rule inspects the leading comments of each field
@@ -34,11 +36,21 @@ import (
 //   string secret = 1 [(google.api.field_behavior) = INPUT_ONLY];
 // }
 var inputOnly = &lint.FieldRule{
-	Name: lint.NewRuleName("core", "0203", "input-only"),
-	URI:  "http://api.dev/203#guidance",
+	Name:   lint.NewRuleName("core", "0203", "input-only"),
+	URI:    "http://api.dev/203#guidance",
+	OnlyIf: withoutInputOnlyFieldBehavior,
 	LintField: func(f *desc.FieldDescriptor) []lint.Problem {
 		return checkLeadingComments(f, inputOnlyRegexp, "INPUT_ONLY")
 	},
 }
 
 var inputOnlyRegexp = regexp.MustCompile("(?i).*input.?only.*")
+
+func withoutInputOnlyFieldBehavior(f *desc.FieldDescriptor) bool {
+	for _, v := range utils.GetFieldBehavior(f) {
+		if v == annotations.FieldBehavior_INPUT_ONLY {
+			return false
+		}
+	}
+	return true
+}


Test patch:
diff --git a/rules/aip0203/input_only_test.go b/rules/aip0203/input_only_test.go
index f4f08f5..331caeb 100644
--- a/rules/aip0203/input_only_test.go
+++ b/rules/aip0203/input_only_test.go
@@ -8,76 +8,93 @@ import (
 
 func TestInputOnly(t *testing.T) {
 	testCases := []struct {
-		name       string
-		annotation string
-		problems   testutils.Problems
+		name     string
+		comment  string
+		field    string
+		problems testutils.Problems
 	}{
 		{
-			name:       "input_only",
-			annotation: "input_only",
+			name:     "valid case with INPUT_ONLY field behavior annotation",
+			comment:  "input_only",
+			field:    "string secret = 1 [(google.api.field_behavior) = INPUT_ONLY];",
+			problems: nil,
+		},
+		{
+			name:    "input_only",
+			comment: "input_only",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "inputonly",
-			annotation: "inputonly",
+			name:    "inputonly",
+			comment: "inputonly",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "@inputonly",
-			annotation: "@inputonly",
+			name:    "@inputonly",
+			comment: "@inputonly",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "@input_only",
-			annotation: "@input_only",
+			name:    "@input_only",
+			comment: "@input_only",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "INPUT_ONLY",
-			annotation: "INPUT_ONLY",
+			name:    "INPUT_ONLY",
+			comment: "INPUT_ONLY",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "inputOnly",
-			annotation: "inputOnly",
+			name:    "inputOnly",
+			comment: "inputOnly",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "@inputOnly",
-			annotation: "@inputonly",
+			name:    "@inputOnly",
+			comment: "@inputonly",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "@INPUT_ONLY",
-			annotation: "@INPUT_ONLY",
+			name:    "@INPUT_ONLY",
+			comment: "@INPUT_ONLY",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "input_only_free_text",
-			annotation: "This field is input only",
+			name:    "input_only_free_text",
+			comment: "This field is input only",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
 		},
 		{
-			name:       "!inputOnly",
-			annotation: "!inputOnly",
+			name:    "!inputOnly",
+			comment: "!inputOnly",
+			field:   "string secret = 1;",
 			problems: testutils.Problems{{
 				Message: "google.api.field_behavior",
 			}},
@@ -87,15 +104,20 @@ func TestInputOnly(t *testing.T) {
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			template := `
+				import "google/api/field_behavior.proto";
+
 				message Book {
 					// Secrets to be stored in the book
-					// {{.Annotation}}
-					string secrect = 1;
+					// {{.Comment}}
+					{{.Field}}
 				}
 				`
-			file := testutils.ParseProto3Tmpl(t, template, struct{ Annotation string }{test.annotation})
+			file := testutils.ParseProto3Tmpl(t, template, struct {
+				Comment string
+				Field   string
+			}{test.comment, test.field})
 			f := file.GetMessageTypes()[0].GetFields()[0]
-			problems := inputOnly.LintField(f)
+			problems := inputOnly.Lint(file)
 			if diff := test.problems.SetDescriptor(f).Diff(problems); diff != "" {
 				t.Errorf(diff)
 			}