Skip to content

Provide conformance for messages provided by SwiftProtobuf #811

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ third_party/**
Examples/EchoWeb/dist
Examples/EchoWeb/node_modules
Examples/EchoWeb/package-lock.json
dev/codegen-tests/**/generated/*
11 changes: 3 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,10 @@ test-generate-linuxmain: generate-linuxmain
@git diff --exit-code Tests/LinuxMain.swift Tests/*/XCTestManifests.swift > /dev/null || \
{ echo "Generated tests are out-of-date; run 'swift test --generate-linuxmain' to update them!"; exit 1; }

# Generates code for the Echo server and client and tests them against 'golden' data.
# Runs codegen tests.
.PHONY:
test-plugin: ${ECHO_PROTO} ${PROTOC_GEN_GRPC_SWIFT} ${ECHO_GRPC}
protoc $< \
--proto_path=$(dir $<) \
--plugin=${PROTOC_GEN_GRPC_SWIFT} \
--grpc-swift_opt=Visibility=Public \
--grpc-swift_out=/tmp
diff -u /tmp/echo.grpc.swift ${ECHO_GRPC}
test-plugin: ${PROTOC_GEN_GRPC_SWIFT}
PROTOC_GEN_GRPC_SWIFT=${PROTOC_GEN_GRPC_SWIFT} ./dev/codegen-tests/run-tests.sh

### Misc. ######################################################################

Expand Down
3 changes: 1 addition & 2 deletions Sources/Examples/Echo/Model/echo.grpc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ extension Echo_EchoProvider {
}


// Provides conformance to `GRPCPayload` for request and response messages
// Provides conformance to `GRPCPayload`
extension Echo_EchoRequest: GRPCProtobufPayload {}
extension Echo_EchoResponse: GRPCProtobufPayload {}

3 changes: 1 addition & 2 deletions Sources/Examples/HelloWorld/Model/helloworld.grpc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@ extension Helloworld_GreeterProvider {
}


// Provides conformance to `GRPCPayload` for request and response messages
// Provides conformance to `GRPCPayload`
extension Helloworld_HelloRequest: GRPCProtobufPayload {}
extension Helloworld_HelloReply: GRPCProtobufPayload {}

7 changes: 3 additions & 4 deletions Sources/Examples/RouteGuide/Model/route_guide.grpc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,9 @@ extension Routeguide_RouteGuideProvider {
}


// Provides conformance to `GRPCPayload` for request and response messages
// Provides conformance to `GRPCPayload`
extension Routeguide_Point: GRPCProtobufPayload {}
extension Routeguide_Feature: GRPCProtobufPayload {}
extension Routeguide_Rectangle: GRPCProtobufPayload {}
extension Routeguide_RouteSummary: GRPCProtobufPayload {}
extension Routeguide_Feature: GRPCProtobufPayload {}
extension Routeguide_RouteNote: GRPCProtobufPayload {}

extension Routeguide_RouteSummary: GRPCProtobufPayload {}
53 changes: 53 additions & 0 deletions Sources/GRPC/GRPCProtobufPayload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,56 @@ public extension GRPCProtobufPayload {
buffer.writeBytes(data)
}
}

// SwiftProtobuf ships a bunch of different messages. We'll provide conformance to them here to
// avoid having to generate the conformance.
//
// See: https://github.com/grpc/grpc-swift/issues/801

