From 6ad092b195e820bc3013feacd4c2681a6440f681 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 18 Feb 2021 11:33:35 +0100 Subject: [PATCH 1/8] async/await support --- Package.swift | 4 +- Sources/AWSLambdaRuntime/Lambda+Codable.swift | 63 +++++++++++++++++++ .../AWSLambdaRuntimeCore/LambdaHandler.swift | 33 ++++++++++ 3 files changed, 98 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 4f6ebc22..398a32fd 100644 --- a/Package.swift +++ b/Package.swift @@ -24,12 +24,12 @@ let package = Package( .byName(name: "AWSLambdaRuntimeCore"), .product(name: "NIO", package: "swift-nio"), .product(name: "NIOFoundationCompat", package: "swift-nio"), - ]), + ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), .target(name: "AWSLambdaRuntimeCore", dependencies: [ .product(name: "Logging", package: "swift-log"), .product(name: "Backtrace", package: "swift-backtrace"), .product(name: "NIOHTTP1", package: "swift-nio"), - ]), + ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), .testTarget(name: "AWSLambdaRuntimeCoreTests", dependencies: [ .byName(name: "AWSLambdaRuntimeCore"), .product(name: "NIOTestUtils", package: "swift-nio"), diff --git a/Sources/AWSLambdaRuntime/Lambda+Codable.swift b/Sources/AWSLambdaRuntime/Lambda+Codable.swift index 620c52e2..217d4953 100644 --- a/Sources/AWSLambdaRuntime/Lambda+Codable.swift +++ b/Sources/AWSLambdaRuntime/Lambda+Codable.swift @@ -78,6 +78,69 @@ internal struct CodableVoidClosureWrapper: LambdaHandler { } } +// MARK: - Async + +extension Lambda { + + /// An async Lambda Closure that takes a `In: Decodable` and returns an `Out: Encodable` + public typealias CodableAsyncClosure = (Lambda.Context, In) async throws -> Out + + /// Run a Lambda defined by implementing the `CodableAsyncClosure` function. + /// + /// - parameters: + /// - closure: `CodableAsyncClosure` based Lambda. + /// + /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. + public static func run(_ closure: @escaping CodableAsyncClosure) { + self.run(CodableAsyncWrapper(closure)) + } + + /// An asynchronous Lambda Closure that takes a `In: Decodable` and returns nothing. + public typealias CodableVoidAsyncClosure = (Lambda.Context, In) async throws -> () + + /// Run a Lambda defined by implementing the `CodableVoidAsyncClosure` function. + /// + /// - parameters: + /// - closure: `CodableVoidAsyncClosure` based Lambda. + /// + /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. + public static func run(_ closure: @escaping CodableVoidAsyncClosure) { + self.run(CodableVoidAsyncWrapper(closure)) + } +} + +internal struct CodableAsyncWrapper: AsyncLambdaHandler { + typealias In = In + typealias Out = Out + + private let closure: Lambda.CodableAsyncClosure + + init(_ closure: @escaping Lambda.CodableAsyncClosure) { + self.closure = closure + } + + func handle(context: Lambda.Context, event: In) async throws -> Out { + try await self.closure(context, event) + } +} + +internal struct CodableVoidAsyncWrapper: AsyncLambdaHandler { + typealias In = In + typealias Out = Void + + private let closure: Lambda.CodableVoidAsyncClosure + + init(_ closure: @escaping Lambda.CodableVoidAsyncClosure) { + self.closure = closure + } + + func handle(context: Lambda.Context, event: In) async throws -> Void { + try await self.closure(context, event) + } +} + +// MARK: - Codable support + /// Implementation of a`ByteBuffer` to `In` decoding extension EventLoopLambdaHandler where In: Decodable { @inlinable diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 008620d8..9e8a4a84 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -85,6 +85,39 @@ extension LambdaHandler { } } +// MARK: - AsyncLambdaHandler + +/// Strongly typed, processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` async. +public protocol AsyncLambdaHandler: EventLoopLambdaHandler { + + /// The Lambda handling method + /// Concrete Lambda handlers implement this method to provide the Lambda functionality. + /// + /// - parameters: + /// - context: Runtime `Context`. + /// - event: Event of type `In` representing the event or request. + /// + /// - Returns: A Lambda result ot type `Out`. + func handle(context: Lambda.Context, event: In) async throws -> Out +} + +extension AsyncLambdaHandler { + public func handle(context: Lambda.Context, event: In) -> EventLoopFuture { + @asyncHandler func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { + do { + let result = try await handle(context: context, event: event) + promise.succeed(result) + } catch { + promise.fail(error) + } + } + + let promise = context.eventLoop.makePromise(of: Out.self) + _run(context: context, event: event, promise: promise) + return promise.futureResult + } +} + // MARK: - EventLoopLambdaHandler /// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` asynchronously. From 71d7805627be9de328d6c5db7c14a20b4d1cc46f Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Sun, 21 Feb 2021 12:46:21 +0100 Subject: [PATCH 2/8] Added simple tests --- Package.swift | 2 +- Sources/AWSLambdaRuntime/Lambda+Codable.swift | 2 ++ .../AWSLambdaRuntimeCore/LambdaHandler.swift | 22 ++++++------ .../Lambda+CodeableTest.swift | 35 ++++++++++++++++++- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/Package.swift b/Package.swift index 398a32fd..5dcf008e 100644 --- a/Package.swift +++ b/Package.swift @@ -38,7 +38,7 @@ let package = Package( .testTarget(name: "AWSLambdaRuntimeTests", dependencies: [ .byName(name: "AWSLambdaRuntimeCore"), .byName(name: "AWSLambdaRuntime"), - ]), + ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), .target(name: "AWSLambdaEvents", dependencies: []), .testTarget(name: "AWSLambdaEventsTests", dependencies: ["AWSLambdaEvents"]), // testing helper diff --git a/Sources/AWSLambdaRuntime/Lambda+Codable.swift b/Sources/AWSLambdaRuntime/Lambda+Codable.swift index 217d4953..67eeaaa7 100644 --- a/Sources/AWSLambdaRuntime/Lambda+Codable.swift +++ b/Sources/AWSLambdaRuntime/Lambda+Codable.swift @@ -80,6 +80,7 @@ internal struct CodableVoidClosureWrapper: LambdaHandler { // MARK: - Async +#if compiler(>=5.4) && $AsyncAwait extension Lambda { /// An async Lambda Closure that takes a `In: Decodable` and returns an `Out: Encodable` @@ -138,6 +139,7 @@ internal struct CodableVoidAsyncWrapper: AsyncLambdaHandler { try await self.closure(context, event) } } +#endif // MARK: - Codable support diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 9e8a4a84..79f6e4ef 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -87,6 +87,7 @@ extension LambdaHandler { // MARK: - AsyncLambdaHandler +#if compiler(>=5.4) && $AsyncAwait /// Strongly typed, processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` async. public protocol AsyncLambdaHandler: EventLoopLambdaHandler { @@ -103,20 +104,21 @@ public protocol AsyncLambdaHandler: EventLoopLambdaHandler { extension AsyncLambdaHandler { public func handle(context: Lambda.Context, event: In) -> EventLoopFuture { - @asyncHandler func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { - do { - let result = try await handle(context: context, event: event) - promise.succeed(result) - } catch { - promise.fail(error) - } - } - let promise = context.eventLoop.makePromise(of: Out.self) - _run(context: context, event: event, promise: promise) + self._run(context: context, event: event, promise: promise) return promise.futureResult } + + @asyncHandler private func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { + do { + let result = try await handle(context: context, event: event) + promise.succeed(result) + } catch { + promise.fail(error) + } + } } +#endif // MARK: - EventLoopLambdaHandler diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift index 9aa3f72a..bb44ef46 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift @@ -53,7 +53,7 @@ class CodableLambdaTest: XCTestCase { var response: Response? let closureWrapper = CodableClosureWrapper { (_, req: Request, completion: (Result) -> Void) in - XCTAssertEqual(request, request) + XCTAssertEqual(request, req) completion(.success(Response(requestId: req.requestId))) } @@ -62,6 +62,39 @@ class CodableLambdaTest: XCTestCase { XCTAssertNoThrow(response = try JSONDecoder().decode(Response.self, from: XCTUnwrap(outputBuffer))) XCTAssertEqual(response?.requestId, request.requestId) } + + #if compiler(>=5.4) && $AsyncAwait + func testCodableVoidAsyncWrapper() { + let request = Request(requestId: UUID().uuidString) + var inputBuffer: ByteBuffer? + var outputBuffer: ByteBuffer? + + let closureWrapper = CodableVoidAsyncWrapper { (context, req: Request) in + XCTAssertEqual(request, req) + } + + XCTAssertNoThrow(inputBuffer = try JSONEncoder().encode(request, using: self.allocator)) + XCTAssertNoThrow(outputBuffer = try closureWrapper.handle(context: self.newContext(), event: XCTUnwrap(inputBuffer)).wait()) + XCTAssertNil(outputBuffer) + } + + func testCodableAsyncWrapper() { + let request = Request(requestId: UUID().uuidString) + var inputBuffer: ByteBuffer? + var outputBuffer: ByteBuffer? + var response: Response? + + let closureWrapper = CodableAsyncWrapper { (context, req: Request) -> Response in + XCTAssertEqual(req, request) + return Response(requestId: req.requestId) + } + + XCTAssertNoThrow(inputBuffer = try JSONEncoder().encode(request, using: self.allocator)) + XCTAssertNoThrow(outputBuffer = try closureWrapper.handle(context: self.newContext(), event: XCTUnwrap(inputBuffer)).wait()) + XCTAssertNoThrow(response = try JSONDecoder().decode(Response.self, from: XCTUnwrap(outputBuffer))) + XCTAssertEqual(response?.requestId, request.requestId) + } + #endif // convencience method func newContext() -> Lambda.Context { From e3b6b7c98e2345dac4b891fd5cbf3827cd007068 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Mon, 22 Feb 2021 08:36:54 +0100 Subject: [PATCH 3/8] Async Lambda initializer --- Sources/AWSLambdaRuntimeCore/Lambda.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index 5dc27648..f1360b61 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -55,6 +55,25 @@ public enum Lambda { fatalError("\(error)") } } + + #if compiler(>=5.4) && $AsyncAwait + public static func run(_ factory: @escaping (InitializationContext) async throws -> Handler) { + self.run { context -> EventLoopFuture in + @asyncHandler func _createLambda(_ context: InitializationContext, promise: EventLoopPromise) { + do { + let handler = try await factory(context) + promise.succeed(handler) + } catch { + promise.fail(error) + } + } + + let promise = context.eventLoop.makePromise(of: Handler.self) + _createLambda(context, promise: promise) + return promise.futureResult + } + } + #endif /// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a factory, typically a constructor. /// From 469e7f8867e55c2ccff5b6a1d91d9a3decda9e52 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 1 Apr 2021 09:40:13 +0200 Subject: [PATCH 4/8] Update for Swift 5.5 --- Package.swift | 6 +++--- Sources/AWSLambdaRuntime/Lambda+Codable.swift | 9 ++++----- Sources/AWSLambdaRuntimeCore/Lambda.swift | 6 +++--- Sources/AWSLambdaRuntimeCore/LambdaHandler.swift | 5 ++--- Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift | 8 ++++---- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Package.swift b/Package.swift index 5dcf008e..4f6ebc22 100644 --- a/Package.swift +++ b/Package.swift @@ -24,12 +24,12 @@ let package = Package( .byName(name: "AWSLambdaRuntimeCore"), .product(name: "NIO", package: "swift-nio"), .product(name: "NIOFoundationCompat", package: "swift-nio"), - ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), + ]), .target(name: "AWSLambdaRuntimeCore", dependencies: [ .product(name: "Logging", package: "swift-log"), .product(name: "Backtrace", package: "swift-backtrace"), .product(name: "NIOHTTP1", package: "swift-nio"), - ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), + ]), .testTarget(name: "AWSLambdaRuntimeCoreTests", dependencies: [ .byName(name: "AWSLambdaRuntimeCore"), .product(name: "NIOTestUtils", package: "swift-nio"), @@ -38,7 +38,7 @@ let package = Package( .testTarget(name: "AWSLambdaRuntimeTests", dependencies: [ .byName(name: "AWSLambdaRuntimeCore"), .byName(name: "AWSLambdaRuntime"), - ], swiftSettings: [.unsafeFlags(["-Xfrontend", "-enable-experimental-concurrency"])]), + ]), .target(name: "AWSLambdaEvents", dependencies: []), .testTarget(name: "AWSLambdaEventsTests", dependencies: ["AWSLambdaEvents"]), // testing helper diff --git a/Sources/AWSLambdaRuntime/Lambda+Codable.swift b/Sources/AWSLambdaRuntime/Lambda+Codable.swift index 67eeaaa7..872a68fc 100644 --- a/Sources/AWSLambdaRuntime/Lambda+Codable.swift +++ b/Sources/AWSLambdaRuntime/Lambda+Codable.swift @@ -80,12 +80,11 @@ internal struct CodableVoidClosureWrapper: LambdaHandler { // MARK: - Async -#if compiler(>=5.4) && $AsyncAwait +#if compiler(>=5.5) && $AsyncAwait extension Lambda { - /// An async Lambda Closure that takes a `In: Decodable` and returns an `Out: Encodable` public typealias CodableAsyncClosure = (Lambda.Context, In) async throws -> Out - + /// Run a Lambda defined by implementing the `CodableAsyncClosure` function. /// /// - parameters: @@ -97,7 +96,7 @@ extension Lambda { } /// An asynchronous Lambda Closure that takes a `In: Decodable` and returns nothing. - public typealias CodableVoidAsyncClosure = (Lambda.Context, In) async throws -> () + public typealias CodableVoidAsyncClosure = (Lambda.Context, In) async throws -> Void /// Run a Lambda defined by implementing the `CodableVoidAsyncClosure` function. /// @@ -135,7 +134,7 @@ internal struct CodableVoidAsyncWrapper: AsyncLambdaHandler { self.closure = closure } - func handle(context: Lambda.Context, event: In) async throws -> Void { + func handle(context: Lambda.Context, event: In) async throws { try await self.closure(context, event) } } diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index f1360b61..df0bf4d6 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -55,8 +55,8 @@ public enum Lambda { fatalError("\(error)") } } - - #if compiler(>=5.4) && $AsyncAwait + + #if compiler(>=5.5) && $AsyncAwait public static func run(_ factory: @escaping (InitializationContext) async throws -> Handler) { self.run { context -> EventLoopFuture in @asyncHandler func _createLambda(_ context: InitializationContext, promise: EventLoopPromise) { @@ -67,7 +67,7 @@ public enum Lambda { promise.fail(error) } } - + let promise = context.eventLoop.makePromise(of: Handler.self) _createLambda(context, promise: promise) return promise.futureResult diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 79f6e4ef..cb468de7 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -87,10 +87,9 @@ extension LambdaHandler { // MARK: - AsyncLambdaHandler -#if compiler(>=5.4) && $AsyncAwait +#if compiler(>=5.5) && $AsyncAwait /// Strongly typed, processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` async. public protocol AsyncLambdaHandler: EventLoopLambdaHandler { - /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. /// @@ -108,7 +107,7 @@ extension AsyncLambdaHandler { self._run(context: context, event: event, promise: promise) return promise.futureResult } - + @asyncHandler private func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { do { let result = try await handle(context: context, event: event) diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift index bb44ef46..a987e287 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift @@ -62,14 +62,14 @@ class CodableLambdaTest: XCTestCase { XCTAssertNoThrow(response = try JSONDecoder().decode(Response.self, from: XCTUnwrap(outputBuffer))) XCTAssertEqual(response?.requestId, request.requestId) } - - #if compiler(>=5.4) && $AsyncAwait + + #if compiler(>=5.5) && $AsyncAwait func testCodableVoidAsyncWrapper() { let request = Request(requestId: UUID().uuidString) var inputBuffer: ByteBuffer? var outputBuffer: ByteBuffer? - let closureWrapper = CodableVoidAsyncWrapper { (context, req: Request) in + let closureWrapper = CodableVoidAsyncWrapper { (_, req: Request) in XCTAssertEqual(request, req) } @@ -84,7 +84,7 @@ class CodableLambdaTest: XCTestCase { var outputBuffer: ByteBuffer? var response: Response? - let closureWrapper = CodableAsyncWrapper { (context, req: Request) -> Response in + let closureWrapper = CodableAsyncWrapper { (_, req: Request) -> Response in XCTAssertEqual(req, request) return Response(requestId: req.requestId) } From 0578764fcea1a05cdc14b3df86d75ccd2c78c3ee Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 1 Apr 2021 11:47:32 +0200 Subject: [PATCH 5/8] Add AsyncLambdaHandler tests --- .../AWSLambdaRuntimeCore/LambdaHandler.swift | 2 +- ...ringTest.swift => LambdaHandlerTest.swift} | 71 ++++++++++++++++++- docker/docker-compose.al2.main.yaml | 5 ++ 3 files changed, 76 insertions(+), 2 deletions(-) rename Tests/AWSLambdaRuntimeCoreTests/{Lambda+StringTest.swift => LambdaHandlerTest.swift} (79%) diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index cb468de7..996885e2 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -110,7 +110,7 @@ extension AsyncLambdaHandler { @asyncHandler private func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { do { - let result = try await handle(context: context, event: event) + let result = try await self.handle(context: context, event: event) promise.succeed(result) } catch { promise.fail(error) diff --git a/Tests/AWSLambdaRuntimeCoreTests/Lambda+StringTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift similarity index 79% rename from Tests/AWSLambdaRuntimeCoreTests/Lambda+StringTest.swift rename to Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 8e880296..39de9a99 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/Lambda+StringTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -16,7 +16,9 @@ import NIO import XCTest -class StringLambdaTest: XCTestCase { +class LambdaHandlerTest: XCTestCase { + // MARK: Callback + func testCallbackSuccess() { let server = MockLambdaServer(behavior: Behavior()) XCTAssertNoThrow(try server.start().wait()) @@ -77,6 +79,71 @@ class StringLambdaTest: XCTestCase { assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } + #if compiler(>=5.5) && $AsyncAwait + + // MARK: AsyncLambdaHandler + + func testAsyncHandlerSuccess() { + let server = MockLambdaServer(behavior: Behavior()) + XCTAssertNoThrow(try server.start().wait()) + defer { XCTAssertNoThrow(try server.stop().wait()) } + + struct Handler: AsyncLambdaHandler { + typealias In = String + typealias Out = String + + func handle(context: Lambda.Context, event: String) async throws -> String { + event + } + } + + let maxTimes = Int.random(in: 1 ... 10) + let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let result = Lambda.run(configuration: configuration, handler: Handler()) + assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) + } + + func testVoidAsyncHandlerSuccess() { + let server = MockLambdaServer(behavior: Behavior(result: .success(nil))) + XCTAssertNoThrow(try server.start().wait()) + defer { XCTAssertNoThrow(try server.stop().wait()) } + + struct Handler: AsyncLambdaHandler { + typealias In = String + typealias Out = Void + + func handle(context: Lambda.Context, event: String) async throws {} + } + + let maxTimes = Int.random(in: 1 ... 10) + let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let result = Lambda.run(configuration: configuration, handler: Handler()) + assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) + } + + func testAsyncHandlerFailure() { + let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom")))) + XCTAssertNoThrow(try server.start().wait()) + defer { XCTAssertNoThrow(try server.stop().wait()) } + + struct Handler: AsyncLambdaHandler { + typealias In = String + typealias Out = String + + func handle(context: Lambda.Context, event: String) async throws -> String { + throw TestError("boom") + } + } + + let maxTimes = Int.random(in: 1 ... 10) + let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) + let result = Lambda.run(configuration: configuration, handler: Handler()) + assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) + } + #endif + + // MARK: EventLoop + func testEventLoopSuccess() { let server = MockLambdaServer(behavior: Behavior()) XCTAssertNoThrow(try server.start().wait()) @@ -137,6 +204,8 @@ class StringLambdaTest: XCTestCase { assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } + // MARK: Closure + func testClosureSuccess() { let server = MockLambdaServer(behavior: Behavior()) XCTAssertNoThrow(try server.start().wait()) diff --git a/docker/docker-compose.al2.main.yaml b/docker/docker-compose.al2.main.yaml index c63a13e2..741c8a43 100644 --- a/docker/docker-compose.al2.main.yaml +++ b/docker/docker-compose.al2.main.yaml @@ -10,9 +10,14 @@ services: test: image: swift-aws-lambda:al2-main + command: /bin/bash -cl "swift test --enable-test-discovery -Xswiftc -warnings-as-errors $${SANITIZER_ARG-} -Xswiftc -Xfrontend -Xswiftc -enable-experimental-concurrency" test-samples: image: swift-aws-lambda:al2-main + command: >- + /bin/bash -clx " + swift build -Xswiftc -Xfrontend -Xswiftc -enable-experimental-concurrency --package-path Examples/LambdaFunctions && + swift build -Xswiftc -Xfrontend -Xswiftc -enable-experimental-concurrency --package-path Examples/LocalDebugging/MyLambda" shell: image: swift-aws-lambda:al2-main From d6f9d9a666af858574c4bb1ef77e1df01d2d45e4 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 1 Apr 2021 14:25:21 +0200 Subject: [PATCH 6/8] PR review --- Package.swift | 3 ++- Sources/AWSLambdaRuntime/Lambda+Codable.swift | 26 +++++++------------ Sources/AWSLambdaRuntimeCore/Lambda.swift | 14 +++------- .../AWSLambdaRuntimeCore/LambdaHandler.swift | 17 ++++-------- .../LambdaHandlerTest.swift | 6 ++--- .../Lambda+CodeableTest.swift | 4 +-- 6 files changed, 26 insertions(+), 44 deletions(-) diff --git a/Package.swift b/Package.swift index 4f6ebc22..02c34b6b 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,7 @@ let package = Package( .library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.26.0")), + .package(url: "https://github.com/apple/swift-nio.git", .branch("main")), .package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.0.0")), .package(url: "https://github.com/swift-server/swift-backtrace.git", .upToNextMajor(from: "1.1.0")), ], @@ -29,6 +29,7 @@ let package = Package( .product(name: "Logging", package: "swift-log"), .product(name: "Backtrace", package: "swift-backtrace"), .product(name: "NIOHTTP1", package: "swift-nio"), + .product(name: "_NIOConcurrency", package: "swift-nio"), ]), .testTarget(name: "AWSLambdaRuntimeCoreTests", dependencies: [ .byName(name: "AWSLambdaRuntimeCore"), diff --git a/Sources/AWSLambdaRuntime/Lambda+Codable.swift b/Sources/AWSLambdaRuntime/Lambda+Codable.swift index 872a68fc..cda4a05c 100644 --- a/Sources/AWSLambdaRuntime/Lambda+Codable.swift +++ b/Sources/AWSLambdaRuntime/Lambda+Codable.swift @@ -82,29 +82,23 @@ internal struct CodableVoidClosureWrapper: LambdaHandler { #if compiler(>=5.5) && $AsyncAwait extension Lambda { - /// An async Lambda Closure that takes a `In: Decodable` and returns an `Out: Encodable` - public typealias CodableAsyncClosure = (Lambda.Context, In) async throws -> Out - /// Run a Lambda defined by implementing the `CodableAsyncClosure` function. /// /// - parameters: /// - closure: `CodableAsyncClosure` based Lambda. /// /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. - public static func run(_ closure: @escaping CodableAsyncClosure) { + public static func run(_ closure: @escaping (In, Lambda.Context) async throws -> Out) { self.run(CodableAsyncWrapper(closure)) } - /// An asynchronous Lambda Closure that takes a `In: Decodable` and returns nothing. - public typealias CodableVoidAsyncClosure = (Lambda.Context, In) async throws -> Void - /// Run a Lambda defined by implementing the `CodableVoidAsyncClosure` function. /// /// - parameters: /// - closure: `CodableVoidAsyncClosure` based Lambda. /// /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. - public static func run(_ closure: @escaping CodableVoidAsyncClosure) { + public static func run(_ closure: @escaping (In, Lambda.Context) async throws -> Void) { self.run(CodableVoidAsyncWrapper(closure)) } } @@ -113,14 +107,14 @@ internal struct CodableAsyncWrapper: AsyncLambdaH typealias In = In typealias Out = Out - private let closure: Lambda.CodableAsyncClosure + private let closure: (In, Lambda.Context) async throws -> Out - init(_ closure: @escaping Lambda.CodableAsyncClosure) { + init(_ closure: @escaping (In, Lambda.Context) async throws -> Out) { self.closure = closure } - func handle(context: Lambda.Context, event: In) async throws -> Out { - try await self.closure(context, event) + func handle(event: In, context: Lambda.Context) async throws -> Out { + try await self.closure(event, context) } } @@ -128,14 +122,14 @@ internal struct CodableVoidAsyncWrapper: AsyncLambdaHandler { typealias In = In typealias Out = Void - private let closure: Lambda.CodableVoidAsyncClosure + private let closure: (In, Lambda.Context) async throws -> Void - init(_ closure: @escaping Lambda.CodableVoidAsyncClosure) { + init(_ closure: @escaping (In, Lambda.Context) async throws -> Void) { self.closure = closure } - func handle(context: Lambda.Context, event: In) async throws { - try await self.closure(context, event) + func handle(event: In, context: Lambda.Context) async throws { + try await self.closure(event, context) } } #endif diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index df0bf4d6..e66ef000 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -18,6 +18,7 @@ import Glibc import Darwin.C #endif +import _NIOConcurrency import Backtrace import Logging import NIO @@ -59,17 +60,10 @@ public enum Lambda { #if compiler(>=5.5) && $AsyncAwait public static func run(_ factory: @escaping (InitializationContext) async throws -> Handler) { self.run { context -> EventLoopFuture in - @asyncHandler func _createLambda(_ context: InitializationContext, promise: EventLoopPromise) { - do { - let handler = try await factory(context) - promise.succeed(handler) - } catch { - promise.fail(error) - } - } - let promise = context.eventLoop.makePromise(of: Handler.self) - _createLambda(context, promise: promise) + promise.completeWithAsync { + try await factory(context) + } return promise.futureResult } } diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 996885e2..5c836f89 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -94,27 +94,20 @@ public protocol AsyncLambdaHandler: EventLoopLambdaHandler { /// Concrete Lambda handlers implement this method to provide the Lambda functionality. /// /// - parameters: - /// - context: Runtime `Context`. /// - event: Event of type `In` representing the event or request. + /// - context: Runtime `Context`. /// /// - Returns: A Lambda result ot type `Out`. - func handle(context: Lambda.Context, event: In) async throws -> Out + func handle(event: In, context: Lambda.Context) async throws -> Out } extension AsyncLambdaHandler { public func handle(context: Lambda.Context, event: In) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Out.self) - self._run(context: context, event: event, promise: promise) - return promise.futureResult - } - - @asyncHandler private func _run(context: Lambda.Context, event: In, promise: EventLoopPromise) { - do { - let result = try await self.handle(context: context, event: event) - promise.succeed(result) - } catch { - promise.fail(error) + promise.completeWithAsync { + try await self.handle(event: event, context: context) } + return promise.futureResult } } #endif diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 39de9a99..765df431 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -92,7 +92,7 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = String - func handle(context: Lambda.Context, event: String) async throws -> String { + func handle(event: String, context: Lambda.Context) async throws -> String { event } } @@ -112,7 +112,7 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = Void - func handle(context: Lambda.Context, event: String) async throws {} + func handle(event: String, context: Lambda.Context) async throws {} } let maxTimes = Int.random(in: 1 ... 10) @@ -130,7 +130,7 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = String - func handle(context: Lambda.Context, event: String) async throws -> String { + func handle(event: String, context: Lambda.Context) async throws -> String { throw TestError("boom") } } diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift index a987e287..72946daa 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift @@ -69,7 +69,7 @@ class CodableLambdaTest: XCTestCase { var inputBuffer: ByteBuffer? var outputBuffer: ByteBuffer? - let closureWrapper = CodableVoidAsyncWrapper { (_, req: Request) in + let closureWrapper = CodableVoidAsyncWrapper { (req: Request, _) in XCTAssertEqual(request, req) } @@ -84,7 +84,7 @@ class CodableLambdaTest: XCTestCase { var outputBuffer: ByteBuffer? var response: Response? - let closureWrapper = CodableAsyncWrapper { (_, req: Request) -> Response in + let closureWrapper = CodableAsyncWrapper { (req: Request, _) -> Response in XCTAssertEqual(req, request) return Response(requestId: req.requestId) } From 2b66aeb2d37a80159b2b4ce2f4e23f69b08115e3 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 6 Apr 2021 10:33:22 +0200 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: tomer doron --- Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 765df431..5ec3a5b9 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -17,7 +17,7 @@ import NIO import XCTest class LambdaHandlerTest: XCTestCase { - // MARK: Callback + // MARK: - Callback func testCallbackSuccess() { let server = MockLambdaServer(behavior: Behavior()) @@ -81,7 +81,7 @@ class LambdaHandlerTest: XCTestCase { #if compiler(>=5.5) && $AsyncAwait - // MARK: AsyncLambdaHandler + // MARK: - AsyncLambdaHandler func testAsyncHandlerSuccess() { let server = MockLambdaServer(behavior: Behavior()) @@ -142,7 +142,7 @@ class LambdaHandlerTest: XCTestCase { } #endif - // MARK: EventLoop + // MARK: - EventLoop func testEventLoopSuccess() { let server = MockLambdaServer(behavior: Behavior()) @@ -204,7 +204,7 @@ class LambdaHandlerTest: XCTestCase { assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } - // MARK: Closure + // MARK: - Closure func testClosureSuccess() { let server = MockLambdaServer(behavior: Behavior()) From 325b7b3a1c436d3c87814bcfc61a7c912cda04b9 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Sat, 17 Apr 2021 22:00:44 +0200 Subject: [PATCH 8/8] async init and @main --- Package.swift | 2 +- Sources/AWSLambdaRuntime/Lambda+Codable.swift | 56 ------------------- Sources/AWSLambdaRuntimeCore/Lambda.swift | 12 ---- .../AWSLambdaRuntimeCore/LambdaHandler.swift | 25 ++++++++- .../LambdaHandlerTest.swift | 17 ++++-- .../Lambda+CodeableTest.swift | 33 ----------- 6 files changed, 38 insertions(+), 107 deletions(-) diff --git a/Package.swift b/Package.swift index 02c34b6b..02641a5a 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,7 @@ let package = Package( .library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", .branch("main")), + .package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.28.0")), .package(url: "https://github.com/apple/swift-log.git", .upToNextMajor(from: "1.0.0")), .package(url: "https://github.com/swift-server/swift-backtrace.git", .upToNextMajor(from: "1.1.0")), ], diff --git a/Sources/AWSLambdaRuntime/Lambda+Codable.swift b/Sources/AWSLambdaRuntime/Lambda+Codable.swift index cda4a05c..e80022a8 100644 --- a/Sources/AWSLambdaRuntime/Lambda+Codable.swift +++ b/Sources/AWSLambdaRuntime/Lambda+Codable.swift @@ -78,62 +78,6 @@ internal struct CodableVoidClosureWrapper: LambdaHandler { } } -// MARK: - Async - -#if compiler(>=5.5) && $AsyncAwait -extension Lambda { - /// Run a Lambda defined by implementing the `CodableAsyncClosure` function. - /// - /// - parameters: - /// - closure: `CodableAsyncClosure` based Lambda. - /// - /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. - public static func run(_ closure: @escaping (In, Lambda.Context) async throws -> Out) { - self.run(CodableAsyncWrapper(closure)) - } - - /// Run a Lambda defined by implementing the `CodableVoidAsyncClosure` function. - /// - /// - parameters: - /// - closure: `CodableVoidAsyncClosure` based Lambda. - /// - /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. - public static func run(_ closure: @escaping (In, Lambda.Context) async throws -> Void) { - self.run(CodableVoidAsyncWrapper(closure)) - } -} - -internal struct CodableAsyncWrapper: AsyncLambdaHandler { - typealias In = In - typealias Out = Out - - private let closure: (In, Lambda.Context) async throws -> Out - - init(_ closure: @escaping (In, Lambda.Context) async throws -> Out) { - self.closure = closure - } - - func handle(event: In, context: Lambda.Context) async throws -> Out { - try await self.closure(event, context) - } -} - -internal struct CodableVoidAsyncWrapper: AsyncLambdaHandler { - typealias In = In - typealias Out = Void - - private let closure: (In, Lambda.Context) async throws -> Void - - init(_ closure: @escaping (In, Lambda.Context) async throws -> Void) { - self.closure = closure - } - - func handle(event: In, context: Lambda.Context) async throws { - try await self.closure(event, context) - } -} -#endif - // MARK: - Codable support /// Implementation of a`ByteBuffer` to `In` decoding diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index e66ef000..264a5138 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -57,18 +57,6 @@ public enum Lambda { } } - #if compiler(>=5.5) && $AsyncAwait - public static func run(_ factory: @escaping (InitializationContext) async throws -> Handler) { - self.run { context -> EventLoopFuture in - let promise = context.eventLoop.makePromise(of: Handler.self) - promise.completeWithAsync { - try await factory(context) - } - return promise.futureResult - } - } - #endif - /// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a factory, typically a constructor. /// /// - parameters: diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 5c836f89..542b1bc4 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -87,9 +87,18 @@ extension LambdaHandler { // MARK: - AsyncLambdaHandler -#if compiler(>=5.5) && $AsyncAwait +#if compiler(>=5.5) /// Strongly typed, processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` async. +@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) public protocol AsyncLambdaHandler: EventLoopLambdaHandler { + /// The Lambda initialization method + /// Use this method to initialize resources that will be used in every request. + /// + /// Examples for this can be HTTP or database clients. + /// - parameters: + /// - context: Runtime `InitializationContext`. + init(context: Lambda.InitializationContext) async throws + /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. /// @@ -101,6 +110,7 @@ public protocol AsyncLambdaHandler: EventLoopLambdaHandler { func handle(event: In, context: Lambda.Context) async throws -> Out } +@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) extension AsyncLambdaHandler { public func handle(context: Lambda.Context, event: In) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Out.self) @@ -110,6 +120,19 @@ extension AsyncLambdaHandler { return promise.futureResult } } + +@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) +extension AsyncLambdaHandler { + public static func main() { + Lambda.run { context -> EventLoopFuture in + let promise = context.eventLoop.makePromise(of: ByteBufferLambdaHandler.self) + promise.completeWithAsync { + try await Self(context: context) + } + return promise.futureResult + } + } +} #endif // MARK: - EventLoopLambdaHandler diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 5ec3a5b9..53a56910 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -79,10 +79,11 @@ class LambdaHandlerTest: XCTestCase { assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } - #if compiler(>=5.5) && $AsyncAwait + #if compiler(>=5.5) // MARK: - AsyncLambdaHandler + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) func testAsyncHandlerSuccess() { let server = MockLambdaServer(behavior: Behavior()) XCTAssertNoThrow(try server.start().wait()) @@ -92,6 +93,8 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = String + init(context: Lambda.InitializationContext) {} + func handle(event: String, context: Lambda.Context) async throws -> String { event } @@ -99,10 +102,11 @@ class LambdaHandlerTest: XCTestCase { let maxTimes = Int.random(in: 1 ... 10) let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, handler: Handler()) + let result = Lambda.run(configuration: configuration, factory: Handler.init) assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) func testVoidAsyncHandlerSuccess() { let server = MockLambdaServer(behavior: Behavior(result: .success(nil))) XCTAssertNoThrow(try server.start().wait()) @@ -112,15 +116,18 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = Void + init(context: Lambda.InitializationContext) {} + func handle(event: String, context: Lambda.Context) async throws {} } let maxTimes = Int.random(in: 1 ... 10) let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, handler: Handler()) + let result = Lambda.run(configuration: configuration, factory: Handler.init) assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } + @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *) func testAsyncHandlerFailure() { let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom")))) XCTAssertNoThrow(try server.start().wait()) @@ -130,6 +137,8 @@ class LambdaHandlerTest: XCTestCase { typealias In = String typealias Out = String + init(context: Lambda.InitializationContext) {} + func handle(event: String, context: Lambda.Context) async throws -> String { throw TestError("boom") } @@ -137,7 +146,7 @@ class LambdaHandlerTest: XCTestCase { let maxTimes = Int.random(in: 1 ... 10) let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, handler: Handler()) + let result = Lambda.run(configuration: configuration, factory: Handler.init) assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes) } #endif diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift index 72946daa..814977fa 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodeableTest.swift @@ -63,39 +63,6 @@ class CodableLambdaTest: XCTestCase { XCTAssertEqual(response?.requestId, request.requestId) } - #if compiler(>=5.5) && $AsyncAwait - func testCodableVoidAsyncWrapper() { - let request = Request(requestId: UUID().uuidString) - var inputBuffer: ByteBuffer? - var outputBuffer: ByteBuffer? - - let closureWrapper = CodableVoidAsyncWrapper { (req: Request, _) in - XCTAssertEqual(request, req) - } - - XCTAssertNoThrow(inputBuffer = try JSONEncoder().encode(request, using: self.allocator)) - XCTAssertNoThrow(outputBuffer = try closureWrapper.handle(context: self.newContext(), event: XCTUnwrap(inputBuffer)).wait()) - XCTAssertNil(outputBuffer) - } - - func testCodableAsyncWrapper() { - let request = Request(requestId: UUID().uuidString) - var inputBuffer: ByteBuffer? - var outputBuffer: ByteBuffer? - var response: Response? - - let closureWrapper = CodableAsyncWrapper { (req: Request, _) -> Response in - XCTAssertEqual(req, request) - return Response(requestId: req.requestId) - } - - XCTAssertNoThrow(inputBuffer = try JSONEncoder().encode(request, using: self.allocator)) - XCTAssertNoThrow(outputBuffer = try closureWrapper.handle(context: self.newContext(), event: XCTUnwrap(inputBuffer)).wait()) - XCTAssertNoThrow(response = try JSONDecoder().decode(Response.self, from: XCTUnwrap(outputBuffer))) - XCTAssertEqual(response?.requestId, request.requestId) - } - #endif - // convencience method func newContext() -> Lambda.Context { Lambda.Context(requestID: UUID().uuidString,