From 454fa9a744d13209813900534eefd4ec694300af Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Mon, 3 Jan 2022 09:18:20 +0100 Subject: [PATCH 1/5] Lambda factory as a protocol requirement. --- Sources/AWSLambdaRuntimeCore/Lambda.swift | 54 ++++---------- .../AWSLambdaRuntimeCore/LambdaHandler.swift | 73 +++++++++++++++---- .../AWSLambdaRuntimeCore/LambdaRunner.swift | 6 +- .../AWSLambdaRuntimeCore/LambdaRuntime.swift | 16 ++-- .../LambdaHandlerTest.swift | 42 ++++++++--- .../LambdaHandlers.swift | 33 +++++++-- .../LambdaRunnerTest.swift | 7 +- .../LambdaRuntimeClientTest.swift | 34 ++++----- .../LambdaRuntimeTest.swift | 63 +++------------- .../LambdaTest.swift | 50 +++++-------- Tests/AWSLambdaRuntimeCoreTests/Utils.swift | 8 +- .../Lambda+CodableTest.swift | 12 ++- 12 files changed, 200 insertions(+), 198 deletions(-) diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index d54d0192..c6163fd3 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -24,27 +24,6 @@ import NIOCore import NIOPosix public enum Lambda { - public typealias Handler = ByteBufferLambdaHandler - - /// `ByteBufferLambdaHandler` factory. - /// - /// A function that takes a `InitializationContext` and returns an `EventLoopFuture` of a `ByteBufferLambdaHandler` - public typealias HandlerFactory = (InitializationContext) -> EventLoopFuture - - /// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a `LambdaHandlerFactory`. - /// Use this to initialize all your resources that you want to cache between invocations. This could be database connections and HTTP clients for example. - /// It is encouraged to use the given `EventLoop`'s conformance to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance. - /// - /// - parameters: - /// - factory: A `ByteBufferLambdaHandler` factory. - /// - /// - 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(_ factory: @escaping HandlerFactory) { - if case .failure(let error) = self.run(factory: factory) { - fatalError("\(error)") - } - } - /// Utility to access/read environment variables public static func env(_ name: String) -> String? { guard let value = getenv(name) else { @@ -53,30 +32,23 @@ public enum Lambda { return String(cString: value) } - #if compiler(>=5.5) && canImport(_Concurrency) - // for testing and internal use - @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) - internal static func run(configuration: Configuration = .init(), handlerType: Handler.Type) -> Result { - self.run(configuration: configuration, factory: { context -> EventLoopFuture in - let promise = context.eventLoop.makePromise(of: ByteBufferLambdaHandler.self) - promise.completeWithTask { - try await Handler(context: context) - } - return promise.futureResult - }) - } - #endif - - // for testing and internal use - internal static func run(configuration: Configuration = .init(), factory: @escaping HandlerFactory) -> Result { - let _run = { (configuration: Configuration, factory: @escaping HandlerFactory) -> Result in + /// Run a Lambda defined by implementing the ``ByteBufferLambdaHandler`` protocol. + /// The Runtime will manage the Lambdas application lifecycle automatically. It will invoke the + /// ``ByteBufferLambdaHandler/factory(context:)`` to create a new Handler. + /// + /// - parameters: + /// - factory: A `ByteBufferLambdaHandler` factory. + /// + /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. + internal static func run(configuration: Configuration = .init(), handlerType: Handler.Type) -> Result { + let _run = { (configuration: Configuration) -> Result in Backtrace.install() var logger = Logger(label: "Lambda") logger.logLevel = configuration.general.logLevel var result: Result! MultiThreadedEventLoopGroup.withCurrentThreadAsEventLoop { eventLoop in - let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, configuration: configuration, factory: factory) + let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, configuration: configuration) #if DEBUG let signalSource = trap(signal: configuration.lifecycle.stopSignal) { signal in logger.info("intercepted signal: \(signal)") @@ -108,13 +80,13 @@ public enum Lambda { if Lambda.env("LOCAL_LAMBDA_SERVER_ENABLED").flatMap(Bool.init) ?? false { do { return try Lambda.withLocalServer { - _run(configuration, factory) + _run(configuration) } } catch { return .failure(error) } } else { - return _run(configuration, factory) + return _run(configuration) } #else return _run(configuration, factory) diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index f0a9d256..af0d0527 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -18,7 +18,13 @@ import NIOCore // MARK: - LambdaHandler #if compiler(>=5.5) && canImport(_Concurrency) -/// Strongly typed, processing protocol for a Lambda that takes a user defined `Event` and returns a user defined `Output` async. +/// Strongly typed, processing protocol for a Lambda that takes a user defined +/// ``EventLoopLambdaHandler/Event`` and returns a user defined +/// ``EventLoopLambdaHandler/Output`` asynchronously. +/// +/// - note: Most users should implement this protocol instead of the lower +/// level protocols ``EventLoopLambdaHandler`` and +/// ``ByteBufferLambdaHandler``. @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) public protocol LambdaHandler: EventLoopLambdaHandler { /// The Lambda initialization method @@ -42,6 +48,14 @@ public protocol LambdaHandler: EventLoopLambdaHandler { @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) extension LambdaHandler { + public static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + let promise = context.eventLoop.makePromise(of: Self.self) + promise.completeWithTask { + try await Self.init(context: context) + } + return promise.futureResult + } + public func handle(_ event: Event, context: LambdaContext) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Output.self) promise.completeWithTask { @@ -51,25 +65,31 @@ extension LambdaHandler { } } -@available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) -extension LambdaHandler { - public static func main() { - _ = Lambda.run(handlerType: Self.self) - } -} #endif // MARK: - EventLoopLambdaHandler -/// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user defined `Event` and returns a user defined `Output` asynchronously. -/// `EventLoopLambdaHandler` extends `ByteBufferLambdaHandler`, performing `ByteBuffer` -> `Event` decoding and `Output` -> `ByteBuffer` encoding. +/// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user +/// defined ``Event`` and returns a user defined ``Output`` asynchronously. /// -/// - note: To implement a Lambda, implement either `LambdaHandler` or the `EventLoopLambdaHandler` protocol. -/// The `LambdaHandler` will offload the Lambda execution to a `DispatchQueue` making processing safer but slower -/// The `EventLoopLambdaHandler` will execute the Lambda on the same `EventLoop` as the core runtime engine, making the processing faster but requires -/// more care from the implementation to never block the `EventLoop`. +/// ``EventLoopLambdaHandler`` extends ``ByteBufferLambdaHandler``, performing +/// `ByteBuffer` -> ``Event`` decoding and ``Output`` -> `ByteBuffer` encoding. +/// +/// - note: To implement a Lambda, implement either ``LambdaHandler`` or the +/// ``EventLoopLambdaHandler`` protocol. The ``LambdaHandler`` will offload +/// the Lambda execution to an async Task making processing safer but slower (due to +/// fewer thread hops). +/// The ``EventLoopLambdaHandler`` will execute the Lambda on the same `EventLoop` +/// as the core runtime engine, making the processing faster but requires more care from the +/// implementation to never block the `EventLoop`. Implement this protocol only in performance +/// critical situations and implement ``LambdaHandler`` in all other circumstances. public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler { + + /// The lambda functions input. In most cases this should be Codable. If your event originates from an + /// AWS service, have a look at [AWSLambdaEvents](https://github.com/swift-server/swift-aws-lambda-events), + /// which provides a number of commonly used AWS Event implementations. associatedtype Event + /// The lambda functions output. Can be `Void`. associatedtype Output /// The Lambda handling method @@ -135,9 +155,19 @@ extension EventLoopLambdaHandler where Output == Void { /// An `EventLoopFuture` based processing protocol for a Lambda that takes a `ByteBuffer` and returns a `ByteBuffer?` asynchronously. /// -/// - note: This is a low level protocol designed to power the higher level `EventLoopLambdaHandler` and `LambdaHandler` based APIs. +/// - note: This is a low level protocol designed to power the higher level ``EventLoopLambdaHandler`` and +/// ``LambdaHandler`` based APIs. /// Most users are not expected to use this protocol. public protocol ByteBufferLambdaHandler { + + /// Create your Lambda handler for the runtime. + /// + /// Use this to initialize all your resources that you want to cache between invocations. This could be database + /// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance + /// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it + /// minimizes thread hopping. + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture + /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. /// @@ -163,6 +193,21 @@ extension ByteBufferLambdaHandler { } } +extension ByteBufferLambdaHandler { + + /// Initializes and runs the lambda function. + /// + /// If you precede your ``ByteBufferLambdaHandler`` conformer's declaration with the + /// [@main](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626) + /// attribute, the system calls the conformer's `main()` method to launch the lambda function. + /// + /// The lambda runtime provides a default implementation of the method that manages the launch + /// process. + public static func main() { + _ = Lambda.run(configuration: .init(), handlerType: Self.self) + } +} + @usableFromInline enum CodecError: Error { case requestDecoding(Error) diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift index c2404b4b..2fa9d0f8 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift @@ -34,14 +34,14 @@ extension Lambda { /// Run the user provided initializer. This *must* only be called once. /// /// - Returns: An `EventLoopFuture` fulfilled with the outcome of the initialization. - func initialize(logger: Logger, factory: @escaping HandlerFactory) -> EventLoopFuture { + func initialize(logger: Logger, handlerType: Handler.Type) -> EventLoopFuture { logger.debug("initializing lambda") // 1. create the handler from the factory // 2. report initialization error if one occured let context = InitializationContext(logger: logger, eventLoop: self.eventLoop, allocator: self.allocator) - return factory(context) + return Handler.factory(context: context) // Hopping back to "our" EventLoop is important in case the factory returns a future // that originated from a foreign EventLoop/EventLoopGroup. // This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops @@ -56,7 +56,7 @@ extension Lambda { } } - func run(logger: Logger, handler: Handler) -> EventLoopFuture { + func run(logger: Logger, handler: Handler) -> EventLoopFuture { logger.debug("lambda invocation sequence starting") // 1. request invocation from lambda runtime engine self.isGettingNextInvocation = true diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift index 53b9c430..e6a61281 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift @@ -19,12 +19,11 @@ import NIOCore /// `LambdaRuntime` manages the Lambda process lifecycle. /// /// - note: It is intended to be used within a single `EventLoop`. For this reason this class is not thread safe. -public final class LambdaRuntime { +public final class LambdaRuntime { private let eventLoop: EventLoop private let shutdownPromise: EventLoopPromise private let logger: Logger private let configuration: Lambda.Configuration - private let factory: Lambda.HandlerFactory private var state = State.idle { willSet { @@ -39,16 +38,15 @@ public final class LambdaRuntime { /// - eventLoop: An `EventLoop` to run the Lambda on. /// - logger: A `Logger` to log the Lambda events. /// - factory: A `LambdaHandlerFactory` to create the concrete Lambda handler. - public convenience init(eventLoop: EventLoop, logger: Logger, factory: @escaping Lambda.HandlerFactory) { - self.init(eventLoop: eventLoop, logger: logger, configuration: .init(), factory: factory) + public convenience init(eventLoop: EventLoop, logger: Logger) { + self.init(eventLoop: eventLoop, logger: logger, configuration: .init()) } - init(eventLoop: EventLoop, logger: Logger, configuration: Lambda.Configuration, factory: @escaping Lambda.HandlerFactory) { + init(eventLoop: EventLoop, logger: Logger, configuration: Lambda.Configuration) { self.eventLoop = eventLoop self.shutdownPromise = eventLoop.makePromise(of: Int.self) self.logger = logger self.configuration = configuration - self.factory = factory } deinit { @@ -79,8 +77,8 @@ public final class LambdaRuntime { logger[metadataKey: "lifecycleId"] = .string(self.configuration.lifecycle.id) let runner = Lambda.Runner(eventLoop: self.eventLoop, configuration: self.configuration) - let startupFuture = runner.initialize(logger: logger, factory: self.factory) - startupFuture.flatMap { handler -> EventLoopFuture<(ByteBufferLambdaHandler, Result)> in + let startupFuture = runner.initialize(logger: logger, handlerType: Handler.self) + startupFuture.flatMap { handler -> EventLoopFuture<(Handler, Result)> in // after the startup future has succeeded, we have a handler that we can use // to `run` the lambda. let finishedPromise = self.eventLoop.makePromise(of: Int.self) @@ -173,7 +171,7 @@ public final class LambdaRuntime { private enum State { case idle case initializing - case active(Lambda.Runner, Lambda.Handler) + case active(Lambda.Runner, Handler) case shuttingdown case shutdown diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 27290d54..52194162 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -158,6 +158,10 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = String + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(Handler()) + } func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(event) @@ -166,9 +170,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, factory: { context in - context.eventLoop.makeSucceededFuture(Handler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -180,6 +182,10 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = Void + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(Handler()) + } func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(()) @@ -188,9 +194,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, factory: { context in - context.eventLoop.makeSucceededFuture(Handler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -202,6 +206,10 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = String + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(Handler()) + } func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(TestError("boom")) @@ -210,9 +218,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, factory: { context in - context.eventLoop.makeSucceededFuture(Handler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: Handler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -220,10 +226,22 @@ class LambdaHandlerTest: XCTestCase { let server = MockLambdaServer(behavior: FailedBootstrapBehavior()) XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } + + struct Handler: EventLoopLambdaHandler { + typealias Event = String + typealias Output = String + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeFailedFuture(TestError("kaboom")) + } - let result = Lambda.run(configuration: .init(), factory: { context in - context.eventLoop.makeFailedFuture(TestError("kaboom")) - }) + func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { + XCTFail("Must never be called") + return context.eventLoop.makeFailedFuture(TestError("boom")) + } + } + + let result = Lambda.run(configuration: .init(), handlerType: Handler.self) assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom")) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift index 23899c63..3c02f753 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift @@ -14,27 +14,48 @@ import AWSLambdaRuntimeCore import NIOCore +import XCTest struct EchoHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(EchoHandler()) + } func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(event) } } -struct FailedHandler: EventLoopLambdaHandler { +struct StartupError: Error {} + +struct StartupErrorHandler: EventLoopLambdaHandler { typealias Event = String - typealias Output = Void + typealias Output = String + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeFailedFuture(StartupError()) + } + + func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { + XCTFail("Must never be called") + return context.eventLoop.makeSucceededFuture(event) + } +} - private let reason: String +struct RuntimeError: Error {} + +struct RuntimeErrorHandler: EventLoopLambdaHandler { + typealias Event = String + typealias Output = Void - init(_ reason: String) { - self.reason = reason + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(RuntimeErrorHandler()) } func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { - context.eventLoop.makeFailedFuture(TestError(self.reason)) + context.eventLoop.makeFailedFuture(RuntimeError()) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRunnerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRunnerTest.swift index dd87eb61..a561dbf8 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRunnerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRunnerTest.swift @@ -40,12 +40,11 @@ class LambdaRunnerTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertNoThrow(try runLambda(behavior: Behavior(), handler: EchoHandler())) + XCTAssertNoThrow(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) } func testFailure() { struct Behavior: LambdaServerBehavior { - static let error = "boom" let requestId = UUID().uuidString func getInvocation() -> GetInvocationResult { .success((requestId: self.requestId, event: "hello")) @@ -58,7 +57,7 @@ class LambdaRunnerTest: XCTestCase { func processError(requestId: String, error: ErrorResponse) -> Result { XCTAssertEqual(self.requestId, requestId, "expecting requestId to match") - XCTAssertEqual(Behavior.error, error.errorMessage, "expecting error to match") + XCTAssertEqual(String(describing: RuntimeError()), error.errorMessage, "expecting error to match") return .success(()) } @@ -67,6 +66,6 @@ class LambdaRunnerTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertNoThrow(try runLambda(behavior: Behavior(), handler: FailedHandler(Behavior.error))) + XCTAssertNoThrow(try runLambda(behavior: Behavior(), handlerType: RuntimeErrorHandler.self)) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift index 551cc014..2130d0e2 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeClientTest.swift @@ -24,20 +24,20 @@ import XCTest class LambdaRuntimeClientTest: XCTestCase { func testSuccess() { let behavior = Behavior() - XCTAssertNoThrow(try runLambda(behavior: behavior, handler: EchoHandler())) + XCTAssertNoThrow(try runLambda(behavior: behavior, handlerType: EchoHandler.self)) XCTAssertEqual(behavior.state, 6) } func testFailure() { let behavior = Behavior() - XCTAssertNoThrow(try runLambda(behavior: behavior, handler: FailedHandler("boom"))) + XCTAssertNoThrow(try runLambda(behavior: behavior, handlerType: RuntimeErrorHandler.self)) XCTAssertEqual(behavior.state, 10) } - func testBootstrapFailure() { + func testStartupFailure() { let behavior = Behavior() - XCTAssertThrowsError(try runLambda(behavior: behavior, factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in - XCTAssertEqual(error as? TestError, TestError("boom")) + XCTAssertThrowsError(try runLambda(behavior: behavior, handlerType: StartupErrorHandler.self)) { + XCTAssert($0 is StartupError) } XCTAssertEqual(behavior.state, 1) } @@ -63,8 +63,8 @@ class LambdaRuntimeClientTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in - XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { + XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) } } @@ -89,8 +89,8 @@ class LambdaRuntimeClientTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in - XCTAssertEqual(error as? Lambda.RuntimeError, .noBody) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { + XCTAssertEqual($0 as? Lambda.RuntimeError, .noBody) } } @@ -116,8 +116,8 @@ class LambdaRuntimeClientTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in - XCTAssertEqual(error as? Lambda.RuntimeError, .invocationMissingHeader(AmazonHeaders.requestID)) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { + XCTAssertEqual($0 as? Lambda.RuntimeError, .invocationMissingHeader(AmazonHeaders.requestID)) } } @@ -141,8 +141,8 @@ class LambdaRuntimeClientTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: EchoHandler())) { error in - XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: EchoHandler.self)) { + XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) } } @@ -166,8 +166,8 @@ class LambdaRuntimeClientTest: XCTestCase { return .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), handler: FailedHandler("boom"))) { error in - XCTAssertEqual(error as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: RuntimeErrorHandler.self)) { + XCTAssertEqual($0 as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) } } @@ -192,8 +192,8 @@ class LambdaRuntimeClientTest: XCTestCase { .failure(.internalServerError) } } - XCTAssertThrowsError(try runLambda(behavior: Behavior(), factory: { $0.eventLoop.makeFailedFuture(TestError("boom")) })) { error in - XCTAssertEqual(error as? TestError, TestError("boom")) + XCTAssertThrowsError(try runLambda(behavior: Behavior(), handlerType: StartupErrorHandler.self)) { + XCTAssert($0 is StartupError) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift index 460cfd64..44adc676 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift @@ -29,37 +29,16 @@ class LambdaRuntimeTest: XCTestCase { let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let testError = TestError("kaboom") - let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: { - $0.eventLoop.makeFailedFuture(testError) - }) + let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger) // eventLoop.submit in this case returns an EventLoopFuture> // which is why we need `wait().wait()` - XCTAssertThrowsError(try eventLoop.flatSubmit { runtime.start() }.wait()) { error in - XCTAssertEqual(testError, error as? TestError) + XCTAssertThrowsError(try eventLoop.flatSubmit { runtime.start() }.wait()) { + XCTAssert($0 is StartupError) } - XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in - XCTAssertEqual(testError, error as? TestError) - } - } - - struct CallbackLambdaHandler: ByteBufferLambdaHandler { - let handler: (LambdaContext, ByteBuffer) -> (EventLoopFuture) - let shutdown: (Lambda.ShutdownContext) -> EventLoopFuture - - init(_ handler: @escaping (LambdaContext, ByteBuffer) -> (EventLoopFuture), shutdown: @escaping (Lambda.ShutdownContext) -> EventLoopFuture) { - self.handler = handler - self.shutdown = shutdown - } - - func handle(_ event: ByteBuffer, context: LambdaContext) -> EventLoopFuture { - self.handler(context, event) - } - - func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture { - self.shutdown(context) + XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { + XCTAssert($0 is StartupError) } } @@ -70,23 +49,14 @@ class LambdaRuntimeTest: XCTestCase { let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - var count = 0 - let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) { context in - count += 1 - return context.eventLoop.makeSucceededFuture(()) - } - let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: { - $0.eventLoop.makeSucceededFuture(handler) - }) + let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger) XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait()) - XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in - XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), error as? Lambda.RuntimeError) + XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { + XCTAssertEqual(.badStatusCode(HTTPResponseStatus.internalServerError), $0 as? Lambda.RuntimeError) } - XCTAssertEqual(count, 1) } func testLambdaResultIfShutsdownIsUnclean() { @@ -96,28 +66,19 @@ class LambdaRuntimeTest: XCTestCase { let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } - var count = 0 - let handler = CallbackLambdaHandler({ XCTFail("Should not be reached"); return $0.eventLoop.makeSucceededFuture($1) }) { context in - count += 1 - return context.eventLoop.makeFailedFuture(TestError("kaboom")) - } - let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger, factory: { - $0.eventLoop.makeSucceededFuture(handler) - }) + let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger) - XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait()) - XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { error in + XCTAssertNoThrow(try eventLoop.flatSubmit { runtime.start() }.wait()) + XCTAssertThrowsError(try runtime.shutdownFuture.wait()) { error in guard case Lambda.RuntimeError.shutdownError(let shutdownError, .failure(let runtimeError)) = error else { - XCTFail("Unexpected error"); return + XCTFail("Unexpected error: \(error)"); return } XCTAssertEqual(shutdownError as? TestError, TestError("kaboom")) XCTAssertEqual(runtimeError as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) } - XCTAssertEqual(count, 1) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift index 2bff7688..3da730f0 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaTest.swift @@ -26,22 +26,18 @@ class LambdaTest: XCTestCase { let maxTimes = Int.random(in: 10 ... 20) let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } func testFailure() { - let server = MockLambdaServer(behavior: Behavior(result: .failure(TestError("boom")))) + let server = MockLambdaServer(behavior: Behavior(result: .failure(RuntimeError()))) XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } let maxTimes = Int.random(in: 10 ... 20) let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(FailedHandler("boom")) - }) + let result = Lambda.run(configuration: configuration, handlerType: RuntimeErrorHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -50,8 +46,8 @@ class LambdaTest: XCTestCase { XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) }) - assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom")) + let result = Lambda.run(configuration: .init(), handlerType: StartupErrorHandler.self) + assertLambdaRuntimeResult(result, shouldFailWithError: StartupError()) } func testBootstrapFailureAndReportErrorFailure() { @@ -80,8 +76,8 @@ class LambdaTest: XCTestCase { XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - let result = Lambda.run(factory: { $0.eventLoop.makeFailedFuture(TestError("kaboom")) }) - assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom")) + let result = Lambda.run(configuration: .init(), handlerType: StartupErrorHandler.self) + assertLambdaRuntimeResult(result, shouldFailWithError: StartupError()) } func testStartStopInDebugMode() { @@ -99,7 +95,7 @@ class LambdaTest: XCTestCase { usleep(100_000) kill(getpid(), signal.rawValue) } - let result = Lambda.run(configuration: configuration, factory: { $0.eventLoop.makeSucceededFuture(EchoHandler()) }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) switch result { case .success(let invocationCount): @@ -118,9 +114,7 @@ class LambdaTest: XCTestCase { let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1), runtimeEngine: .init(requestTimeout: .milliseconds(timeout))) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("timeout")) } @@ -130,9 +124,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.upstreamError("connectionResetByPeer")) } @@ -143,9 +135,7 @@ class LambdaTest: XCTestCase { defer { XCTAssertNoThrow(try server.stop().wait()) } let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: 1)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: 1) } @@ -156,9 +146,7 @@ class LambdaTest: XCTestCase { let maxTimes = 10 let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -169,9 +157,7 @@ class LambdaTest: XCTestCase { let maxTimes = 10 let configuration = Lambda.Configuration(lifecycle: .init(maxTimes: maxTimes)) - let result = Lambda.run(configuration: configuration, factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: configuration, handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shoudHaveRun: maxTimes) } @@ -199,9 +185,7 @@ class LambdaTest: XCTestCase { } } - let result = Lambda.run(configuration: .init(), factory: { - $0.eventLoop.makeSucceededFuture(EchoHandler()) - }) + let result = Lambda.run(configuration: .init(), handlerType: EchoHandler.self) assertLambdaRuntimeResult(result, shouldFailWithError: Lambda.RuntimeError.badStatusCode(.internalServerError)) } @@ -271,9 +255,9 @@ class LambdaTest: XCTestCase { private struct Behavior: LambdaServerBehavior { let requestId: String let event: String - let result: Result + let result: Result - init(requestId: String = UUID().uuidString, event: String = "hello", result: Result = .success("hello")) { + init(requestId: String = UUID().uuidString, event: String = "hello", result: Result = .success("hello")) { self.requestId = requestId self.event = event self.result = result @@ -302,7 +286,7 @@ private struct Behavior: LambdaServerBehavior { XCTFail("unexpected to succeed, but failed with: \(error)") return .failure(.internalServerError) case .failure(let expected): - XCTAssertEqual(expected.description, error.errorMessage, "expecting error to match") + XCTAssertEqual(String(describing: expected), error.errorMessage, "expecting error to match") return .success(()) } } diff --git a/Tests/AWSLambdaRuntimeCoreTests/Utils.swift b/Tests/AWSLambdaRuntimeCoreTests/Utils.swift index 5dd0a5cd..96b1a1c8 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/Utils.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/Utils.swift @@ -18,11 +18,7 @@ import NIOCore import NIOPosix import XCTest -func runLambda(behavior: LambdaServerBehavior, handler: Lambda.Handler) throws { - try runLambda(behavior: behavior, factory: { $0.eventLoop.makeSucceededFuture(handler) }) -} - -func runLambda(behavior: LambdaServerBehavior, factory: @escaping Lambda.HandlerFactory) throws { +func runLambda(behavior: LambdaServerBehavior, handlerType: Handler.Type) throws { let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } let logger = Logger(label: "TestLogger") @@ -30,7 +26,7 @@ func runLambda(behavior: LambdaServerBehavior, factory: @escaping Lambda.Handler let runner = Lambda.Runner(eventLoop: eventLoopGroup.next(), configuration: configuration) let server = try MockLambdaServer(behavior: behavior).start().wait() defer { XCTAssertNoThrow(try server.stop().wait()) } - try runner.initialize(logger: logger, factory: factory).flatMap { handler in + try runner.initialize(logger: logger, handlerType: handlerType).flatMap { handler in runner.run(logger: logger, handler: handler) }.wait() } diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift index 214631ad..7a773744 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift @@ -41,7 +41,11 @@ class CodableLambdaTest: XCTestCase { typealias Event = Request typealias Output = Void - let expected: Request + var expected: Request? + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(Handler()) + } func handle(_ event: Request, context: LambdaContext) -> EventLoopFuture { XCTAssertEqual(event, self.expected) @@ -66,7 +70,11 @@ class CodableLambdaTest: XCTestCase { typealias Event = Request typealias Output = Response - let expected: Request + var expected: Request? + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(Handler()) + } func handle(_ event: Request, context: LambdaContext) -> EventLoopFuture { XCTAssertEqual(event, self.expected) From 2ea61300a3115b49c3c048052e6c6f982151069c Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Mon, 3 Jan 2022 14:46:44 +0100 Subject: [PATCH 2/5] Fix test and code format --- .../AWSLambdaRuntimeCore/LambdaHandler.swift | 9 +++----- .../AWSLambdaRuntimeCore/LambdaRuntime.swift | 4 ++-- .../LambdaHandlerTest.swift | 12 +++++----- .../LambdaHandlers.swift | 4 ++-- .../LambdaRuntimeTest.swift | 23 +++++++++++++++++-- .../Lambda+CodableTest.swift | 4 ++-- 6 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index af0d0527..b9832b23 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -51,11 +51,11 @@ extension LambdaHandler { public static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Self.self) promise.completeWithTask { - try await Self.init(context: context) + try await Self(context: context) } return promise.futureResult } - + public func handle(_ event: Event, context: LambdaContext) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Output.self) promise.completeWithTask { @@ -84,7 +84,6 @@ extension LambdaHandler { /// implementation to never block the `EventLoop`. Implement this protocol only in performance /// critical situations and implement ``LambdaHandler`` in all other circumstances. public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler { - /// The lambda functions input. In most cases this should be Codable. If your event originates from an /// AWS service, have a look at [AWSLambdaEvents](https://github.com/swift-server/swift-aws-lambda-events), /// which provides a number of commonly used AWS Event implementations. @@ -159,7 +158,6 @@ extension EventLoopLambdaHandler where Output == Void { /// ``LambdaHandler`` based APIs. /// Most users are not expected to use this protocol. public protocol ByteBufferLambdaHandler { - /// Create your Lambda handler for the runtime. /// /// Use this to initialize all your resources that you want to cache between invocations. This could be database @@ -167,7 +165,7 @@ public protocol ByteBufferLambdaHandler { /// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it /// minimizes thread hopping. static func factory(context: Lambda.InitializationContext) -> EventLoopFuture - + /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. /// @@ -194,7 +192,6 @@ extension ByteBufferLambdaHandler { } extension ByteBufferLambdaHandler { - /// Initializes and runs the lambda function. /// /// If you precede your ``ByteBufferLambdaHandler`` conformer's declaration with the diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift index e6a61281..7db1a38f 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift @@ -86,7 +86,7 @@ public final class LambdaRuntime { self.run(promise: finishedPromise) return finishedPromise.futureResult.mapResult { (handler, $0) } } - .flatMap { (handler, runnerResult) -> EventLoopFuture in + .flatMap { handler, runnerResult -> EventLoopFuture in // after the lambda finishPromise has succeeded or failed we need to // shutdown the handler let shutdownContext = Lambda.ShutdownContext(logger: logger, eventLoop: self.eventLoop) @@ -95,7 +95,7 @@ public final class LambdaRuntime { // the runner result logger.error("Error shutting down handler: \(error)") throw Lambda.RuntimeError.shutdownError(shutdownError: error, runnerResult: runnerResult) - }.flatMapResult { (_) -> Result in + }.flatMapResult { _ -> Result in // we had no error shutting down the lambda. let's return the runner's result runnerResult } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 52194162..8cf972a8 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -158,7 +158,7 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -182,7 +182,7 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = Void - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -206,7 +206,7 @@ class LambdaHandlerTest: XCTestCase { struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -226,11 +226,11 @@ class LambdaHandlerTest: XCTestCase { let server = MockLambdaServer(behavior: FailedBootstrapBehavior()) XCTAssertNoThrow(try server.start().wait()) defer { XCTAssertNoThrow(try server.stop().wait()) } - + struct Handler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(TestError("kaboom")) } @@ -240,7 +240,7 @@ class LambdaHandlerTest: XCTestCase { return context.eventLoop.makeFailedFuture(TestError("boom")) } } - + let result = Lambda.run(configuration: .init(), handlerType: Handler.self) assertLambdaRuntimeResult(result, shouldFailWithError: TestError("kaboom")) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift index 3c02f753..8291253b 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift @@ -19,7 +19,7 @@ import XCTest struct EchoHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(EchoHandler()) } @@ -34,7 +34,7 @@ struct StartupError: Error {} struct StartupErrorHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(StartupError()) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift index 44adc676..b798fa65 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift @@ -66,9 +66,28 @@ class LambdaRuntimeTest: XCTestCase { let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } + struct ShutdownError: Error {} + + struct ShutdownErrorHandler: EventLoopLambdaHandler { + typealias Event = String + typealias Output = Void + + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(ShutdownErrorHandler()) + } + + func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { + context.eventLoop.makeSucceededVoidFuture() + } + + func shutdown(context: Lambda.ShutdownContext) -> EventLoopFuture { + context.eventLoop.makeFailedFuture(ShutdownError()) + } + } + let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger) + let runtime = LambdaRuntime(eventLoop: eventLoop, logger: logger) XCTAssertNoThrow(try eventLoop.flatSubmit { runtime.start() }.wait()) XCTAssertThrowsError(try runtime.shutdownFuture.wait()) { error in @@ -76,7 +95,7 @@ class LambdaRuntimeTest: XCTestCase { XCTFail("Unexpected error: \(error)"); return } - XCTAssertEqual(shutdownError as? TestError, TestError("kaboom")) + XCTAssert(shutdownError is ShutdownError) XCTAssertEqual(runtimeError as? Lambda.RuntimeError, .badStatusCode(.internalServerError)) } } diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift index 7a773744..fa68c2be 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift @@ -42,7 +42,7 @@ class CodableLambdaTest: XCTestCase { typealias Output = Void var expected: Request? - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -71,7 +71,7 @@ class CodableLambdaTest: XCTestCase { typealias Output = Response var expected: Request? - + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } From b6f65af3dd1053f93d021fdcbd82e52331dfe8fb Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Mon, 3 Jan 2022 14:52:03 +0100 Subject: [PATCH 3/5] Fix examples --- .../Benchmark/{main.swift => BenchmarkHandler.swift} | 9 ++++++--- .../Benchmark/{main.swift => BenchmarkHandler.swift} | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) rename Examples/Benchmark/{main.swift => BenchmarkHandler.swift} (82%) rename Examples/Deployment/Sources/Benchmark/{main.swift => BenchmarkHandler.swift} (86%) diff --git a/Examples/Benchmark/main.swift b/Examples/Benchmark/BenchmarkHandler.swift similarity index 82% rename from Examples/Benchmark/main.swift rename to Examples/Benchmark/BenchmarkHandler.swift index 4a76d1c6..8f7734e4 100644 --- a/Examples/Benchmark/main.swift +++ b/Examples/Benchmark/BenchmarkHandler.swift @@ -20,13 +20,16 @@ import NIOCore // `EventLoopLambdaHandler` does not offload the Lambda processing to a separate thread // while the closure-based handlers do. -struct MyLambda: EventLoopLambdaHandler { +@main +struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(BenchmarkHandler()) + } + func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture("hello, world!") } } - -Lambda.run { $0.eventLoop.makeSucceededFuture(MyLambda()) } diff --git a/Examples/Deployment/Sources/Benchmark/main.swift b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift similarity index 86% rename from Examples/Deployment/Sources/Benchmark/main.swift rename to Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift index 454e265a..e7f86a00 100644 --- a/Examples/Deployment/Sources/Benchmark/main.swift +++ b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift @@ -20,13 +20,16 @@ import NIO // `EventLoopLambdaHandler` does not offload the Lambda processing to a separate thread // while the closure-based handlers do. +@main struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String + static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + context.eventLoop.makeSucceededFuture(BenchmarkHandler()) + } + func handle(_ event: String, context: LambdaContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture("hello, world!") } } - -Lambda.run { $0.eventLoop.makeSucceededFuture(BenchmarkHandler()) } From 4bae23fee6a2d7a6934ddeec02369c2a78984ef3 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 7 Jan 2022 09:37:32 +0100 Subject: [PATCH 4/5] Fix code review comments --- Sources/AWSLambdaRuntimeCore/Lambda.swift | 12 ++++++++---- Sources/AWSLambdaRuntimeCore/LambdaContext.swift | 4 +++- Sources/AWSLambdaRuntimeCore/LambdaHandler.swift | 4 ++-- Sources/AWSLambdaRuntimeCore/LambdaRunner.swift | 2 +- Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift | 1 - .../LambdaHandlerTest.swift | 8 ++++---- Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift | 6 +++--- .../LambdaRuntimeTest.swift | 2 +- Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift | 4 ++-- 9 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Sources/AWSLambdaRuntimeCore/Lambda.swift b/Sources/AWSLambdaRuntimeCore/Lambda.swift index c6163fd3..1bf4f0dc 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda.swift @@ -34,13 +34,17 @@ public enum Lambda { /// Run a Lambda defined by implementing the ``ByteBufferLambdaHandler`` protocol. /// The Runtime will manage the Lambdas application lifecycle automatically. It will invoke the - /// ``ByteBufferLambdaHandler/factory(context:)`` to create a new Handler. + /// ``ByteBufferLambdaHandler/makeHandler(context:)`` to create a new Handler. /// /// - parameters: - /// - factory: A `ByteBufferLambdaHandler` factory. + /// - configuration: A Lambda runtime configuration object + /// - handlerType: The Handler to create and invoke. /// /// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine. - internal static func run(configuration: Configuration = .init(), handlerType: Handler.Type) -> Result { + internal static func run( + configuration: Configuration = .init(), + handlerType: Handler.Type + ) -> Result { let _run = { (configuration: Configuration) -> Result in Backtrace.install() var logger = Logger(label: "Lambda") @@ -89,7 +93,7 @@ public enum Lambda { return _run(configuration) } #else - return _run(configuration, factory) + return _run(configuration) #endif } } diff --git a/Sources/AWSLambdaRuntimeCore/LambdaContext.swift b/Sources/AWSLambdaRuntimeCore/LambdaContext.swift index ca4af5c6..39e12439 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaContext.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaContext.swift @@ -20,7 +20,9 @@ import NIOCore extension Lambda { /// Lambda runtime initialization context. - /// The Lambda runtime generates and passes the `InitializationContext` to the Lambda factory as an argument. + /// The Lambda runtime generates and passes the `InitializationContext` to the Handlers + /// ``ByteBufferLambdaHandler/makeHandler(context:)`` or ``LambdaHandler/init(context:)`` + /// as an argument. public struct InitializationContext { /// `Logger` to log with /// diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index b9832b23..3c2697ff 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -48,7 +48,7 @@ public protocol LambdaHandler: EventLoopLambdaHandler { @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *) extension LambdaHandler { - public static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + public static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { let promise = context.eventLoop.makePromise(of: Self.self) promise.completeWithTask { try await Self(context: context) @@ -164,7 +164,7 @@ public protocol ByteBufferLambdaHandler { /// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance /// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it /// minimizes thread hopping. - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture /// The Lambda handling method /// Concrete Lambda handlers implement this method to provide the Lambda functionality. diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift index 2fa9d0f8..38499a05 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift @@ -41,7 +41,7 @@ extension Lambda { let context = InitializationContext(logger: logger, eventLoop: self.eventLoop, allocator: self.allocator) - return Handler.factory(context: context) + return Handler.makeHandler(context: context) // Hopping back to "our" EventLoop is important in case the factory returns a future // that originated from a foreign EventLoop/EventLoopGroup. // This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift index 7db1a38f..46e73d1b 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift @@ -37,7 +37,6 @@ public final class LambdaRuntime { /// - parameters: /// - eventLoop: An `EventLoop` to run the Lambda on. /// - logger: A `Logger` to log the Lambda events. - /// - factory: A `LambdaHandlerFactory` to create the concrete Lambda handler. public convenience init(eventLoop: EventLoop, logger: Logger) { self.init(eventLoop: eventLoop, logger: logger, configuration: .init()) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift index 8cf972a8..a41f6a57 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlerTest.swift @@ -159,7 +159,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -183,7 +183,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = Void - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -207,7 +207,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -231,7 +231,7 @@ class LambdaHandlerTest: XCTestCase { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(TestError("kaboom")) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift index 8291253b..41cdcf8c 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaHandlers.swift @@ -20,7 +20,7 @@ struct EchoHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(EchoHandler()) } @@ -35,7 +35,7 @@ struct StartupErrorHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeFailedFuture(StartupError()) } @@ -51,7 +51,7 @@ struct RuntimeErrorHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = Void - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(RuntimeErrorHandler()) } diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift index b798fa65..213f628a 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift @@ -72,7 +72,7 @@ class LambdaRuntimeTest: XCTestCase { typealias Event = String typealias Output = Void - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(ShutdownErrorHandler()) } diff --git a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift index fa68c2be..c11cf005 100644 --- a/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift +++ b/Tests/AWSLambdaRuntimeTests/Lambda+CodableTest.swift @@ -43,7 +43,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } @@ -72,7 +72,7 @@ class CodableLambdaTest: XCTestCase { var expected: Request? - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(Handler()) } From 6dec8482680c94a0c4f549a1bb33472eda4dfda3 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 7 Jan 2022 09:42:09 +0100 Subject: [PATCH 5/5] Fix examples again --- Examples/Benchmark/BenchmarkHandler.swift | 2 +- Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Examples/Benchmark/BenchmarkHandler.swift b/Examples/Benchmark/BenchmarkHandler.swift index 8f7734e4..5ce09a42 100644 --- a/Examples/Benchmark/BenchmarkHandler.swift +++ b/Examples/Benchmark/BenchmarkHandler.swift @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(BenchmarkHandler()) } diff --git a/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift index e7f86a00..1338fa7e 100644 --- a/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift +++ b/Examples/Deployment/Sources/Benchmark/BenchmarkHandler.swift @@ -25,7 +25,7 @@ struct BenchmarkHandler: EventLoopLambdaHandler { typealias Event = String typealias Output = String - static func factory(context: Lambda.InitializationContext) -> EventLoopFuture { + static func makeHandler(context: Lambda.InitializationContext) -> EventLoopFuture { context.eventLoop.makeSucceededFuture(BenchmarkHandler()) }