Browse Source

Allow clients to make calls via factory methods (#421)

* Allow GRPCClient to not be for a specific service

* Factory methods for creating calls on a connection

* Remove callOptions on GRPCClientConnection
George Barnett 6 years ago
parent
commit
3c8fb7f991

+ 17 - 9
Sources/Examples/EchoNIO/Generated/echo.grpc.swift

@@ -35,19 +35,19 @@ internal protocol Echo_EchoService_NIO {
   func update(callOptions: CallOptions?, handler: @escaping (Echo_EchoResponse) -> Void) -> BidirectionalStreamingClientCall<Echo_EchoRequest, Echo_EchoResponse>
 }
 
-internal final class Echo_EchoService_NIOClient: GRPCClient, Echo_EchoService_NIO {
+internal final class Echo_EchoService_NIOClient: GRPCServiceClient, Echo_EchoService_NIO {
   internal let connection: GRPCClientConnection
-  internal let service = "echo.Echo"
+  internal var serviceName: String { return "echo.Echo" }
   internal var defaultCallOptions: CallOptions
 
   /// Creates a client for the echo.Echo service.
   ///
   /// - Parameters:
   ///   - connection: `GRPCClientConnection` to the service host.
-  ///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them. Defaults to `client.defaultCallOptions`.
-  internal init(connection: GRPCClientConnection, defaultCallOptions: CallOptions? = nil) {
+  ///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
+  internal init(connection: GRPCClientConnection, defaultCallOptions: CallOptions = CallOptions()) {
     self.connection = connection
-    self.defaultCallOptions = defaultCallOptions ?? connection.defaultCallOptions
+    self.defaultCallOptions = defaultCallOptions
   }
 
   /// Asynchronous unary call to Get.
@@ -57,7 +57,9 @@ internal final class Echo_EchoService_NIOClient: GRPCClient, Echo_EchoService_NI
   ///   - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
   /// - Returns: A `UnaryClientCall` with futures for the metadata, status and response.
   internal func get(_ request: Echo_EchoRequest, callOptions: CallOptions? = nil) -> UnaryClientCall<Echo_EchoRequest, Echo_EchoResponse> {
-    return UnaryClientCall(connection: self.connection, path: self.path(forMethod: "Get"), request: request, callOptions: callOptions ?? self.defaultCallOptions)
+    return self.makeUnaryCall(path: self.path(forMethod: "Get"),
+                              request: request,
+                              callOptions: callOptions ?? self.defaultCallOptions)
   }
 
   /// Asynchronous server-streaming call to Expand.
@@ -68,7 +70,10 @@ internal final class Echo_EchoService_NIOClient: GRPCClient, Echo_EchoService_NI
   ///   - handler: A closure called when each response is received from the server.
   /// - Returns: A `ServerStreamingClientCall` with futures for the metadata and status.
   internal func expand(_ request: Echo_EchoRequest, callOptions: CallOptions? = nil, handler: @escaping (Echo_EchoResponse) -> Void) -> ServerStreamingClientCall<Echo_EchoRequest, Echo_EchoResponse> {
-    return ServerStreamingClientCall(connection: self.connection, path: self.path(forMethod: "Expand"), request: request, callOptions: callOptions ?? self.defaultCallOptions, handler: handler)
+    return self.makeServerStreamingCall(path: self.path(forMethod: "Expand"),
+                                        request: request,
+                                        callOptions: callOptions ?? self.defaultCallOptions,
+                                        handler: handler)
   }
 
   /// Asynchronous client-streaming call to Collect.
@@ -80,7 +85,8 @@ internal final class Echo_EchoService_NIOClient: GRPCClient, Echo_EchoService_NI
   ///   - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
   /// - Returns: A `ClientStreamingClientCall` with futures for the metadata, status and response.
   internal func collect(callOptions: CallOptions? = nil) -> ClientStreamingClientCall<Echo_EchoRequest, Echo_EchoResponse> {
-    return ClientStreamingClientCall(connection: self.connection, path: self.path(forMethod: "Collect"), callOptions: callOptions ?? self.defaultCallOptions)
+    return self.makeClientStreamingCall(path: self.path(forMethod: "Collect"),
+                                        callOptions: callOptions ?? self.defaultCallOptions)
   }
 
   /// Asynchronous bidirectional-streaming call to Update.
@@ -93,7 +99,9 @@ internal final class Echo_EchoService_NIOClient: GRPCClient, Echo_EchoService_NI
   ///   - handler: A closure called when each response is received from the server.
   /// - Returns: A `ClientStreamingClientCall` with futures for the metadata and status.
   internal func update(callOptions: CallOptions? = nil, handler: @escaping (Echo_EchoResponse) -> Void) -> BidirectionalStreamingClientCall<Echo_EchoRequest, Echo_EchoResponse> {
-    return BidirectionalStreamingClientCall(connection: self.connection, path: self.path(forMethod: "Update"), callOptions: callOptions ?? self.defaultCallOptions, handler: handler)
+    return self.makeBidirectionalStreamingCall(path: self.path(forMethod: "Update"),
+                                               callOptions: callOptions ?? self.defaultCallOptions,
+                                               handler: handler)
   }
 
 }

+ 54 - 9
Sources/SwiftGRPCNIO/GRPCClient.swift

@@ -14,29 +14,74 @@
  * limitations under the License.
  */
 import Foundation
+import SwiftProtobuf
 
-/// A GRPC client for a given service.
+/// A GRPC client.
 public protocol GRPCClient {
   /// The connection providing the underlying HTTP/2 channel for this client.
   var connection: GRPCClientConnection { get }
 
-  /// Name of the service this client is for (e.g. "echo.Echo").
-  var service: String { get }
-
   /// The call options to use should the user not provide per-call options.
   var defaultCallOptions: CallOptions { get set }
+}
+
+extension GRPCClient {
+  public func makeUnaryCall<Request: Message, Response: Message>(
+    path: String,
+    request: Request,
+    callOptions: CallOptions,
+    responseType: Response.Type = Response.self
+  ) -> UnaryClientCall<Request, Response> {
+    return UnaryClientCall(connection: self.connection, path: path, request: request, callOptions: callOptions)
+  }
+
+  public func makeServerStreamingCall<Request: Message, Response: Message>(
+    path: String,
+    request: Request,
+    callOptions: CallOptions,
+    responseType: Response.Type = Response.self,
+    handler: @escaping (Response) -> Void
+  ) -> ServerStreamingClientCall<Request, Response> {
+    return ServerStreamingClientCall(connection: self.connection, path: path, request: request, callOptions: callOptions, handler: handler)
+  }
 
-  /// Return the path for the given method in the format "/Service-Name/Method-Name".
+  public func makeClientStreamingCall<Request: Message, Response: Message>(
+    path: String,
+    callOptions: CallOptions,
+    requestType: Request.Type = Request.self,
+    responseType: Response.Type = Response.self
+  ) -> ClientStreamingClientCall<Request, Response> {
+    return ClientStreamingClientCall(connection: self.connection, path: path, callOptions: callOptions)
+  }
+
+  public func makeBidirectionalStreamingCall<Request: Message, Response: Message>(
+    path: String,
+    callOptions: CallOptions,
+    requestType: Request.Type = Request.self,
+    responseType: Response.Type = Response.self,
+    handler: @escaping (Response) -> Void
+  ) -> BidirectionalStreamingClientCall<Request, Response> {
+    return BidirectionalStreamingClientCall(connection: self.connection, path: path, callOptions: callOptions, handler: handler)
+  }
+}
+
+/// A GRPC client for a named service.
+public protocol GRPCServiceClient: GRPCClient {
+  /// Name of the service this client is for (e.g. "echo.Echo").
+  var serviceName: String { get }
+
+  /// Creates a path for a given method on this service.
   ///
-  /// This may be overriden if consumers require a different path format.
+  /// This defaults to "/Service-Name/Method-Name" but may be overriden if consumers
+  /// require a different path format.
   ///
-  /// - Parameter forMethod: name of method to return a path for.
+  /// - Parameter method: name of method to return a path for.
   /// - Returns: path for the given method used in gRPC request headers.
   func path(forMethod method: String) -> String
 }
 
-extension GRPCClient {
+extension GRPCServiceClient {
   public func path(forMethod method: String) -> String {
-    return "/\(service)/\(method)"
+    return "/\(self.serviceName)/\(method)"
   }
 }

+ 1 - 3
Sources/SwiftGRPCNIO/GRPCClientConnection.swift

@@ -74,14 +74,12 @@ open class GRPCClientConnection {
   public let channel: Channel
   public let multiplexer: HTTP2StreamMultiplexer
   public let host: String
-  public var defaultCallOptions: CallOptions
   public let httpProtocol: HTTP2ToHTTP1ClientCodec.HTTPProtocol
 
-  init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, host: String, httpProtocol: HTTP2ToHTTP1ClientCodec.HTTPProtocol, defaultCallOptions: CallOptions = CallOptions()) {
+  init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, host: String, httpProtocol: HTTP2ToHTTP1ClientCodec.HTTPProtocol) {
     self.channel = channel
     self.multiplexer = multiplexer
     self.host = host
-    self.defaultCallOptions = defaultCallOptions
     self.httpProtocol = httpProtocol
   }
 

+ 17 - 9
Sources/protoc-gen-swiftgrpc/Generator-Client.swift

@@ -446,21 +446,21 @@ extension Generator {
   }
 
   private func printNIOServiceClientImplementation() {
-    println("\(access) final class \(serviceClassName)Client: GRPCClient, \(serviceClassName) {")
+    println("\(access) final class \(serviceClassName)Client: GRPCServiceClient, \(serviceClassName) {")
     indent()
     println("\(access) let connection: GRPCClientConnection")
-    println("\(access) let service = \"\(servicePath)\"")
+    println("\(access) var serviceName: String { return \"\(servicePath)\" }")
     println("\(access) var defaultCallOptions: CallOptions")
     println()
     println("/// Creates a client for the \(servicePath) service.")
     println("///")
     printParameters()
     println("///   - connection: `GRPCClientConnection` to the service host.")
-    println("///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them. Defaults to `client.defaultCallOptions`.")
-    println("\(access) init(connection: GRPCClientConnection, defaultCallOptions: CallOptions? = nil) {")
+    println("///   - defaultCallOptions: Options to use for each service call if the user doesn't provide them.")
+    println("\(access) init(connection: GRPCClientConnection, defaultCallOptions: CallOptions = CallOptions()) {")
     indent()
     println("self.connection = connection")
-    println("self.defaultCallOptions = defaultCallOptions ?? connection.defaultCallOptions")
+    println("self.defaultCallOptions = defaultCallOptions")
     outdent()
     println("}")
     println()
@@ -477,7 +477,9 @@ extension Generator {
         println("/// - Returns: A `UnaryClientCall` with futures for the metadata, status and response.")
         println("\(access) func \(methodFunctionName)(_ request: \(methodInputName), callOptions: CallOptions? = nil) -> UnaryClientCall<\(methodInputName), \(methodOutputName)> {")
         indent()
-        println("return UnaryClientCall(connection: self.connection, path: self.path(forMethod: \"\(method.name)\"), request: request, callOptions: callOptions ?? self.defaultCallOptions)")
+        println("return self.makeUnaryCall(path: self.path(forMethod: \"\(method.name)\"),")
+        println("                          request: request,")
+        println("                          callOptions: callOptions ?? self.defaultCallOptions)")
         outdent()
         println("}")
 
@@ -491,7 +493,10 @@ extension Generator {
         println("/// - Returns: A `ServerStreamingClientCall` with futures for the metadata and status.")
         println("\(access) func \(methodFunctionName)(_ request: \(methodInputName), callOptions: CallOptions? = nil, handler: @escaping (\(methodOutputName)) -> Void) -> ServerStreamingClientCall<\(methodInputName), \(methodOutputName)> {")
         indent()
-        println("return ServerStreamingClientCall(connection: self.connection, path: self.path(forMethod: \"\(method.name)\"), request: request, callOptions: callOptions ?? self.defaultCallOptions, handler: handler)")
+        println("return self.makeServerStreamingCall(path: self.path(forMethod: \"\(method.name)\"),")
+        println("                                    request: request,")
+        println("                                    callOptions: callOptions ?? self.defaultCallOptions,")
+        println("                                    handler: handler)")
         outdent()
         println("}")
 
@@ -505,7 +510,8 @@ extension Generator {
         println("/// - Returns: A `ClientStreamingClientCall` with futures for the metadata, status and response.")
         println("\(access) func \(methodFunctionName)(callOptions: CallOptions? = nil) -> ClientStreamingClientCall<\(methodInputName), \(methodOutputName)> {")
         indent()
-        println("return ClientStreamingClientCall(connection: self.connection, path: self.path(forMethod: \"\(method.name)\"), callOptions: callOptions ?? self.defaultCallOptions)")
+        println("return self.makeClientStreamingCall(path: self.path(forMethod: \"\(method.name)\"),")
+        println("                                    callOptions: callOptions ?? self.defaultCallOptions)")
         outdent()
         println("}")
 
@@ -520,7 +526,9 @@ extension Generator {
         println("/// - Returns: A `ClientStreamingClientCall` with futures for the metadata and status.")
         println("\(access) func \(methodFunctionName)(callOptions: CallOptions? = nil, handler: @escaping (\(methodOutputName)) -> Void) -> BidirectionalStreamingClientCall<\(methodInputName), \(methodOutputName)> {")
         indent()
-        println("return BidirectionalStreamingClientCall(connection: self.connection, path: self.path(forMethod: \"\(method.name)\"), callOptions: callOptions ?? self.defaultCallOptions, handler: handler)")
+        println("return self.makeBidirectionalStreamingCall(path: self.path(forMethod: \"\(method.name)\"),")
+        println("                                           callOptions: callOptions ?? self.defaultCallOptions,")
+        println("                                           handler: handler)")
         outdent()
         println("}")
       }