Repository: googleapis/api-linter
Pull request #1252 (2023-09-12 16:05:38)

fix(AIP-121): ignore streaming lookalikes
When checking for missing standard methods, ignore streaming methods as they can never be standard methods. Based on https://github.com/googleapis/api-linter/pull/1250#discussion_r1322469624.


Patch:
diff --git a/rules/aip0121/resource_must_support_get.go b/rules/aip0121/resource_must_support_get.go
index 7dc66c3..10fb276 100644
--- a/rules/aip0121/resource_must_support_get.go
+++ b/rules/aip0121/resource_must_support_get.go
@@ -33,6 +33,12 @@ var resourceMustSupportGet = &lint.ServiceRule{
 		// Iterate all RPCs and try to find resources. Mark the
 		// resources which have a Get method, and which ones do not.
 		for _, m := range s.GetMethods() {
+			// Streaming methods do not count as standard methods even if they
+			// look like them.
+			if utils.IsStreaming(m) {
+				continue
+			}
+
 			if utils.IsGetMethod(m) && utils.IsResource(utils.GetResponseType(m)) {
 				t := utils.GetResource(m.GetOutputType()).GetType()
 				resourcesWithGet.Add(t)
diff --git a/rules/aip0121/resource_must_support_list.go b/rules/aip0121/resource_must_support_list.go
index 6e0f962..96ed0fd 100644
--- a/rules/aip0121/resource_must_support_list.go
+++ b/rules/aip0121/resource_must_support_list.go
@@ -33,6 +33,12 @@ var resourceMustSupportList = &lint.ServiceRule{
 		// Iterate all RPCs and try to find resources. Mark the
 		// resources which have a List method, and which ones do not.
 		for _, m := range s.GetMethods() {
+			// Streaming methods do not count as standard methods even if they
+			// look like them.
+			if utils.IsStreaming(m) {
+				continue
+			}
+
 			if utils.IsListMethod(m) {
 				if msg := utils.GetListResourceMessage(m); msg != nil && utils.IsResource(msg) {
 					t := utils.GetResource(msg).GetType()
diff --git a/rules/internal/utils/method.go b/rules/internal/utils/method.go
index e5531e8..8eaca4b 100644
--- a/rules/internal/utils/method.go
+++ b/rules/internal/utils/method.go
@@ -75,3 +75,8 @@ func GetListResourceMessage(m *desc.MethodDescriptor) *desc.MessageDescriptor {
 	}
 	return nil
 }
+
+// IsStreaming returns if the method is either client or server streaming.
+func IsStreaming(m *desc.MethodDescriptor) bool {
+	return m.IsClientStreaming() || m.IsServerStreaming()
+}


Test patch:
diff --git a/rules/aip0121/resource_must_support_get_test.go b/rules/aip0121/resource_must_support_get_test.go
index 07af817..4a1aeb8 100644
--- a/rules/aip0121/resource_must_support_get_test.go
+++ b/rules/aip0121/resource_must_support_get_test.go
@@ -60,6 +60,9 @@ func TestResourceMustSupportGet(t *testing.T) {
 		{"ValidIgnoreNonResourceList", `
 			rpc ListBooks(ListBooksRequest) returns (RepeatedOther) {};
 		`, nil},
+		{"ValidIgnoreStreamingLookalike", `
+			rpc CreateBook(CreateBookRequest) returns (stream Book) {};
+		`, nil},
 		{"InvalidCreateOnly", `
 			rpc CreateBook(CreateBookRequest) returns (Book) {};
 		`, []lint.Problem{
diff --git a/rules/aip0121/resource_must_support_list_test.go b/rules/aip0121/resource_must_support_list_test.go
index 5f22c7e..7232cea 100644
--- a/rules/aip0121/resource_must_support_list_test.go
+++ b/rules/aip0121/resource_must_support_list_test.go
@@ -81,6 +81,9 @@ func TestResourceMustSupportList(t *testing.T) {
 		{"ValidIgnoreNonResource", `
 			rpc GetBookCover(GetBookCoverRequest) returns (Other) {};
 		`, nil},
+		{"ValidIgnoreStreamingLookalike", `
+			rpc GetBook(GetBookRequest) returns (stream Book) {};
+		`, nil},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			file := testutils.ParseProto3Tmpl(t, `