瀏覽代碼

Allow separate generation of test clients (#881)

Motivation:

Users should be able to generate their test clients separately to their
real clients so that their real clients can be public and their test
clients internal.

Modifications:

- Allow test clients to be generated separately from real clients
- Add an option to control whether conformance is generated; this allows
  test clients to be generated separately from real clients when the
  service and messages are defined in a single file without duplicating
  the conformance.
- Expand on the plugin documentation.

Result:

Resolves #878
George Barnett 5 年之前
父節點
當前提交
2272cc9198

+ 8 - 6
Sources/protoc-gen-grpc-swift/Generator-Client.swift

@@ -19,12 +19,14 @@ import SwiftProtobufPluginLibrary
 
 extension Generator {
   internal func printClient() {
-    println()
-    printServiceClientProtocol()
-    println()
-    printClientProtocolExtension()
-    println()
-    printServiceClientImplementation()
+    if self.options.generateClient {
+      self.println()
+      self.printServiceClientProtocol()
+      self.println()
+      self.printClientProtocolExtension()
+      self.println()
+      self.printServiceClientImplementation()
+    }
 
     if self.options.generateTestClient {
       self.println()

+ 10 - 7
Sources/protoc-gen-grpc-swift/Generator.swift

@@ -104,11 +104,11 @@ class Generator {
     }
     println()
 
-    if options.generateClient {
-      for service in file.services {
-        self.service = service
-        printClient()
-      }
+    // We defer the check for printing clients to `printClient()` since this could be the 'real'
+    // client or the test client.
+    for service in file.services {
+      self.service = service
+      self.printClient()
     }
     println()
 
@@ -118,7 +118,10 @@ class Generator {
         printServer()
       }
     }
-    self.println()
-    self.printProtobufExtensions()
+
+    if options.generatePayloadConformance {
+      self.println()
+      self.printProtobufExtensions()
+    }
   }
 }

+ 8 - 0
Sources/protoc-gen-grpc-swift/options.swift

@@ -55,6 +55,7 @@ final class GeneratorOptions {
   private(set) var generateServer = true
   private(set) var generateClient = true
   private(set) var generateTestClient = false
+  private(set) var generatePayloadConformance = true
   private(set) var protoToModuleMappings = ProtoFileToModuleMappings()
   private(set) var fileNaming = FileNaming.FullPath
   private(set) var extraModuleImports: [String] = []
@@ -90,6 +91,13 @@ final class GeneratorOptions {
           throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
         }
 
+      case "PayloadConformance":
+        if let value = Bool(pair.value) {
+          self.generatePayloadConformance = value
+        } else {
+          throw GenerationError.invalidParameterValue(name: pair.key, value: pair.value)
+        }
+
       case "ProtoPathModuleMappings":
         if !pair.value.isEmpty {
           do {

+ 37 - 0
dev/codegen-tests/06-test-client-only/generate-and-diff.sh

@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# Copyright 2020, gRPC Authors All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eu
+
+HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+source "${HERE}/../test-boilerplate.sh"
+
+function all_at_once {
+  echo "[${TEST}]"
+
+  prepare
+
+  protoc \
+    --proto_path="${PROTO_DIR}" \
+    --plugin="${PROTOC_GEN_GRPC_SWIFT}" \
+    --grpc-swift_opt=Server=false,Client=false,TestClient=true,PayloadConformance=false \
+    --grpc-swift_out="${OUTPUT_DIR}" \
+    "${PROTO_DIR}"/*
+
+  validate
+}
+
+all_at_once

+ 70 - 0
dev/codegen-tests/06-test-client-only/golden/test.grpc.swift

@@ -0,0 +1,70 @@
+//
+// DO NOT EDIT.
+//
+// Generated by the protocol buffer compiler.
+// Source: test.proto
+//
+
+//
+// Copyright 2018, gRPC Authors All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+import Foundation
+import GRPC
+import NIO
+import NIOHTTP1
+import SwiftProtobuf
+
+
+internal final class Codegentest_FooTestClient: Codegentest_FooClientProtocol {
+  private let fakeChannel: FakeChannel
+  internal var defaultCallOptions: CallOptions
+
+  internal var channel: GRPCChannel {
+    return self.fakeChannel
+  }
+
+  internal init(
+    fakeChannel: FakeChannel = FakeChannel(),
+    defaultCallOptions callOptions: CallOptions = CallOptions()
+  ) {
+    self.fakeChannel = fakeChannel
+    self.defaultCallOptions = callOptions
+  }
+
+  /// Make a unary response for the Bar RPC. This must be called
+  /// before calling 'bar'. See also 'FakeUnaryResponse'.
+  ///
+  /// - Parameter requestHandler: a handler for request parts sent by the RPC.
+  internal func makeBarResponseStream(
+    _ requestHandler: @escaping (FakeRequestPart<Codegentest_BarRequest>) -> () = { _ in }
+  ) -> FakeUnaryResponse<Codegentest_BarRequest, Codegentest_BarResponse> {
+    return self.fakeChannel.makeFakeUnaryResponse(path: "/codegentest.Foo/Bar", requestHandler: requestHandler)
+  }
+
+  internal func enqueueBarResponse(
+    _ response: Codegentest_BarResponse,
+    _ requestHandler: @escaping (FakeRequestPart<Codegentest_BarRequest>) -> () = { _ in }
+  )  {
+    let stream = self.makeBarResponseStream(requestHandler)
+    // This is the only operation on the stream; try! is fine.
+    try! stream.sendMessage(response)
+  }
+
+  /// Returns true if there are response streams enqueued for 'Bar'
+  internal var hasBarResponsesRemaining: Bool {
+    return self.fakeChannel.hasFakeResponseEnqueued(forPath: "/codegentest.Foo/Bar")
+  }
+}
+

+ 28 - 0
dev/codegen-tests/06-test-client-only/proto/test.proto

@@ -0,0 +1,28 @@
+// Copyright 2020, gRPC Authors All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+syntax = "proto3";
+
+package codegentest;
+
+service Foo {
+  rpc Bar(BarRequest) returns (BarResponse) {}
+}
+
+message BarRequest {
+  string text = 1;
+}
+
+message BarResponse {
+  string text = 1;
+}

+ 64 - 12
docs/plugin.md

@@ -1,4 +1,4 @@
-# `protoc` Swift gRPC plugin
+# `protoc` gRPC Swift plugin
 
 gRPC Swift provides a plugin for the [protocol buffer][protocol-buffers]
 compiler `protoc` to generate classes for clients and services.
@@ -23,18 +23,69 @@ when invoking `protoc`.
 
 ## Plugin Options
 
-The table below lists the options available to the `protoc-gen-grpc-swift`
-plugin:
+### Visibility
 
-| Flag                      | Values                                    | Default    | Description
-|:--------------------------|:------------------------------------------|:-----------|:----------------------------------------------------------------------------------------------------------------------
-| `Visibility`              | `Internal`/`Public`                       | `Internal` | ACL of generated code
-| `Server`                  | `true`/`false`                            | `true`     | Whether to generate server code
-| `Client`                  | `true`/`false`                            | `true`     | Whether to generate client code
-| `TestClient`              | `true`/`false`                            | `false`    | Whether to generate test client code. Ignored if `Client` is `false`.
-| `FileNaming`              | `FullPath`/`PathToUnderscores`/`DropPath` | `FullPath` | How to handle the naming of generated sources, see [documentation][swift-protobuf-filenaming]
-| `ExtraModuleImports`      | `String`                                  |            | Extra module to import in generated code. This parameter may be included multiple times to import more than one module
-| `ProtoPathModuleMappings` | `String`                                  |            | The path of the file that contains the module mappings for the generated code, see [swift-protobuf documentation](https://github.com/apple/swift-protobuf/blob/master/Documentation/PLUGIN.md#generation-option-protopathmodulemappings---swift-module-names-for-proto-paths)
+The **Visibility** option determines the access control for generated code.
+
+- **Possible values:** Public, Internal
+- **Default value:** Internal
+
+### Server
+
+The **Server** option determines whether server code is generated. The
+generated server code includes a `protocol` which users must implement
+to provide the logic for their service.
+
+- **Possible values:** true, false
+- **Default value:** true
+
+### Client
+
+The **Client** option determines whether client code is generated. The
+generated client code includes a `protocol` and a `class` conforming to that
+protocol.
+
+- **Possible values:** true, false
+- **Default value:** true
+
+### TestClient
+
+The **TestClient** option determines whether test client code is generated.
+This does *not* include the `protocol` generated by the **Client** option.
+
+- **Possible values:** true, false
+- **Default value:** false
+
+### PayloadConformance
+
+The **PayloadConformance** option determines whether conformance to
+`GRPCProtobufPayload` is generated for message definitions. Conformance to
+`GRPCPayload` indicates that gRPC can serialize and deserialize the payload.
+`GRPCProtobufPayload` protives a default implementation to this `GRPCPayload`
+for `SwiftProtobuf.Message`s.
+
+- **Possible values:** true, false
+- **Default value:** true
+
+### FileNaming
+
+The **FileNaming** option determines how generated source files should be named.
+
+- **Possible values:**
+  - **FullPath**: The full path of the input file will be used;
+    `foo/bar/baz.proto` will generate `foo/bar/baz.grpc.proto`
+  - **PathToUnderscores**: Directories structures are flattened;
+    `foo/bar/baz.proto` will generate `foo_bar_baz.grpc.proto`
+  - **DropPath**: The path is dropped and only the name of the file is kept;
+    `foo/bar/baz.proto` will generate `baz.grpc.proto`
+- **Default value:** FullPath
+
+### ProtoPathModuleMappings
+
+The **ProtoPathModuleMappings** option allows module mappings to be specified.
+See the [SwiftProtobuf documentation][swift-protobuf-module-mappings].
+
+## Specifying Options
 
 To pass extra parameters to the plugin, use a comma-separated parameter list
 separated from the output directory by a colon. Alternatively use the
@@ -54,3 +105,4 @@ protoc <your proto> --grpc-swift_opt=Client=true,Server=false --grpc-swift_out=.
 
 [protocol-buffers]: https://developers.google.com/protocol-buffers/docs/overview
 [swift-protobuf-filenaming]: https://github.com/apple/swift-protobuf/blob/master/Documentation/PLUGIN.md#generation-option-filenaming---naming-of-generated-sources
+[swift-protobuf-module-mappings]: https://github.com/apple/swift-protobuf/blob/master/Documentation/PLUGIN.md#generation-option-protopathmodulemappings---swift-module-names-for-proto-paths