Skip to content

Commit 187b609

Browse files
Adding support for v1alpha reflection (#1703)
Motivation: v1alpha reflection is also used by GRPC users, so we should provide support for it. Modifications: - Generated the .grpc and .pb files for the v1alpha reflection. - Added an extension for the ReflectionService that represents the version, that is set by the users. - Created an enum to represent the 2 possible Reflection Service Providers and changed the methods that were calling provider's methods to switch between the possible cases. Result: Users will be able to set the version for the Reflection Service that they use, v1alpha becoming a possibility.
1 parent cddc12d commit 187b609

17 files changed

+3049
-414
lines changed

Makefile

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,42 +131,70 @@ ${SERIALIZATION_GRPC_REFLECTION}: ${ECHO_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
131131
.PHONY:
132132
generate-reflection-data: ${SERIALIZATION_GRPC_REFLECTION}
133133

134-
REFLECTION_PROTO=Sources/GRPCReflectionService/Model/reflection.proto
135-
REFLECTION_PB=$(REFLECTION_PROTO:.proto=.pb.swift)
136-
REFLECTION_GRPC=$(REFLECTION_PROTO:.proto=.grpc.swift)
134+
REFLECTION_V1_PROTO=Sources/GRPCReflectionService/v1/reflection-v1.proto
135+
REFLECTION_V1_PB=$(REFLECTION_V1_PROTO:.proto=.pb.swift)
136+
REFLECTION_V1_GRPC=$(REFLECTION_V1_PROTO:.proto=.grpc.swift)
137+
138+
REFLECTION_V1ALPHA_PROTO=Sources/GRPCReflectionService/v1Alpha/reflection-v1alpha.proto
139+
REFLECTION_V1ALPHA_PB=$(REFLECTION_V1ALPHA_PROTO:.proto=.pb.swift)
140+
REFLECTION_V1ALPHA_GRPC=$(REFLECTION_V1ALPHA_PROTO:.proto=.grpc.swift)
137141

138142
# For Reflection we'll generate only the Server code.
139-
${REFLECTION_GRPC}: ${REFLECTION_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
143+
${REFLECTION_V1_GRPC}: ${REFLECTION_V1_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
140144
protoc $< \
141145
--proto_path=$(dir $<) \
142146
--plugin=${PROTOC_GEN_GRPC_SWIFT} \
143147
--grpc-swift_opt=Client=false \
144148
--grpc-swift_out=$(dir $<)
145149

150+
# For Reflection we'll generate only the Server code.
151+
${REFLECTION_V1ALPHA_GRPC}: ${REFLECTION_V1ALPHA_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
152+
protoc $< \
153+
--proto_path=$(dir $<) \
154+
--plugin=${PROTOC_GEN_GRPC_SWIFT} \
155+
--grpc-swift_opt=Client=false \
156+
--grpc-swift_out=$(dir $<)
157+
146158
# Generates protobufs and gRPC server for the Reflection Service
147159
.PHONY:
148-
generate-reflection: ${REFLECTION_PB} ${REFLECTION_GRPC}
160+
generate-reflection: ${REFLECTION_V1_PB} ${REFLECTION_V1_GRPC} ${REFLECTION_V1ALPHA_PB} ${REFLECTION_V1ALPHA_GRPC}
149161

150-
TEST_REFLECTION_GRPC=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/reflection.grpc.swift
151-
TEST_REFLECTION_PB=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/reflection.pb.swift
162+
TEST_REFLECTION_V1_GRPC=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/v1/reflection-v1.grpc.swift
163+
TEST_REFLECTION_V1_PB=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/v1/reflection-v1.pb.swift
164+
TEST_REFLECTION_V1ALPHA_GRPC=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/v1Alpha/reflection-v1alpha.grpc.swift
165+
TEST_REFLECTION_V1ALPHA_PB=Tests/GRPCTests/GRPCReflectionServiceTests/Generated/v1Alpha/reflection-v1alpha.pb.swift
152166

153-
# For Reflection we'll generate only the Server code.
154-
${TEST_REFLECTION_GRPC}: ${REFLECTION_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
167+
# For Testing the Reflection we'll generate only the Client code.
168+
${TEST_REFLECTION_V1_GRPC}: ${REFLECTION_V1_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
169+
protoc $< \
170+
--proto_path=$(dir $<) \
171+
--plugin=${PROTOC_GEN_GRPC_SWIFT} \
172+
--grpc-swift_opt=Client=true,Server=false \
173+
--grpc-swift_out=$(dir ${TEST_REFLECTION_V1_GRPC})
174+
175+
${TEST_REFLECTION_V1_PB}: ${REFLECTION_V1_PROTO} ${PROTOC_GEN_SWIFT}
176+
protoc $< \
177+
--proto_path=$(dir $<) \
178+
--plugin=${PROTOC_GEN_SWIFT} \
179+
--swift_out=$(dir ${TEST_REFLECTION_V1_PB})
180+
181+
# For Testing the Reflection we'll generate only the Client code.
182+
${TEST_REFLECTION_V1ALPHA_GRPC}: ${REFLECTION_V1ALPHA_PROTO} ${PROTOC_GEN_GRPC_SWIFT}
155183
protoc $< \
156184
--proto_path=$(dir $<) \
157185
--plugin=${PROTOC_GEN_GRPC_SWIFT} \
158186
--grpc-swift_opt=Client=true,Server=false \
159-
--grpc-swift_out=$(dir ${TEST_REFLECTION_GRPC})
187+
--grpc-swift_out=$(dir ${TEST_REFLECTION_V1ALPHA_GRPC})
160188

161-
${TEST_REFLECTION_PB}: ${REFLECTION_PROTO} ${PROTOC_GEN_SWIFT}
189+
${TEST_REFLECTION_V1ALPHA_PB}: ${REFLECTION_V1ALPHA_PROTO} ${PROTOC_GEN_SWIFT}
162190
protoc $< \
163191
--proto_path=$(dir $<) \
164192
--plugin=${PROTOC_GEN_SWIFT} \
165-
--swift_out=$(dir ${TEST_REFLECTION_PB})
193+
--swift_out=$(dir ${TEST_REFLECTION_V1ALPHA_PB})
166194

167-
# Generates protobufs and gRPC client for the Reflection Service Tests
195+
# Generates protobufs and gRPC clients for the Reflection Service Tests
168196
.PHONY:
169-
generate-reflection-client: ${TEST_REFLECTION_PB} ${TEST_REFLECTION_GRPC}
197+
generate-reflection-test-clients: ${TEST_REFLECTION_V1_PB} ${TEST_REFLECTION_V1_GRPC} ${TEST_REFLECTION_V1ALPHA_PB} ${TEST_REFLECTION_V1ALPHA_GRPC}
170198

171199
### Testing ####################################################################
172200

Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,8 @@ extension Target {
455455
],
456456
path: "Sources/GRPCReflectionService",
457457
exclude: [
458-
"Model/reflection.proto",
458+
"v1/reflection-v1.proto",
459+
"v1Alpha/reflection-v1alpha.proto"
459460
]
460461
)
461462
}

Sources/GRPCReflectionService/Server/ReflectionService.swift

Lines changed: 57 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,15 @@ import SwiftProtobuf
2121

2222
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
2323
public final class ReflectionService: CallHandlerProvider, Sendable {
24-
private let reflectionService: ReflectionServiceProvider
24+
private let provider: Provider
25+
2526
public var serviceName: Substring {
26-
self.reflectionService.serviceName
27+
switch self.provider {
28+
case .v1(let provider):
29+
return provider.serviceName
30+
case .v1Alpha(let provider):
31+
return provider.serviceName
32+
}
2733
}
2834

2935
/// Creates a `ReflectionService` by loading serialized reflection data created by `protoc-gen-grpc-swift`.
@@ -33,26 +39,49 @@ public final class ReflectionService: CallHandlerProvider, Sendable {
3339
/// current working directory.
3440
///
3541
/// - Parameter filePaths: The paths to files containing serialized reflection data.
42+
/// - Parameter version: The version of the reflection service to create.
3643
///
3744
/// - Throws: When a file can't be read from disk or parsed.
38-
public init(serializedFileDescriptorProtoFilePaths filePaths: [String]) throws {
45+
public init(serializedFileDescriptorProtoFilePaths filePaths: [String], version: Version) throws {
3946
let fileDescriptorProtos = try ReflectionService.readSerializedFileDescriptorProtos(
4047
atPaths: filePaths
4148
)
42-
self.reflectionService = try ReflectionServiceProvider(
43-
fileDescriptorProtos: fileDescriptorProtos
44-
)
49+
switch version.wrapped {
50+
case .v1:
51+
self.provider = .v1(
52+
try ReflectionServiceProviderV1(fileDescriptorProtos: fileDescriptorProtos)
53+
)
54+
case .v1Alpha:
55+
self.provider = .v1Alpha(
56+
try ReflectionServiceProviderV1Alpha(fileDescriptorProtos: fileDescriptorProtos)
57+
)
58+
}
4559
}
4660

47-
public init(fileDescriptors: [Google_Protobuf_FileDescriptorProto]) throws {
48-
self.reflectionService = try ReflectionServiceProvider(fileDescriptorProtos: fileDescriptors)
61+
public init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto], version: Version) throws
62+
{
63+
switch version.wrapped {
64+
case .v1:
65+
self.provider = .v1(
66+
try ReflectionServiceProviderV1(fileDescriptorProtos: fileDescriptorProtos)
67+
)
68+
case .v1Alpha:
69+
self.provider = .v1Alpha(
70+
try ReflectionServiceProviderV1Alpha(fileDescriptorProtos: fileDescriptorProtos)
71+
)
72+
}
4973
}
5074

5175
public func handle(
5276
method name: Substring,
5377
context: GRPC.CallHandlerContext
5478
) -> GRPC.GRPCServerHandlerProtocol? {
55-
self.reflectionService.handle(method: name, context: context)
79+
switch self.provider {
80+
case .v1(let reflectionV1Provider):
81+
return reflectionV1Provider.handle(method: name, context: context)
82+
case .v1Alpha(let reflectionV1AlphaProvider):
83+
return reflectionV1AlphaProvider.handle(method: name, context: context)
84+
}
5685
}
5786
}
5887

@@ -222,163 +251,6 @@ internal struct ReflectionServiceData: Sendable {
222251
}
223252
}
224253

225-
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
226-
internal final class ReflectionServiceProvider: Grpc_Reflection_V1_ServerReflectionAsyncProvider {
227-
private let protoRegistry: ReflectionServiceData
228-
229-
internal init(fileDescriptorProtos: [Google_Protobuf_FileDescriptorProto]) throws {
230-
self.protoRegistry = try ReflectionServiceData(
231-
fileDescriptors: fileDescriptorProtos
232-
)
233-
}
234-
235-
internal func _findFileByFileName(
236-
_ fileName: String
237-
) -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
238-
return self.protoRegistry
239-
.serialisedFileDescriptorProtosForDependenciesOfFile(named: fileName)
240-
.map { fileDescriptorProtos in
241-
Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.fileDescriptorResponse(
242-
.with {
243-
$0.fileDescriptorProto = fileDescriptorProtos
244-
}
245-
)
246-
}
247-
}
248-
249-
internal func findFileByFileName(
250-
_ fileName: String,
251-
request: Grpc_Reflection_V1_ServerReflectionRequest
252-
) -> Grpc_Reflection_V1_ServerReflectionResponse {
253-
let result = self._findFileByFileName(fileName)
254-
return result.makeResponse(request: request)
255-
}
256-
257-
internal func getServicesNames(
258-
request: Grpc_Reflection_V1_ServerReflectionRequest
259-
) throws -> Grpc_Reflection_V1_ServerReflectionResponse {
260-
var listServicesResponse = Grpc_Reflection_V1_ListServiceResponse()
261-
listServicesResponse.service = self.protoRegistry.serviceNames.map { serviceName in
262-
Grpc_Reflection_V1_ServiceResponse.with {
263-
$0.name = serviceName
264-
}
265-
}
266-
return Grpc_Reflection_V1_ServerReflectionResponse(
267-
request: request,
268-
messageResponse: .listServicesResponse(listServicesResponse)
269-
)
270-
}
271-
272-
internal func findFileBySymbol(
273-
_ symbolName: String,
274-
request: Grpc_Reflection_V1_ServerReflectionRequest
275-
) -> Grpc_Reflection_V1_ServerReflectionResponse {
276-
let result = self.protoRegistry.nameOfFileContainingSymbol(
277-
named: symbolName
278-
).flatMap {
279-
self._findFileByFileName($0)
280-
}
281-
return result.makeResponse(request: request)
282-
}
283-
284-
internal func findFileByExtension(
285-
extensionRequest: Grpc_Reflection_V1_ExtensionRequest,
286-
request: Grpc_Reflection_V1_ServerReflectionRequest
287-
) -> Grpc_Reflection_V1_ServerReflectionResponse {
288-
let result = self.protoRegistry.nameOfFileContainingExtension(
289-
extendeeName: extensionRequest.containingType,
290-
fieldNumber: extensionRequest.extensionNumber
291-
).flatMap {
292-
self._findFileByFileName($0)
293-
}
294-
return result.makeResponse(request: request)
295-
}
296-
297-
internal func findExtensionsFieldNumbersOfType(
298-
named typeName: String,
299-
request: Grpc_Reflection_V1_ServerReflectionRequest
300-
) -> Grpc_Reflection_V1_ServerReflectionResponse {
301-
let result = self.protoRegistry.extensionsFieldNumbersOfType(
302-
named: typeName
303-
).map { fieldNumbers in
304-
Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse.allExtensionNumbersResponse(
305-
Grpc_Reflection_V1_ExtensionNumberResponse.with {
306-
$0.baseTypeName = typeName
307-
$0.extensionNumber = fieldNumbers
308-
}
309-
)
310-
}
311-
return result.makeResponse(request: request)
312-
}
313-
314-
internal func serverReflectionInfo(
315-
requestStream: GRPCAsyncRequestStream<Grpc_Reflection_V1_ServerReflectionRequest>,
316-
responseStream: GRPCAsyncResponseStreamWriter<Grpc_Reflection_V1_ServerReflectionResponse>,
317-
context: GRPCAsyncServerCallContext
318-
) async throws {
319-
for try await request in requestStream {
320-
switch request.messageRequest {
321-
case let .fileByFilename(fileName):
322-
let response = self.findFileByFileName(
323-
fileName,
324-
request: request
325-
)
326-
try await responseStream.send(response)
327-
328-
case .listServices:
329-
let response = try self.getServicesNames(request: request)
330-
try await responseStream.send(response)
331-
332-
case let .fileContainingSymbol(symbolName):
333-
let response = self.findFileBySymbol(
334-
symbolName,
335-
request: request
336-
)
337-
try await responseStream.send(response)
338-
339-
case let .fileContainingExtension(extensionRequest):
340-
let response = self.findFileByExtension(
341-
extensionRequest: extensionRequest,
342-
request: request
343-
)
344-
try await responseStream.send(response)
345-
346-
case let .allExtensionNumbersOfType(typeName):
347-
let response = self.findExtensionsFieldNumbersOfType(
348-
named: typeName,
349-
request: request
350-
)
351-
try await responseStream.send(response)
352-
353-
default:
354-
let response = Grpc_Reflection_V1_ServerReflectionResponse(
355-
request: request,
356-
messageResponse: .errorResponse(
357-
Grpc_Reflection_V1_ErrorResponse.with {
358-
$0.errorCode = Int32(GRPCStatus.Code.unimplemented.rawValue)
359-
$0.errorMessage = "The request is not implemented."
360-
}
361-
)
362-
)
363-
try await responseStream.send(response)
364-
}
365-
}
366-
}
367-
}
368-
369-
extension Grpc_Reflection_V1_ServerReflectionResponse {
370-
init(
371-
request: Grpc_Reflection_V1_ServerReflectionRequest,
372-
messageResponse: Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse
373-
) {
374-
self = .with {
375-
$0.validHost = request.host
376-
$0.originalRequest = request
377-
$0.messageResponse = messageResponse
378-
}
379-
}
380-
}
381-
382254
extension Google_Protobuf_FileDescriptorProto {
383255
var qualifiedServiceAndMethodNames: [String] {
384256
var names: [String] = []
@@ -413,35 +285,29 @@ extension Google_Protobuf_FileDescriptorProto {
413285
}
414286
}
415287

416-
extension Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, GRPCStatus> {
417-
func recover() -> Result<Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse, Never>
418-
{
419-
self.flatMapError { status in
420-
let error = Grpc_Reflection_V1_ErrorResponse.with {
421-
$0.errorCode = Int32(status.code.rawValue)
422-
$0.errorMessage = status.message ?? ""
423-
}
424-
return .success(.errorResponse(error))
288+
@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
289+
extension ReflectionService {
290+
/// The version of the reflection service.
291+
///
292+
/// Depending in the version you are using, when creating the ReflectionService
293+
/// provide the corresponding `Version` variable (`v1` or `v1Alpha`).
294+
public struct Version: Sendable, Hashable {
295+
internal enum Wrapped {
296+
case v1
297+
case v1Alpha
425298
}
426-
}
299+
var wrapped: Wrapped
300+
private init(_ wrapped: Wrapped) { self.wrapped = wrapped }
427301

428-
func makeResponse(
429-
request: Grpc_Reflection_V1_ServerReflectionRequest
430-
) -> Grpc_Reflection_V1_ServerReflectionResponse {
431-
let result = self.recover().attachRequest(request)
432-
// Safe to '!' as the failure type is 'Never'.
433-
return try! result.get()
302+
/// The v1 version of reflection service: https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1/reflection.proto.
303+
public static var v1: Self { Self(.v1) }
304+
/// The v1alpha version of reflection service: https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto.
305+
public static var v1Alpha: Self { Self(.v1Alpha) }
434306
}
435-
}
436307

437-
extension Result
438-
where Success == Grpc_Reflection_V1_ServerReflectionResponse.OneOf_MessageResponse {
439-
func attachRequest(
440-
_ request: Grpc_Reflection_V1_ServerReflectionRequest
441-
) -> Result<Grpc_Reflection_V1_ServerReflectionResponse, Failure> {
442-
self.map { message in
443-
Grpc_Reflection_V1_ServerReflectionResponse(request: request, messageResponse: message)
444-
}
308+
private enum Provider {
309+
case v1(ReflectionServiceProviderV1)
310+
case v1Alpha(ReflectionServiceProviderV1Alpha)
445311
}
446312
}
447313

0 commit comments

Comments
 (0)