mirror of
https://github.com/swift-server/swift-aws-lambda-runtime.git
synced 2026-05-03 07:22:27 +00:00
async/await support (#186)
- Add an `AsyncLambdaHandler`. Will be renamed to `LambdaHandler` as soon as we drop the current callback based `LambdaHandler`. - The default way to use an `AsyncLambdaHandler` is to use `@main` to execute it. Don't use `Lambda.run` for it. We wan't to remove `Lambda.run` for 1.0. Co-authored-by: tomer doron <tomerd@apple.com>
This commit is contained in:
+2
-1
@@ -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", .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")),
|
||||
],
|
||||
@@ -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"),
|
||||
|
||||
@@ -78,6 +78,8 @@ internal struct CodableVoidClosureWrapper<In: Decodable>: LambdaHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Codable support
|
||||
|
||||
/// Implementation of a`ByteBuffer` to `In` decoding
|
||||
extension EventLoopLambdaHandler where In: Decodable {
|
||||
@inlinable
|
||||
|
||||
@@ -18,6 +18,7 @@ import Glibc
|
||||
import Darwin.C
|
||||
#endif
|
||||
|
||||
import _NIOConcurrency
|
||||
import Backtrace
|
||||
import Logging
|
||||
import NIO
|
||||
|
||||
@@ -85,6 +85,56 @@ extension LambdaHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AsyncLambdaHandler
|
||||
|
||||
#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.
|
||||
///
|
||||
/// - parameters:
|
||||
/// - event: Event of type `In` representing the event or request.
|
||||
/// - context: Runtime `Context`.
|
||||
///
|
||||
/// - Returns: A Lambda result ot type `Out`.
|
||||
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<Out> {
|
||||
let promise = context.eventLoop.makePromise(of: Out.self)
|
||||
promise.completeWithAsync {
|
||||
try await self.handle(event: event, context: context)
|
||||
}
|
||||
return promise.futureResult
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
|
||||
extension AsyncLambdaHandler {
|
||||
public static func main() {
|
||||
Lambda.run { context -> EventLoopFuture<ByteBufferLambdaHandler> in
|
||||
let promise = context.eventLoop.makePromise(of: ByteBufferLambdaHandler.self)
|
||||
promise.completeWithAsync {
|
||||
try await Self(context: context)
|
||||
}
|
||||
return promise.futureResult
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - EventLoopLambdaHandler
|
||||
|
||||
/// Strongly typed, `EventLoopFuture` based processing protocol for a Lambda that takes a user defined `In` and returns a user defined `Out` asynchronously.
|
||||
|
||||
+79
-1
@@ -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,80 @@ class StringLambdaTest: XCTestCase {
|
||||
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
#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())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
struct Handler: AsyncLambdaHandler {
|
||||
typealias In = String
|
||||
typealias Out = String
|
||||
|
||||
init(context: Lambda.InitializationContext) {}
|
||||
|
||||
func handle(event: String, context: Lambda.Context) 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, 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())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
struct Handler: AsyncLambdaHandler {
|
||||
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, 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())
|
||||
defer { XCTAssertNoThrow(try server.stop().wait()) }
|
||||
|
||||
struct Handler: AsyncLambdaHandler {
|
||||
typealias In = String
|
||||
typealias Out = String
|
||||
|
||||
init(context: Lambda.InitializationContext) {}
|
||||
|
||||
func handle(event: String, context: Lambda.Context) 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, factory: Handler.init)
|
||||
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
#endif
|
||||
|
||||
// MARK: - EventLoop
|
||||
|
||||
func testEventLoopSuccess() {
|
||||
let server = MockLambdaServer(behavior: Behavior())
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
@@ -137,6 +213,8 @@ class StringLambdaTest: XCTestCase {
|
||||
assertLambdaLifecycleResult(result, shoudHaveRun: maxTimes)
|
||||
}
|
||||
|
||||
// MARK: - Closure
|
||||
|
||||
func testClosureSuccess() {
|
||||
let server = MockLambdaServer(behavior: Behavior())
|
||||
XCTAssertNoThrow(try server.start().wait())
|
||||
@@ -53,7 +53,7 @@ class CodableLambdaTest: XCTestCase {
|
||||
var response: Response?
|
||||
|
||||
let closureWrapper = CodableClosureWrapper { (_, req: Request, completion: (Result<Response, Error>) -> Void) in
|
||||
XCTAssertEqual(request, request)
|
||||
XCTAssertEqual(request, req)
|
||||
completion(.success(Response(requestId: req.requestId)))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user