Skip to content

Commit 22b6ac2

Browse files
authored
Merge branch 'master' into dynamodb-event
2 parents 92513f9 + 2324074 commit 22b6ac2

File tree

9 files changed

+154
-71
lines changed

9 files changed

+154
-71
lines changed

Sources/AWSLambdaRuntime/Lambda+Codable.swift

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,46 +19,52 @@ import NIOFoundationCompat
1919

2020
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `Codable` payloads.
2121
extension Lambda {
22-
/// Run a Lambda defined by implementing the `CodableLambdaClosure` function.
22+
/// An asynchronous Lambda Closure that takes a `In: Decodable` and returns a `Result<Out: Encodable, Error>` via a completion handler.
23+
public typealias CodableClosure<In: Decodable, Out: Encodable> = (Lambda.Context, In, @escaping (Result<Out, Error>) -> Void) -> Void
24+
25+
/// Run a Lambda defined by implementing the `CodableClosure` function.
26+
///
27+
/// - parameters:
28+
/// - closure: `CodableClosure` based Lambda.
2329
///
2430
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
25-
public static func run<In: Decodable, Out: Encodable>(_ closure: @escaping CodableLambdaClosure<In, Out>) {
31+
public static func run<In: Decodable, Out: Encodable>(_ closure: @escaping CodableClosure<In, Out>) {
2632
self.run(closure: closure)
2733
}
2834

29-
/// Run a Lambda defined by implementing the `CodableVoidLambdaClosure` function.
35+
/// An asynchronous Lambda Closure that takes a `In: Decodable` and returns a `Result<Void, Error>` via a completion handler.
36+
public typealias CodableVoidClosure<In: Decodable> = (Lambda.Context, In, @escaping (Result<Void, Error>) -> Void) -> Void
37+
38+
/// Run a Lambda defined by implementing the `CodableVoidClosure` function.
39+
///
40+
/// - parameters:
41+
/// - closure: `CodableVoidClosure` based Lambda.
3042
///
3143
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
32-
public static func run<In: Decodable>(_ closure: @escaping CodableVoidLambdaClosure<In>) {
44+
public static func run<In: Decodable>(_ closure: @escaping CodableVoidClosure<In>) {
3345
self.run(closure: closure)
3446
}
3547

3648
// for testing
3749
@discardableResult
38-
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping CodableLambdaClosure<In, Out>) -> Result<Int, Error> {
39-
self.run(configuration: configuration, handler: CodableLambdaClosureWrapper(closure))
50+
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping CodableClosure<In, Out>) -> Result<Int, Error> {
51+
self.run(configuration: configuration, handler: CodableClosureWrapper(closure))
4052
}
4153

4254
// for testing
4355
@discardableResult
44-
internal static func run<In: Decodable>(configuration: Configuration = .init(), closure: @escaping CodableVoidLambdaClosure<In>) -> Result<Int, Error> {
45-
self.run(configuration: configuration, handler: CodableVoidLambdaClosureWrapper(closure))
56+
internal static func run<In: Decodable>(configuration: Configuration = .init(), closure: @escaping CodableVoidClosure<In>) -> Result<Int, Error> {
57+
self.run(configuration: configuration, handler: CodableVoidClosureWrapper(closure))
4658
}
4759
}
4860

49-
/// A processing closure for a Lambda that takes a `In` and returns a `Result<Out, Error>` via a `CompletionHandler` asynchronously.
50-
public typealias CodableLambdaClosure<In: Decodable, Out: Encodable> = (Lambda.Context, In, @escaping (Result<Out, Error>) -> Void) -> Void
51-
52-
/// A processing closure for a Lambda that takes a `In` and returns a `Result<Void, Error>` via a `CompletionHandler` asynchronously.
53-
public typealias CodableVoidLambdaClosure<In: Decodable> = (Lambda.Context, In, @escaping (Result<Void, Error>) -> Void) -> Void
54-
55-
internal struct CodableLambdaClosureWrapper<In: Decodable, Out: Encodable>: LambdaHandler {
61+
internal struct CodableClosureWrapper<In: Decodable, Out: Encodable>: LambdaHandler {
5662
typealias In = In
5763
typealias Out = Out
5864

59-
private let closure: CodableLambdaClosure<In, Out>
65+
private let closure: Lambda.CodableClosure<In, Out>
6066

61-
init(_ closure: @escaping CodableLambdaClosure<In, Out>) {
67+
init(_ closure: @escaping Lambda.CodableClosure<In, Out>) {
6268
self.closure = closure
6369
}
6470

@@ -67,13 +73,13 @@ internal struct CodableLambdaClosureWrapper<In: Decodable, Out: Encodable>: Lamb
6773
}
6874
}
6975

70-
internal struct CodableVoidLambdaClosureWrapper<In: Decodable>: LambdaHandler {
76+
internal struct CodableVoidClosureWrapper<In: Decodable>: LambdaHandler {
7177
typealias In = In
7278
typealias Out = Void
7379

74-
private let closure: CodableVoidLambdaClosure<In>
80+
private let closure: Lambda.CodableVoidClosure<In>
7581

76-
init(_ closure: @escaping CodableVoidLambdaClosure<In>) {
82+
init(_ closure: @escaping Lambda.CodableVoidClosure<In>) {
7783
self.closure = closure
7884
}
7985

@@ -126,6 +132,7 @@ private extension Lambda {
126132
}
127133

128134
extension JSONDecoder: LambdaCodableDecoder {}
135+
129136
extension JSONEncoder: LambdaCodableEncoder {
130137
public func encode<T>(_ value: T, using allocator: ByteBufferAllocator) throws -> ByteBuffer where T: Encodable {
131138
// nio will resize the buffer if necessary

Sources/AWSLambdaRuntime/Lambda+String.swift

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,46 +15,52 @@ import NIO
1515

1616
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `String` payloads.
1717
extension Lambda {
18-
/// Run a Lambda defined by implementing the `StringLambdaClosure` function.
18+
/// An asynchronous Lambda Closure that takes a `String` and returns a `Result<String, Error>` via a completion handler.
19+
public typealias StringClosure = (Lambda.Context, String, @escaping (Result<String, Error>) -> Void) -> Void
20+
21+
/// Run a Lambda defined by implementing the `StringClosure` function.
22+
///
23+
/// - parameters:
24+
/// - closure: `StringClosure` based Lambda.
1925
///
2026
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
21-
public static func run(_ closure: @escaping StringLambdaClosure) {
27+
public static func run(_ closure: @escaping StringClosure) {
2228
self.run(closure: closure)
2329
}
2430

25-
/// Run a Lambda defined by implementing the `StringVoidLambdaClosure` function.
31+
/// An asynchronous Lambda Closure that takes a `String` and returns a `Result<Void, Error>` via a completion handler.
32+
public typealias StringVoidClosure = (Lambda.Context, String, @escaping (Result<Void, Error>) -> Void) -> Void
33+
34+
/// Run a Lambda defined by implementing the `StringVoidClosure` function.
35+
///
36+
/// - parameters:
37+
/// - closure: `StringVoidClosure` based Lambda.
2638
///
2739
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
28-
public static func run(_ closure: @escaping StringVoidLambdaClosure) {
40+
public static func run(_ closure: @escaping StringVoidClosure) {
2941
self.run(closure: closure)
3042
}
3143

3244
// for testing
3345
@discardableResult
34-
internal static func run(configuration: Configuration = .init(), closure: @escaping StringLambdaClosure) -> Result<Int, Error> {
35-
self.run(configuration: configuration, handler: StringLambdaClosureWrapper(closure))
46+
internal static func run(configuration: Configuration = .init(), closure: @escaping StringClosure) -> Result<Int, Error> {
47+
self.run(configuration: configuration, handler: StringClosureWrapper(closure))
3648
}
3749

3850
// for testing
3951
@discardableResult
40-
internal static func run(configuration: Configuration = .init(), closure: @escaping StringVoidLambdaClosure) -> Result<Int, Error> {
41-
self.run(configuration: configuration, handler: StringVoidLambdaClosureWrapper(closure))
52+
internal static func run(configuration: Configuration = .init(), closure: @escaping StringVoidClosure) -> Result<Int, Error> {
53+
self.run(configuration: configuration, handler: StringVoidClosureWrapper(closure))
4254
}
4355
}
4456

45-
/// A processing closure for a Lambda that takes a `String` and returns a `Result<String, Error>` via a `CompletionHandler` asynchronously.
46-
public typealias StringLambdaClosure = (Lambda.Context, String, @escaping (Result<String, Error>) -> Void) -> Void
47-
48-
/// A processing closure for a Lambda that takes a `String` and returns a `Result<Void, Error>` via a `CompletionHandler` asynchronously.
49-
public typealias StringVoidLambdaClosure = (Lambda.Context, String, @escaping (Result<Void, Error>) -> Void) -> Void
50-
51-
internal struct StringLambdaClosureWrapper: LambdaHandler {
57+
internal struct StringClosureWrapper: LambdaHandler {
5258
typealias In = String
5359
typealias Out = String
5460

55-
private let closure: StringLambdaClosure
61+
private let closure: Lambda.StringClosure
5662

57-
init(_ closure: @escaping StringLambdaClosure) {
63+
init(_ closure: @escaping Lambda.StringClosure) {
5864
self.closure = closure
5965
}
6066

@@ -63,13 +69,13 @@ internal struct StringLambdaClosureWrapper: LambdaHandler {
6369
}
6470
}
6571

66-
internal struct StringVoidLambdaClosureWrapper: LambdaHandler {
72+
internal struct StringVoidClosureWrapper: LambdaHandler {
6773
typealias In = String
6874
typealias Out = Void
6975

70-
private let closure: StringVoidLambdaClosure
76+
private let closure: Lambda.StringVoidClosure
7177

72-
init(_ closure: @escaping StringVoidLambdaClosure) {
78+
init(_ closure: @escaping Lambda.StringVoidClosure) {
7379
self.closure = closure
7480
}
7581

@@ -78,8 +84,8 @@ internal struct StringVoidLambdaClosureWrapper: LambdaHandler {
7884
}
7985
}
8086

81-
/// Implementation of a`ByteBuffer` to `String` encoding
8287
public extension EventLoopLambdaHandler where In == String {
88+
/// Implementation of a `ByteBuffer` to `String` decoding
8389
func decode(buffer: ByteBuffer) throws -> String {
8490
var buffer = buffer
8591
guard let string = buffer.readString(length: buffer.readableBytes) else {
@@ -89,8 +95,8 @@ public extension EventLoopLambdaHandler where In == String {
8995
}
9096
}
9197

92-
/// Implementation of `String` to `ByteBuffer` decoding
9398
public extension EventLoopLambdaHandler where Out == String {
99+
/// Implementation of `String` to `ByteBuffer` encoding
94100
func encode(allocator: ByteBufferAllocator, value: String) throws -> ByteBuffer? {
95101
// FIXME: reusable buffer
96102
var buffer = allocator.buffer(capacity: value.utf8.count)

Sources/AWSLambdaRuntime/Lambda.swift

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,37 +23,55 @@ import Logging
2323
import NIO
2424

2525
public enum Lambda {
26+
public typealias Handler = ByteBufferLambdaHandler
27+
28+
/// `ByteBufferLambdaHandler` factory.
29+
///
30+
/// A function that takes a `EventLoop` and returns an `EventLoopFuture` of a `ByteBufferLambdaHandler`
31+
public typealias HandlerFactory = (EventLoop) -> EventLoopFuture<Handler>
32+
2633
/// Run a Lambda defined by implementing the `LambdaHandler` protocol.
2734
///
35+
/// - parameters:
36+
/// - handler: `ByteBufferLambdaHandler` based Lambda.
37+
///
2838
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
29-
public static func run(_ handler: ByteBufferLambdaHandler) {
39+
public static func run(_ handler: Handler) {
3040
self.run(handler: handler)
3141
}
3242

3343
/// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a `LambdaHandlerFactory`.
44+
/// Use this to initialize all your resources that you want to cache between invocations. This could be database connections and HTTP clients for example.
45+
/// It is encouraged to use the given `EventLoop`'s conformance to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance.
46+
///
47+
/// - parameters:
48+
/// - factory: A `ByteBufferLambdaHandler` factory.
3449
///
3550
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
36-
public static func run(_ factory: @escaping LambdaHandlerFactory) {
51+
public static func run(_ factory: @escaping HandlerFactory) {
3752
self.run(factory: factory)
3853
}
3954

4055
/// Run a Lambda defined by implementing the `LambdaHandler` protocol provided via a factory, typically a constructor.
4156
///
57+
/// - parameters:
58+
/// - factory: A `ByteBufferLambdaHandler` factory.
59+
///
4260
/// - note: This is a blocking operation that will run forever, as its lifecycle is managed by the AWS Lambda Runtime Engine.
43-
public static func run(_ factory: @escaping (EventLoop) throws -> ByteBufferLambdaHandler) {
61+
public static func run(_ factory: @escaping (EventLoop) throws -> Handler) {
4462
self.run(factory: factory)
4563
}
4664

4765
// for testing and internal use
4866
@discardableResult
49-
internal static func run(configuration: Configuration = .init(), handler: ByteBufferLambdaHandler) -> Result<Int, Error> {
67+
internal static func run(configuration: Configuration = .init(), handler: Handler) -> Result<Int, Error> {
5068
self.run(configuration: configuration, factory: { $0.makeSucceededFuture(handler) })
5169
}
5270

5371
// for testing and internal use
5472
@discardableResult
55-
internal static func run(configuration: Configuration = .init(), factory: @escaping (EventLoop) throws -> ByteBufferLambdaHandler) -> Result<Int, Error> {
56-
self.run(configuration: configuration, factory: { eventloop -> EventLoopFuture<ByteBufferLambdaHandler> in
73+
internal static func run(configuration: Configuration = .init(), factory: @escaping (EventLoop) throws -> Handler) -> Result<Int, Error> {
74+
self.run(configuration: configuration, factory: { eventloop -> EventLoopFuture<Handler> in
5775
do {
5876
let handler = try factory(eventloop)
5977
return eventloop.makeSucceededFuture(handler)
@@ -65,7 +83,7 @@ public enum Lambda {
6583

6684
// for testing and internal use
6785
@discardableResult
68-
internal static func run(configuration: Configuration = .init(), factory: @escaping LambdaHandlerFactory) -> Result<Int, Error> {
86+
internal static func run(configuration: Configuration = .init(), factory: @escaping HandlerFactory) -> Result<Int, Error> {
6987
do {
7088
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) // only need one thread, will improve performance
7189
defer { try! eventLoopGroup.syncShutdownGracefully() }
@@ -76,7 +94,7 @@ public enum Lambda {
7694
}
7795
}
7896

79-
internal static func runAsync(eventLoopGroup: EventLoopGroup, configuration: Configuration, factory: @escaping LambdaHandlerFactory) -> EventLoopFuture<Int> {
97+
internal static func runAsync(eventLoopGroup: EventLoopGroup, configuration: Configuration, factory: @escaping HandlerFactory) -> EventLoopFuture<Int> {
8098
Backtrace.install()
8199
var logger = Logger(label: "Lambda")
82100
logger.logLevel = configuration.general.logLevel
@@ -92,5 +110,3 @@ public enum Lambda {
92110
}
93111
}
94112
}
95-
96-
public typealias LambdaHandlerFactory = (EventLoop) -> EventLoopFuture<ByteBufferLambdaHandler>

Sources/AWSLambdaRuntime/LambdaContext.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import Logging
1717
import NIO
1818

1919
extension Lambda {
20+
/// Lambda runtime context.
21+
/// The Lambda runtime generates and passes the `Context` to the Lambda handler as an argument.
2022
public final class Context {
2123
/// The request ID, which identifies the request that triggered the function invocation.
2224
public let requestId: String
@@ -36,9 +38,20 @@ extension Lambda {
3638
/// For invocations from the AWS Mobile SDK, data about the client application and device.
3739
public let clientContext: String?
3840

39-
/// a logger to log
41+
/// `Logger` to log with
42+
///
43+
/// - note: The `LogLevel` can be configured using the `LOG_LEVEL` environment variable.
4044
public let logger: Logger
45+
46+
/// The `EventLoop` the Lambda is executed on. Use this to schedule work with.
47+
/// This is useful when implementing the `EventLoopLambdaHandler` protocol.
48+
///
49+
/// - note: The `EventLoop` is shared with the Lambda runtime engine and should be handled with extra care.
50+
/// Most importantly the `EventLoop` must never be blocked.
4151
public let eventLoop: EventLoop
52+
53+
/// `ByteBufferAllocator` to allocate `ByteBuffer`
54+
/// This is useful when implementing `EventLoopLambdaHandler`
4255
public let allocator: ByteBufferAllocator
4356

4457
internal init(requestId: String,

Sources/AWSLambdaRuntime/LambdaHandler.swift

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,17 @@ import NIO
2525
/// The `EventLoopLambdaHandler` will execute the Lambda on the same `EventLoop` as the core runtime engine, making the processing faster but requires
2626
/// more care from the implementation to never block the `EventLoop`.
2727
public protocol LambdaHandler: EventLoopLambdaHandler {
28+
/// Defines to which `DispatchQueue` the Lambda execution is offloaded to.
2829
var offloadQueue: DispatchQueue { get }
2930

31+
/// The Lambda handling method
32+
/// Concrete Lambda handlers implement this method to provide the Lambda functionality.
33+
///
34+
/// - parameters:
35+
/// - context: Runtime `Context`.
36+
/// - payload: Payload of type `In` representing the event or request.
37+
/// - callback: Completion handler to report the result of the Lambda back to the runtime engine.
38+
/// The completion handler expects a `Result` with either a response of type `Out` or an `Error`
3039
func handle(context: Lambda.Context, payload: In, callback: @escaping (Result<Out, Error>) -> Void)
3140
}
3241

@@ -66,14 +75,38 @@ public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler {
6675
associatedtype In
6776
associatedtype Out
6877

78+
/// The Lambda handling method
79+
/// Concrete Lambda handlers implement this method to provide the Lambda functionality.
80+
///
81+
/// - parameters:
82+
/// - context: Runtime `Context`.
83+
/// - payload: Payload of type `In` representing the event or request.
84+
///
85+
/// - Returns: An `EventLoopFuture` to report the result of the Lambda back to the runtime engine.
86+
/// The `EventLoopFuture` should be completed with either a response of type `Out` or an `Error`
6987
func handle(context: Lambda.Context, payload: In) -> EventLoopFuture<Out>
7088

89+
/// Encode a response of type `Out` to `ByteBuffer`
90+
/// Concrete Lambda handlers implement this method to provide coding functionality.
91+
/// - parameters:
92+
/// - allocator: A `ByteBufferAllocator` to help allocate the `ByteBuffer`.
93+
/// - value: Response of type `Out`.
94+
///
95+
/// - Returns: A `ByteBuffer` with the encoded version of the `value`.
7196
func encode(allocator: ByteBufferAllocator, value: Out) throws -> ByteBuffer?
97+
98+
/// Decode a`ByteBuffer` to a request or event of type `In`
99+
/// Concrete Lambda handlers implement this method to provide coding functionality.
100+
///
101+
/// - parameters:
102+
/// - buffer: The `ByteBuffer` to decode.
103+
///
104+
/// - Returns: A request or event of type `In`.
72105
func decode(buffer: ByteBuffer) throws -> In
73106
}
74107

75-
/// Driver for `ByteBuffer` -> `In` decoding and `Out` -> `ByteBuffer` encoding
76108
public extension EventLoopLambdaHandler {
109+
/// Driver for `ByteBuffer` -> `In` decoding and `Out` -> `ByteBuffer` encoding
77110
func handle(context: Lambda.Context, payload: ByteBuffer) -> EventLoopFuture<ByteBuffer?> {
78111
switch self.decodeIn(buffer: payload) {
79112
case .failure(let error):
@@ -121,7 +154,15 @@ public extension EventLoopLambdaHandler where Out == Void {
121154
/// - note: This is a low level protocol designed to power the higher level `EventLoopLambdaHandler` and `LambdaHandler` based APIs.
122155
/// Most users are not expected to use this protocol.
123156
public protocol ByteBufferLambdaHandler {
124-
/// Handles the Lambda request.
157+
/// The Lambda handling method
158+
/// Concrete Lambda handlers implement this method to provide the Lambda functionality.
159+
///
160+
/// - parameters:
161+
/// - context: Runtime `Context`.
162+
/// - payload: The event or request payload encoded as `ByteBuffer`.
163+
///
164+
/// - Returns: An `EventLoopFuture` to report the result of the Lambda back to the runtime engine.
165+
/// The `EventLoopFuture` should be completed with either a response encoded as `ByteBuffer` or an `Error`
125166
func handle(context: Lambda.Context, payload: ByteBuffer) -> EventLoopFuture<ByteBuffer?>
126167
}
127168

0 commit comments

Comments
 (0)