extension Google_Protobuf_Any: GRPCProtobufPayload {}
extension Google_Protobuf_Api: GRPCProtobufPayload {}
extension Google_Protobuf_BoolValue: GRPCProtobufPayload {}
extension Google_Protobuf_BytesValue: GRPCProtobufPayload {}
extension Google_Protobuf_DescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_DoubleValue: GRPCProtobufPayload {}
extension Google_Protobuf_Duration: GRPCProtobufPayload {}
extension Google_Protobuf_Empty: GRPCProtobufPayload {}
extension Google_Protobuf_Enum: GRPCProtobufPayload {}
extension Google_Protobuf_EnumDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_EnumOptions: GRPCProtobufPayload {}
extension Google_Protobuf_EnumValue: GRPCProtobufPayload {}
extension Google_Protobuf_EnumValueDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_EnumValueOptions: GRPCProtobufPayload {}
extension Google_Protobuf_ExtensionRangeOptions: GRPCProtobufPayload {}
extension Google_Protobuf_Field: GRPCProtobufPayload {}
extension Google_Protobuf_FieldDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_FieldMask: GRPCProtobufPayload {}
extension Google_Protobuf_FieldOptions: GRPCProtobufPayload {}
extension Google_Protobuf_FileDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_FileDescriptorSet: GRPCProtobufPayload {}
extension Google_Protobuf_FileOptions: GRPCProtobufPayload {}
extension Google_Protobuf_FloatValue: GRPCProtobufPayload {}
extension Google_Protobuf_GeneratedCodeInfo: GRPCProtobufPayload {}
extension Google_Protobuf_Int32Value: GRPCProtobufPayload {}
extension Google_Protobuf_Int64Value: GRPCProtobufPayload {}
extension Google_Protobuf_ListValue: GRPCProtobufPayload {}
extension Google_Protobuf_MessageOptions: GRPCProtobufPayload {}
extension Google_Protobuf_Method: GRPCProtobufPayload {}
extension Google_Protobuf_MethodDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_MethodOptions: GRPCProtobufPayload {}
extension Google_Protobuf_Mixin: GRPCProtobufPayload {}
extension Google_Protobuf_OneofDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_OneofOptions: GRPCProtobufPayload {}
extension Google_Protobuf_Option: GRPCProtobufPayload {}
extension Google_Protobuf_ServiceDescriptorProto: GRPCProtobufPayload {}
extension Google_Protobuf_ServiceOptions: GRPCProtobufPayload {}
extension Google_Protobuf_SourceCodeInfo: GRPCProtobufPayload {}
extension Google_Protobuf_SourceContext: GRPCProtobufPayload {}
extension Google_Protobuf_StringValue: GRPCProtobufPayload {}
extension Google_Protobuf_Struct: GRPCProtobufPayload {}
extension Google_Protobuf_Timestamp: GRPCProtobufPayload {}
extension Google_Protobuf_Type: GRPCProtobufPayload {}
extension Google_Protobuf_UInt32Value: GRPCProtobufPayload {}
extension Google_Protobuf_UInt64Value: GRPCProtobufPayload {}
extension Google_Protobuf_UninterpretedOption: GRPCProtobufPayload {}
extension Google_Protobuf_Value: GRPCProtobufPayload {}
21 changes: 4 additions & 17 deletions Sources/protoc-gen-grpc-swift/Generator-Conformance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,10 @@ import SwiftProtobufPluginLibrary

