From ac95e4b210050246af0dced68c7d11b58efcd164 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Thu, 21 Dec 2023 11:19:46 +0200 Subject: [PATCH 1/3] [CodeGenLib] Typealias translator configuration for client and server typealiases Motivation: The typealias translator should generate typealias for the client and server protocols only if the associated code (client/server code) will be generated as well. Also, at the moment the Typealias translator doesn't generate the ClientProtocol typealias. Modifications: - added a new initialiser for the TypealiasTranslator that sets the client and server property. - Based on the client and server properties' values the typealias translator generates or not the protocol typealiases. - created method gor client protocol typalias generation. - modified tests, so they include the client code. - added new tests for generating only client or server code. Result: The typealias translator will generate typealiases for client and server protocols only when the associated code is also generated, getting rid of the possibility of an error (when the protocol doesn't exist). --- .../IDLToStructuredSwiftTranslator.swift | 5 +- .../Translator/TypealiasTranslator.swift | 36 ++++- .../SnippetBasedTranslatorTests.swift | 139 ++++++++++++++++-- 3 files changed, 160 insertions(+), 20 deletions(-) diff --git a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift index 3870c530b..7ab59762d 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/IDLToStructuredSwiftTranslator.swift @@ -15,20 +15,19 @@ */ struct IDLToStructuredSwiftTranslator: Translator { - private let typealiasTranslator = TypealiasTranslator() - func translate( codeGenerationRequest: CodeGenerationRequest, client: Bool, server: Bool ) throws -> StructuredSwiftRepresentation { + let typealiasTranslator = TypealiasTranslator(client: client, server: server) let topComment = Comment.doc(codeGenerationRequest.leadingTrivia) let imports: [ImportDescription] = [ ImportDescription(moduleName: "GRPCCore") ] var codeBlocks: [CodeBlock] = [] codeBlocks.append( - contentsOf: try self.typealiasTranslator.translate(from: codeGenerationRequest) + contentsOf: try typealiasTranslator.translate(from: codeGenerationRequest) ) let fileDescription = FileDescription( diff --git a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift index 1737b38ae..22976117a 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift @@ -53,6 +53,14 @@ /// A ``CodeGenerationRequest`` can contain multiple namespaces, so the TypealiasTranslator will create a ``CodeBlock`` /// for each namespace. struct TypealiasTranslator: SpecializedTranslator { + let client: Bool + let server: Bool + + init(client: Bool, server: Bool) { + self.client = client + self.server = server + } + func translate(from codeGenerationRequest: CodeGenerationRequest) throws -> [CodeBlock] { var codeBlocks: [CodeBlock] = [] let services = codeGenerationRequest.services @@ -169,9 +177,16 @@ extension TypealiasTranslator { let methodDescriptorsDeclaration = self.makeMethodDescriptors(for: service) serviceEnum.members.append(methodDescriptorsDeclaration) - // Create the streaming and non-streaming service protocol type aliases. - let serviceProtocols = self.makeServiceProtocolsTypealiases(for: service) - serviceEnum.members.append(contentsOf: serviceProtocols) + if self.server { + // Create the streaming and non-streaming service protocol type aliases. + let serviceProtocols = self.makeServiceProtocolsTypealiases(for: service) + serviceEnum.members.append(contentsOf: serviceProtocols) + } + + if self.client { + let clientProtocol = self.makeClientProtocolTypealias(for: service) + serviceEnum.members.append(clientProtocol) + } return .enum(serviceEnum) } @@ -307,4 +322,19 @@ extension TypealiasTranslator { return [streamingServiceProtocolTypealias, serviceProtocolTypealias] } + + private func makeClientProtocolTypealias( + for service: CodeGenerationRequest.ServiceDescriptor + ) -> Declaration { + let namespacedPrefix: String + if service.namespace.isEmpty { + namespacedPrefix = service.name + } else { + namespacedPrefix = "\(service.namespace)_\(service.name)" + } + return .typealias( + name: "ClientProtocol", + existingType: .member(["\(namespacedPrefix)ClientProtocol"]) + ) + } } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift index 87954c8eb..a9f6a23f9 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift @@ -58,13 +58,97 @@ final class SnippetBasedTranslatorTests: XCTestCase { ] typealias StreamingServiceProtocol = namespaceA_ServiceAServiceStreamingProtocol typealias ServiceProtocol = namespaceA_ServiceAServiceProtocol + typealias ClientProtocol = namespaceA_ServiceAClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true + ) + } + + func testTypealiasTranslatorNoMethodsServiceClientAndServer() throws { + let service = ServiceDescriptor( + documentation: "Documentation for ServiceA", + name: "ServiceA", + namespace: "namespaceA", + methods: [] + ) + let expectedSwift = + """ + enum namespaceA { + enum ServiceA { + enum Methods {} + static let methods: [MethodDescriptor] = [] + typealias StreamingServiceProtocol = namespaceA_ServiceAServiceStreamingProtocol + typealias ServiceProtocol = namespaceA_ServiceAServiceProtocol + typealias ClientProtocol = namespaceA_ServiceAClientProtocol + } + } + """ + + try self.assertTypealiasTranslation( + codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), + expectedSwift: expectedSwift, + client: true, + server: true + ) + } + + func testTypealiasTranslatorServer() throws { + let service = ServiceDescriptor( + documentation: "Documentation for ServiceA", + name: "ServiceA", + namespace: "namespaceA", + methods: [] + ) + let expectedSwift = + """ + enum namespaceA { + enum ServiceA { + enum Methods {} + static let methods: [MethodDescriptor] = [] + typealias StreamingServiceProtocol = namespaceA_ServiceAServiceStreamingProtocol + typealias ServiceProtocol = namespaceA_ServiceAServiceProtocol + } + } + """ + + try self.assertTypealiasTranslation( + codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), + expectedSwift: expectedSwift, + client: false, + server: true + ) + } + + func testTypealiasTranslatorClient() throws { + let service = ServiceDescriptor( + documentation: "Documentation for ServiceA", + name: "ServiceA", + namespace: "namespaceA", + methods: [] + ) + let expectedSwift = + """ + enum namespaceA { + enum ServiceA { + enum Methods {} + static let methods: [MethodDescriptor] = [] + typealias ClientProtocol = namespaceA_ServiceAClientProtocol + } + } + """ + + try self.assertTypealiasTranslation( + codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), + expectedSwift: expectedSwift, + client: true, + server: false ) } @@ -101,12 +185,15 @@ final class SnippetBasedTranslatorTests: XCTestCase { ] typealias StreamingServiceProtocol = ServiceAServiceStreamingProtocol typealias ServiceProtocol = ServiceAServiceProtocol + typealias ClientProtocol = ServiceAClientProtocol } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -161,13 +248,16 @@ final class SnippetBasedTranslatorTests: XCTestCase { ] typealias StreamingServiceProtocol = namespaceA_ServiceAServiceStreamingProtocol typealias ServiceProtocol = namespaceA_ServiceAServiceProtocol + typealias ClientProtocol = namespaceA_ServiceAClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -186,13 +276,16 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = namespaceA_ServiceAServiceStreamingProtocol typealias ServiceProtocol = namespaceA_ServiceAServiceProtocol + typealias ClientProtocol = namespaceA_ServiceAClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -219,19 +312,23 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = namespacea_AServiceServiceStreamingProtocol typealias ServiceProtocol = namespacea_AServiceServiceProtocol + typealias ClientProtocol = namespacea_AServiceClientProtocol } enum BService { enum Methods {} static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = namespacea_BServiceServiceStreamingProtocol typealias ServiceProtocol = namespacea_BServiceServiceProtocol + typealias ClientProtocol = namespacea_BServiceClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [serviceB, serviceA]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -257,18 +354,22 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = AServiceServiceStreamingProtocol typealias ServiceProtocol = AServiceServiceProtocol + typealias ClientProtocol = AServiceClientProtocol } enum BService { enum Methods {} static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = BServiceServiceStreamingProtocol typealias ServiceProtocol = BServiceServiceProtocol + typealias ClientProtocol = BServiceClientProtocol } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [serviceB, serviceA]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -295,6 +396,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = anamespace_AServiceServiceStreamingProtocol typealias ServiceProtocol = anamespace_AServiceServiceProtocol + typealias ClientProtocol = anamespace_AServiceClientProtocol } } enum bnamespace { @@ -303,13 +405,16 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = bnamespace_BServiceServiceStreamingProtocol typealias ServiceProtocol = bnamespace_BServiceServiceProtocol + typealias ClientProtocol = bnamespace_BServiceClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [serviceB, serviceA]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -333,6 +438,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = BServiceServiceStreamingProtocol typealias ServiceProtocol = BServiceServiceProtocol + typealias ClientProtocol = BServiceClientProtocol } enum anamespace { enum AService { @@ -340,13 +446,16 @@ final class SnippetBasedTranslatorTests: XCTestCase { static let methods: [MethodDescriptor] = [] typealias StreamingServiceProtocol = anamespace_AServiceServiceStreamingProtocol typealias ServiceProtocol = anamespace_AServiceServiceProtocol + typealias ClientProtocol = anamespace_AServiceClientProtocol } } """ try self.assertTypealiasTranslation( codeGenerationRequest: self.makeCodeGenerationRequest(services: [serviceA, serviceB]), - expectedSwift: expectedSwift + expectedSwift: expectedSwift, + client: true, + server: true ) } @@ -359,7 +468,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { ) let codeGenerationRequest = self.makeCodeGenerationRequest(services: [serviceA, serviceA]) - let translator = TypealiasTranslator() + let translator = TypealiasTranslator(client: true, server: true) self.assertThrowsError( ofType: CodeGenError.self, try translator.translate(from: codeGenerationRequest) @@ -387,7 +496,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { ) let codeGenerationRequest = self.makeCodeGenerationRequest(services: [serviceA, serviceA]) - let translator = TypealiasTranslator() + let translator = TypealiasTranslator(client: true, server: true) self.assertThrowsError( ofType: CodeGenError.self, try translator.translate(from: codeGenerationRequest) @@ -423,7 +532,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { ) let codeGenerationRequest = self.makeCodeGenerationRequest(services: [service]) - let translator = TypealiasTranslator() + let translator = TypealiasTranslator(client: true, server: true) self.assertThrowsError( ofType: CodeGenError.self, try translator.translate(from: codeGenerationRequest) @@ -456,7 +565,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { methods: [] ) let codeGenerationRequest = self.makeCodeGenerationRequest(services: [serviceA, serviceB]) - let translator = TypealiasTranslator() + let translator = TypealiasTranslator(client: true, server: true) self.assertThrowsError( ofType: CodeGenError.self, try translator.translate(from: codeGenerationRequest) @@ -479,9 +588,11 @@ final class SnippetBasedTranslatorTests: XCTestCase { extension SnippetBasedTranslatorTests { private func assertTypealiasTranslation( codeGenerationRequest: CodeGenerationRequest, - expectedSwift: String + expectedSwift: String, + client: Bool, + server: Bool ) throws { - let translator = TypealiasTranslator() + let translator = TypealiasTranslator(client: client, server: server) let codeBlocks = try translator.translate(from: codeGenerationRequest) let renderer = TextBasedRenderer.default renderer.renderCodeBlocks(codeBlocks) From e372e78d1e218670214834c9d3239e4a5194d611 Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Thu, 21 Dec 2023 11:42:37 +0200 Subject: [PATCH 2/3] added no server no client code test --- .../Translator/TypealiasTranslator.swift | 1 + .../SnippetBasedTranslatorTests.swift | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift index 22976117a..9a5c65c1e 100644 --- a/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift +++ b/Sources/GRPCCodeGen/Internal/Translator/TypealiasTranslator.swift @@ -184,6 +184,7 @@ extension TypealiasTranslator { } if self.client { + // Create the client protocol type alias. let clientProtocol = self.makeClientProtocolTypealias(for: service) serviceEnum.members.append(clientProtocol) } diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift index a9f6a23f9..0426315ba 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift @@ -152,6 +152,31 @@ final class SnippetBasedTranslatorTests: XCTestCase { ) } + func testTypealiasTranslatorNoClientNoServer() throws { + let service = ServiceDescriptor( + documentation: "Documentation for ServiceA", + name: "ServiceA", + namespace: "namespaceA", + methods: [] + ) + let expectedSwift = + """ + enum namespaceA { + enum ServiceA { + enum Methods {} + static let methods: [MethodDescriptor] = [] + } + } + """ + + try self.assertTypealiasTranslation( + codeGenerationRequest: self.makeCodeGenerationRequest(services: [service]), + expectedSwift: expectedSwift, + client: false, + server: false + ) + } + func testTypealiasTranslatorEmptyNamespace() throws { let method = MethodDescriptor( documentation: "Documentation for MethodA", From 237d80e30633b96377eb620ae17ad03312cff81c Mon Sep 17 00:00:00 2001 From: Stefana Dranca Date: Thu, 21 Dec 2023 11:56:08 +0200 Subject: [PATCH 3/3] fixed formatting --- .../Internal/Translator/SnippetBasedTranslatorTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift index 0426315ba..11f1d9cae 100644 --- a/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift +++ b/Tests/GRPCCodeGenTests/Internal/Translator/SnippetBasedTranslatorTests.swift @@ -176,7 +176,7 @@ final class SnippetBasedTranslatorTests: XCTestCase { server: false ) } - + func testTypealiasTranslatorEmptyNamespace() throws { let method = MethodDescriptor( documentation: "Documentation for MethodA",