Browse Source

Generate client service name and proto service comments (#1059)

Motivation:

It can be useful to know the service path a client is for, particularly
when constructing calls manually.

Additionally, we include the methods comments for RPCs but not the
service comments, they may be helpful in some cases.

Modifications:

- Generate the 'serviceName' for the client
- Generate the proto source comments for the client and server

Result:

- Generated code is a touch more useful
- Resolves #962, #1036
George Barnett 5 years ago
parent
commit
757319c88a

+ 5 - 1
Sources/Examples/Echo/Model/echo.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Echo_EchoClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `Echo_EchoClient`, then call methods of this protocol to make API calls.
 public protocol Echo_EchoClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Echo_EchoClientInterceptorFactoryProtocol? { get }
 
   func get(
@@ -51,6 +52,9 @@ public protocol Echo_EchoClientProtocol: GRPCClient {
 }
 
 extension Echo_EchoClientProtocol {
+  public var serviceName: String {
+    return "echo.Echo"
+  }
 
   /// Immediately returns an echo of a request.
   ///

+ 9 - 1
Sources/Examples/HelloWorld/Model/helloworld.grpc.swift

@@ -25,8 +25,11 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Helloworld_GreeterClient, then call methods of this protocol to make API calls.
+/// The greeting service definition.
+///
+/// Usage: instantiate `Helloworld_GreeterClient`, then call methods of this protocol to make API calls.
 public protocol Helloworld_GreeterClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Helloworld_GreeterClientInterceptorFactoryProtocol? { get }
 
   func sayHello(
@@ -36,6 +39,9 @@ public protocol Helloworld_GreeterClientProtocol: GRPCClient {
 }
 
 extension Helloworld_GreeterClientProtocol {
+  public var serviceName: String {
+    return "helloworld.Greeter"
+  }
 
   /// Sends a greeting.
   ///
@@ -84,6 +90,8 @@ public final class Helloworld_GreeterClient: Helloworld_GreeterClientProtocol {
   }
 }
 
+/// The greeting service definition.
+///
 /// To build a server, implement a class that conforms to this protocol.
 public protocol Helloworld_GreeterProvider: CallHandlerProvider {
   var interceptors: Helloworld_GreeterServerInterceptorFactoryProtocol? { get }

+ 9 - 1
Sources/Examples/RouteGuide/Model/route_guide.grpc.swift

@@ -25,8 +25,11 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Routeguide_RouteGuideClient, then call methods of this protocol to make API calls.
+/// Interface exported by the server.
+///
+/// Usage: instantiate `Routeguide_RouteGuideClient`, then call methods of this protocol to make API calls.
 public protocol Routeguide_RouteGuideClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Routeguide_RouteGuideClientInterceptorFactoryProtocol? { get }
 
   func getFeature(
@@ -51,6 +54,9 @@ public protocol Routeguide_RouteGuideClientProtocol: GRPCClient {
 }
 
 extension Routeguide_RouteGuideClientProtocol {
+  public var serviceName: String {
+    return "routeguide.RouteGuide"
+  }
 
   /// A simple RPC.
   ///
@@ -184,6 +190,8 @@ public final class Routeguide_RouteGuideClient: Routeguide_RouteGuideClientProto
   }
 }
 
+/// Interface exported by the server.
+///
 /// To build a server, implement a class that conforms to this protocol.
 public protocol Routeguide_RouteGuideProvider: CallHandlerProvider {
   var interceptors: Routeguide_RouteGuideServerInterceptorFactoryProtocol? { get }

+ 31 - 3
Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift

@@ -25,8 +25,12 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Grpc_Testing_TestServiceClient, then call methods of this protocol to make API calls.
+/// A simple service to test the various types of RPCs and experiment with
+/// performance with various types of payload.
+///
+/// Usage: instantiate `Grpc_Testing_TestServiceClient`, then call methods of this protocol to make API calls.
 public protocol Grpc_Testing_TestServiceClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Grpc_Testing_TestServiceClientInterceptorFactoryProtocol? { get }
 
   func emptyCall(
@@ -71,6 +75,9 @@ public protocol Grpc_Testing_TestServiceClientProtocol: GRPCClient {
 }
 
 extension Grpc_Testing_TestServiceClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.TestService"
+  }
 
   /// One empty request followed by one empty response.
   ///
@@ -285,8 +292,12 @@ public final class Grpc_Testing_TestServiceClient: Grpc_Testing_TestServiceClien
   }
 }
 
-/// Usage: instantiate Grpc_Testing_UnimplementedServiceClient, then call methods of this protocol to make API calls.
+/// A simple service NOT implemented at servers so clients can test for
+/// that case.
+///
+/// Usage: instantiate `Grpc_Testing_UnimplementedServiceClient`, then call methods of this protocol to make API calls.
 public protocol Grpc_Testing_UnimplementedServiceClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Grpc_Testing_UnimplementedServiceClientInterceptorFactoryProtocol? { get }
 
   func unimplementedCall(
@@ -296,6 +307,9 @@ public protocol Grpc_Testing_UnimplementedServiceClientProtocol: GRPCClient {
 }
 
 extension Grpc_Testing_UnimplementedServiceClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.UnimplementedService"
+  }
 
   /// A call that no server should implement
   ///
@@ -344,8 +358,11 @@ public final class Grpc_Testing_UnimplementedServiceClient: Grpc_Testing_Unimple
   }
 }
 
-/// Usage: instantiate Grpc_Testing_ReconnectServiceClient, then call methods of this protocol to make API calls.
+/// A service used to control reconnect server.
+///
+/// Usage: instantiate `Grpc_Testing_ReconnectServiceClient`, then call methods of this protocol to make API calls.
 public protocol Grpc_Testing_ReconnectServiceClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Grpc_Testing_ReconnectServiceClientInterceptorFactoryProtocol? { get }
 
   func start(
@@ -360,6 +377,9 @@ public protocol Grpc_Testing_ReconnectServiceClientProtocol: GRPCClient {
 }
 
 extension Grpc_Testing_ReconnectServiceClientProtocol {
+  public var serviceName: String {
+    return "grpc.testing.ReconnectService"
+  }
 
   /// Unary call to Start
   ///
@@ -429,6 +449,9 @@ public final class Grpc_Testing_ReconnectServiceClient: Grpc_Testing_ReconnectSe
   }
 }
 
+/// A simple service to test the various types of RPCs and experiment with
+/// performance with various types of payload.
+///
 /// To build a server, implement a class that conforms to this protocol.
 public protocol Grpc_Testing_TestServiceProvider: CallHandlerProvider {
   var interceptors: Grpc_Testing_TestServiceServerInterceptorFactoryProtocol? { get }
@@ -578,6 +601,9 @@ public protocol Grpc_Testing_TestServiceServerInterceptorFactoryProtocol {
   ///   Defaults to calling `self.makeInterceptors()`.
   func makeUnimplementedCallInterceptors() -> [ServerInterceptor<Grpc_Testing_Empty, Grpc_Testing_Empty>]
 }
+/// A simple service NOT implemented at servers so clients can test for
+/// that case.
+///
 /// To build a server, implement a class that conforms to this protocol.
 public protocol Grpc_Testing_UnimplementedServiceProvider: CallHandlerProvider {
   var interceptors: Grpc_Testing_UnimplementedServiceServerInterceptorFactoryProtocol? { get }
@@ -618,6 +644,8 @@ public protocol Grpc_Testing_UnimplementedServiceServerInterceptorFactoryProtoco
   ///   Defaults to calling `self.makeInterceptors()`.
   func makeUnimplementedCallInterceptors() -> [ServerInterceptor<Grpc_Testing_Empty, Grpc_Testing_Empty>]
 }
+/// A service used to control reconnect server.
+///
 /// To build a server, implement a class that conforms to this protocol.
 public protocol Grpc_Testing_ReconnectServiceProvider: CallHandlerProvider {
   var interceptors: Grpc_Testing_ReconnectServiceServerInterceptorFactoryProtocol? { get }

+ 5 - 8
Sources/GRPCInteroperabilityTestModels/generate.sh

@@ -32,11 +32,8 @@ protoc "src/proto/grpc/testing/messages.proto" \
   --grpc-swift_out=${OUTPUT} \
   --grpc-swift_opt=FileNaming=${FILE_NAMING},Visibility=${VISIBILITY}
 
-echo "The generated code needs to be modified to support testing an unimplemented method."
-echo "On the server side, the generated code needs to be removed so the server has no"
-echo "knowledge of it. Client code requires no modification, since it is required to call"
-echo "the unimplemented method.\n"
-
-echo "In the generated 'Grpc_Testing_TestServiceProvider' protocol code in ${OUTPUT}/test.grpc.swift:"
-echo "1. remove 'unimplementedCall(request:context:)'"
-echo "2. remove the 'UnimplementedCall' case from 'handleMethod(:request:serverHandler:GRPCChannelHandler:channel:errorDelegate)'"
+# The generated code needs to be modified to support testing an unimplemented method.
+# On the server side, the generated code needs to be removed so the server has no
+# knowledge of it. Client code requires no modification, since it is required to call
+# the unimplemented method.
+patch -p3 < unimplemented_call.patch

+ 32 - 0
Sources/GRPCInteroperabilityTestModels/unimplemented_call.patch

@@ -0,0 +1,32 @@
+diff --git a/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift b/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
+index 6a5f3099..ae0ba123 100644
+--- a/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
++++ b/Sources/GRPCInteroperabilityTestModels/Generated/test.grpc.swift
+@@ -485,10 +485,6 @@ public protocol Grpc_Testing_TestServiceProvider: CallHandlerProvider {
+   /// stream of responses are returned to the client when the server starts with
+   /// first request.
+   func halfDuplexCall(context: StreamingResponseCallContext<Grpc_Testing_StreamingOutputCallResponse>) -> EventLoopFuture<(StreamEvent<Grpc_Testing_StreamingOutputCallRequest>) -> Void>
+-
+-  /// The test server will not implement this method. It will be used
+-  /// to test the behavior when clients call unimplemented methods.
+-  func unimplementedCall(request: Grpc_Testing_Empty, context: StatusOnlyCallContext) -> EventLoopFuture<Grpc_Testing_Empty>
+ }
+ 
+ extension Grpc_Testing_TestServiceProvider {
+@@ -565,16 +561,6 @@ extension Grpc_Testing_TestServiceProvider {
+         self.halfDuplexCall(context: context)
+       }
+ 
+-    case "UnimplementedCall":
+-      return CallHandlerFactory.makeUnary(
+-        callHandlerContext: callHandlerContext,
+-        interceptors: self.interceptors?.makeUnimplementedCallInterceptors() ?? []
+-      ) { context in
+-        return { request in
+-          self.unimplementedCall(request: request, context: context)
+-        }
+-      }
+-
+     default:
+       return nil
+     }

+ 16 - 2
Sources/protoc-gen-grpc-swift/Generator-Client.swift

@@ -75,11 +75,18 @@ extension Generator {
   }
 
   private func printServiceClientProtocol() {
+    let comments = self.service.protoSourceComments()
+    if !comments.isEmpty {
+      // Source comments already have the leading '///'
+      self.println(comments, newline: false)
+      self.println("///")
+    }
     self.println(
-      "/// Usage: instantiate \(self.clientClassName), then call methods of this protocol to make API calls."
+      "/// Usage: instantiate `\(self.clientClassName)`, then call methods of this protocol to make API calls."
     )
     self.println("\(self.access) protocol \(self.clientProtocolName): GRPCClient {")
     self.withIndentation {
+      self.println("var serviceName: String { get }")
       self.println("var interceptors: \(self.clientInterceptorProtocolName)? { get }")
 
       for method in service.methods {
@@ -100,8 +107,15 @@ extension Generator {
   private func printClientProtocolExtension() {
     self.println("extension \(self.clientProtocolName) {")
 
-    // Default method implementations.
     self.withIndentation {
+      // Service name.
+      self.println("\(self.access) var serviceName: String {")
+      self.withIndentation {
+        self.println("return \"\(self.servicePath)\"")
+      }
+      self.println("}")
+
+      // Default method implementations.
       self.printMethods()
     }
 

+ 6 - 0
Sources/protoc-gen-grpc-swift/Generator-Server.swift

@@ -27,6 +27,12 @@ extension Generator {
   }
 
   private func printServerProtocol() {
+    let comments = self.service.protoSourceComments()
+    if !comments.isEmpty {
+      // Source comments already have the leading '///'
+      self.println(comments, newline: false)
+      self.println("///")
+    }
     println("/// To build a server, implement a class that conforms to this protocol.")
     println("\(access) protocol \(providerName): CallHandlerProvider {")
     self.withIndentation {

+ 5 - 1
dev/codegen-tests/01-echo/golden/echo.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Echo_EchoClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `Echo_EchoClient`, then call methods of this protocol to make API calls.
 internal protocol Echo_EchoClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Echo_EchoClientInterceptorFactoryProtocol? { get }
 
   func get(
@@ -51,6 +52,9 @@ internal protocol Echo_EchoClientProtocol: GRPCClient {
 }
 
 extension Echo_EchoClientProtocol {
+  internal var serviceName: String {
+    return "echo.Echo"
+  }
 
   /// Immediately returns an echo of a request.
   ///

+ 5 - 1
dev/codegen-tests/02-multifile/golden/a.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate A_ServiceAClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `A_ServiceAClient`, then call methods of this protocol to make API calls.
 internal protocol A_ServiceAClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: A_ServiceAClientInterceptorFactoryProtocol? { get }
 
   func callServiceA(
@@ -36,6 +37,9 @@ internal protocol A_ServiceAClientProtocol: GRPCClient {
 }
 
 extension A_ServiceAClientProtocol {
+  internal var serviceName: String {
+    return "a.ServiceA"
+  }
 
   /// Unary call to CallServiceA
   ///

+ 5 - 1
dev/codegen-tests/02-multifile/golden/b.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate B_ServiceBClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `B_ServiceBClient`, then call methods of this protocol to make API calls.
 internal protocol B_ServiceBClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: B_ServiceBClientInterceptorFactoryProtocol? { get }
 
   func callServiceB(
@@ -36,6 +37,9 @@ internal protocol B_ServiceBClientProtocol: GRPCClient {
 }
 
 extension B_ServiceBClientProtocol {
+  internal var serviceName: String {
+    return "b.ServiceB"
+  }
 
   /// Unary call to CallServiceB
   ///

+ 5 - 1
dev/codegen-tests/03-multifile-with-module-map/golden/a.grpc.swift

@@ -26,8 +26,9 @@ import SwiftProtobuf
 import ModuleB
 
 
-/// Usage: instantiate A_ServiceAClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `A_ServiceAClient`, then call methods of this protocol to make API calls.
 internal protocol A_ServiceAClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: A_ServiceAClientInterceptorFactoryProtocol? { get }
 
   func callServiceA(
@@ -37,6 +38,9 @@ internal protocol A_ServiceAClientProtocol: GRPCClient {
 }
 
 extension A_ServiceAClientProtocol {
+  internal var serviceName: String {
+    return "a.ServiceA"
+  }
 
   /// Unary call to CallServiceA
   ///

+ 5 - 1
dev/codegen-tests/03-multifile-with-module-map/golden/b.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate B_ServiceBClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `B_ServiceBClient`, then call methods of this protocol to make API calls.
 internal protocol B_ServiceBClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: B_ServiceBClientInterceptorFactoryProtocol? { get }
 
   func callServiceB(
@@ -36,6 +37,9 @@ internal protocol B_ServiceBClientProtocol: GRPCClient {
 }
 
 extension B_ServiceBClientProtocol {
+  internal var serviceName: String {
+    return "b.ServiceB"
+  }
 
   /// Unary call to CallServiceB
   ///

+ 5 - 1
dev/codegen-tests/04-service-with-message-import/golden/service.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Codegentest_FooClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `Codegentest_FooClient`, then call methods of this protocol to make API calls.
 internal protocol Codegentest_FooClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Codegentest_FooClientInterceptorFactoryProtocol? { get }
 
   func get(
@@ -36,6 +37,9 @@ internal protocol Codegentest_FooClientProtocol: GRPCClient {
 }
 
 extension Codegentest_FooClientProtocol {
+  internal var serviceName: String {
+    return "codegentest.Foo"
+  }
 
   /// Unary call to Get
   ///

+ 5 - 1
dev/codegen-tests/05-service-only/golden/test.grpc.swift

@@ -25,8 +25,9 @@ import NIO
 import SwiftProtobuf
 
 
-/// Usage: instantiate Codegentest_FooClient, then call methods of this protocol to make API calls.
+/// Usage: instantiate `Codegentest_FooClient`, then call methods of this protocol to make API calls.
 internal protocol Codegentest_FooClientProtocol: GRPCClient {
+  var serviceName: String { get }
   var interceptors: Codegentest_FooClientInterceptorFactoryProtocol? { get }
 
   func bar(
@@ -36,6 +37,9 @@ internal protocol Codegentest_FooClientProtocol: GRPCClient {
 }
 
 extension Codegentest_FooClientProtocol {
+  internal var serviceName: String {
+    return "codegentest.Foo"
+  }
 
   /// Unary call to Bar
   ///