Repository: uber/prototool
Pull request #390 (2019-03-11 22:03:18)

Add non-default RPCS_NO_STREAMING linter
This isn't included in any lint group by default, but will be available to add if someone wants it.

peter-edge: Another failure based on https://github.com/golang/go/issues/30690. Merging anyways.

Patch:
diff --git a/internal/lint/BUILD.bazel b/internal/lint/BUILD.bazel
index c50effe..4e2da2e 100644
--- a/internal/lint/BUILD.bazel
+++ b/internal/lint/BUILD.bazel
@@ -62,6 +62,7 @@ go_library(
         "check_rpc_options_no_google_api_http.go",
         "check_rpcs_have_comments.go",
         "check_rpcs_have_sentence_comments.go",
+        "check_rpcs_no_streaming.go",
         "check_service_names_api_suffix.go",
         "check_service_names_camel_case.go",
         "check_service_names_capitalized.go",
diff --git a/internal/lint/check_rpcs_no_streaming.go b/internal/lint/check_rpcs_no_streaming.go
new file mode 100644
index 0000000..ddac74c
--- /dev/null
+++ b/internal/lint/check_rpcs_no_streaming.go
@@ -0,0 +1,52 @@
+// Copyright (c) 2019 Uber Technologies, Inc.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+package lint
+
+import (
+	"github.com/emicklei/proto"
+	"github.com/uber/prototool/internal/text"
+)
+
+var rpcsNoStreamingLinter = NewLinter(
+	"RPCS_NO_STREAMING",
+	`Verifies that all rpcs are unary.`,
+	checkRPCsNoStreaming,
+)
+
+func checkRPCsNoStreaming(add func(*text.Failure), dirPath string, descriptors []*FileDescriptor) error {
+	return runVisitor(rpcsNoStreamingVisitor{baseAddVisitor: newBaseAddVisitor(add)}, descriptors)
+}
+
+type rpcsNoStreamingVisitor struct {
+	baseAddVisitor
+}
+
+func (v rpcsNoStreamingVisitor) VisitService(service *proto.Service) {
+	for _, child := range service.Elements {
+		child.Accept(v)
+	}
+}
+
+func (v rpcsNoStreamingVisitor) VisitRPC(rpc *proto.RPC) {
+	if rpc.StreamsRequest || rpc.StreamsReturns {
+		v.AddFailuref(rpc.Position, `RPC %q is streaming and only unary RPCs are allowed.`, rpc.Name)
+	}
+}
diff --git a/internal/lint/lint.go b/internal/lint/lint.go
index d13c4ce..19fb83f 100644
--- a/internal/lint/lint.go
+++ b/internal/lint/lint.go
@@ -109,6 +109,7 @@ var (
 		packagesSameInDirLinter,
 		rpcsHaveCommentsLinter,
 		rpcsHaveSentenceCommentsLinter,
+		rpcsNoStreamingLinter,
 		rpcNamesCamelCaseLinter,
 		rpcNamesCapitalizedLinter,
 		rpcOptionsNoGoogleAPIHTTPLinter,


Test patch:
diff --git a/internal/cmd/cmd_test.go b/internal/cmd/cmd_test.go
index 9ae16bb..8ba7148 100644
--- a/internal/cmd/cmd_test.go
+++ b/internal/cmd/cmd_test.go
@@ -555,6 +555,15 @@ func TestLint(t *testing.T) {
 		"testdata/lint/nogoogleapihttp/foo/v1/hello_api.proto",
 	)
 
+	assertDoLintFile(
+		t,
+		false,
+		`15:3:RPCS_NO_STREAMING
+		16:3:RPCS_NO_STREAMING
+		17:3:RPCS_NO_STREAMING`,
+		"testdata/lint/nostreaming/foo/v1/hello_api.proto",
+	)
+
 	assertDoLintFile(
 		t,
 		false,
diff --git a/internal/cmd/testdata/lint/nostreaming/foo/v1/hello_api.proto b/internal/cmd/testdata/lint/nostreaming/foo/v1/hello_api.proto
new file mode 100644
index 0000000..331c8a9
--- /dev/null
+++ b/internal/cmd/testdata/lint/nostreaming/foo/v1/hello_api.proto
@@ -0,0 +1,34 @@
+syntax = "proto3";
+
+package foo.v1;
+
+option csharp_namespace = "Foo.V1";
+option go_package = "foov1";
+option java_multiple_files = true;
+option java_outer_classname = "HelloApiProto";
+option java_package = "com.foo.v1";
+option objc_class_prefix = "FXX";
+option php_namespace = "Foo\\V1";
+
+service HelloAPI {
+  rpc SendFoo(SendFooRequest) returns (SendFooResponse);
+  rpc SendBar(stream SendBarRequest) returns (SendBarResponse);
+  rpc SendBaz(SendBazRequest) returns (stream SendBazResponse);
+  rpc SendBat(stream SendBatRequest) returns (stream SendBatResponse);
+}
+
+message SendFooRequest {}
+
+message SendFooResponse {}
+
+message SendBarRequest {}
+
+message SendBarResponse {}
+
+message SendBazRequest {}
+
+message SendBazResponse {}
+
+message SendBatRequest {}
+
+message SendBatResponse {}
diff --git a/internal/cmd/testdata/lint/nostreaming/prototool.yaml b/internal/cmd/testdata/lint/nostreaming/prototool.yaml
new file mode 100644
index 0000000..60b2a3c
--- /dev/null
+++ b/internal/cmd/testdata/lint/nostreaming/prototool.yaml
@@ -0,0 +1,10 @@
+lint:
+  group: uber2
+  rules:
+    add:
+      - RPCS_NO_STREAMING
+    remove:
+      - ENUMS_HAVE_SENTENCE_COMMENTS
+      - MESSAGES_HAVE_SENTENCE_COMMENTS_EXCEPT_REQUEST_RESPONSE_TYPES
+      - RPCS_HAVE_SENTENCE_COMMENTS
+      - SERVICES_HAVE_SENTENCE_COMMENTS