extension Generator {
internal func printProtobufExtensions() {
println("// Provides conformance to `GRPCPayload` for request and response messages")
for service in self.file.services {
self.service = service
for method in self.service.methods {
self.method = method
self.printExtension(for: self.methodInputName)
self.printExtension(for: self.methodOutputName)
}
println()
println("// Provides conformance to `GRPCPayload`")
for message in self.file.messages {
let name = self.protobufNamer.fullName(message: message)
self.println("extension \(name): GRPCProtobufPayload {}")
}
}

private func printExtension(for messageType: String) {
guard !self.observedMessages.contains(messageType) else {
return
}
self.println("extension \(messageType): GRPCProtobufPayload {}")
self.observedMessages.insert(messageType)
}
}
36 changes: 36 additions & 0 deletions dev/codegen-tests/01-echo/generate-and-diff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/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_out="${OUTPUT_DIR}" \
"${PROTO_DIR}"/*

validate
}

all_at_once
159 changes: 159 additions & 0 deletions dev/codegen-tests/01-echo/golden/echo.grpc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// DO NOT EDIT.
//
// Generated by the protocol buffer compiler.
// Source: echo.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


/// Usage: instantiate Echo_EchoClient, then call methods of this protocol to make API calls.
internal protocol Echo_EchoClientProtocol {
func get(_ request: Echo_EchoRequest, callOptions: CallOptions?) -> UnaryCall<Echo_EchoRequest, Echo_EchoResponse>
func expand(_ request: Echo_EchoRequest, callOptions: CallOptions?, handler: @escaping (Echo_EchoResponse) -> Void) -> ServerStreamingCall<Echo_EchoRequest, Echo_EchoResponse>
func collect(callOptions: CallOptions?) -> ClientStreamingCall<Echo_EchoRequest, Echo_EchoResponse>
func update(callOptions: CallOptions?, handler: @escaping (Echo_EchoResponse) -> Void) -> BidirectionalStreamingCall<Echo_EchoRequest, Echo_EchoResponse>
}

internal final class Echo_EchoClient: GRPCClient, Echo_EchoClientProtocol {
internal let channel: GRPCChannel
internal var defaultCallOptions: CallOptions

/// Creates a client for the echo.Echo service.
///
/// - Parameters:
/// - channel: `GRPCChannel` to the service host.
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
internal init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
self.channel = channel
self.defaultCallOptions = defaultCallOptions
}

/// Immediately returns an echo of a request.
///
/// - Parameters:
/// - request: Request to send to Get.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
internal func get(_ request: Echo_EchoRequest, callOptions: CallOptions? = nil) -> UnaryCall<Echo_EchoRequest, Echo_EchoResponse> {
return self.makeUnaryCall(path: "/echo.Echo/Get",
request: request,
callOptions: callOptions ?? self.defaultCallOptions)
}

/// Splits a request into words and returns each word in a stream of messages.
///
/// - Parameters:
/// - request: Request to send to Expand.
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ServerStreamingCall` with futures for the metadata and status.
internal func expand(_ request: Echo_EchoRequest, callOptions: CallOptions? = nil, handler: @escaping (Echo_EchoResponse) -> Void) -> ServerStreamingCall<Echo_EchoRequest, Echo_EchoResponse> {
return self.makeServerStreamingCall(path: "/echo.Echo/Expand",
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler)
}

/// Collects a stream of messages and returns them concatenated when the caller closes.
///
/// Callers should use the `send` method on the returned object to send messages
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - Returns: A `ClientStreamingCall` with futures for the metadata, status and response.
internal func collect(callOptions: CallOptions? = nil) -> ClientStreamingCall<Echo_EchoRequest, Echo_EchoResponse> {
return self.makeClientStreamingCall(path: "/echo.Echo/Collect",
callOptions: callOptions ?? self.defaultCallOptions)
}

/// Streams back messages as they are received in an input stream.
///
/// Callers should use the `send` method on the returned object to send messages
/// to the server. The caller should send an `.end` after the final message has been sent.
///
/// - Parameters:
/// - callOptions: Call options; `self.defaultCallOptions` is used if `nil`.
/// - handler: A closure called when each response is received from the server.
/// - Returns: A `ClientStreamingCall` with futures for the metadata and status.
internal func update(callOptions: CallOptions? = nil, handler: @escaping (Echo_EchoResponse) -> Void) -> BidirectionalStreamingCall<Echo_EchoRequest, Echo_EchoResponse> {
return self.makeBidirectionalStreamingCall(path: "/echo.Echo/Update",
callOptions: callOptions ?? self.defaultCallOptions,
handler: handler)
}

}

/// To build a server, implement a class that conforms to this protocol.
internal protocol Echo_EchoProvider: CallHandlerProvider {
/// Immediately returns an echo of a request.
func get(request: Echo_EchoRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Echo_EchoResponse>
/// Splits a request into words and returns each word in a stream of messages.
func expand(request: Echo_EchoRequest, context: StreamingResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<GRPCStatus>
/// Collects a stream of messages and returns them concatenated when the caller closes.
func collect(context: UnaryResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<(StreamEvent<Echo_EchoRequest>) -> Void>
/// Streams back messages as they are received in an input stream.
func update(context: StreamingResponseCallContext<Echo_EchoResponse>) -> EventLoopFuture<(StreamEvent<Echo_EchoRequest>) -> Void>
}

extension Echo_EchoProvider {
internal var serviceName: String { return "echo.Echo" }

/// Determines, calls and returns the appropriate request handler, depending on the request's method.
/// Returns nil for methods not handled by this service.
internal func handleMethod(_ methodName: String, callHandlerContext: CallHandlerContext) -> GRPCCallHandler? {
switch methodName {
case "Get":
return UnaryCallHandler(callHandlerContext: callHandlerContext) { context in
return { request in
self.get(request: request, context: context)
}
}

case "Expand":
return ServerStreamingCallHandler(callHandlerContext: callHandlerContext) { context in
return { request in
self.expand(request: request, context: context)
}
}

case "Collect":
return ClientStreamingCallHandler(callHandlerContext: callHandlerContext) { context in
return self.collect(context: context)
}

case "Update":
return BidirectionalStreamingCallHandler(callHandlerContext: callHandlerContext) { context in
return self.update(context: context)
}

default: return nil
}
}
}


// Provides conformance to `GRPCPayload`
extension Echo_EchoRequest: GRPCProtobufPayload {}
extension Echo_EchoResponse: GRPCProtobufPayload {}
1 change: 1 addition & 0 deletions dev/codegen-tests/01-echo/proto/echo.proto
55 changes: 55 additions & 0 deletions dev/codegen-tests/02-multifile/generate-and-diff.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/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}] all_at_once"
prepare

protoc \
--proto_path="${PROTO_DIR}" \
--plugin="${PROTOC_GEN_GRPC_SWIFT}" \
--grpc-swift_out="${OUTPUT_DIR}" \
"${PROTO_DIR}"/*.proto

validate
}

function one_at_a_time {
echo "[${TEST}] one_at_a_time"
prepare

protoc \
--proto_path="${PROTO_DIR}" \
--plugin="${PROTOC_GEN_GRPC_SWIFT}" \
--grpc-swift_out="${OUTPUT_DIR}" \
"${PROTO_DIR}"/a.proto

protoc \
--proto_path="${PROTO_DIR}" \
--plugin="${PROTOC_GEN_GRPC_SWIFT}" \
--grpc-swift_out="${OUTPUT_DIR}" \
"${PROTO_DIR}"/b.proto

validate
}

one_at_a_time
all_at_once
Loading