Skip to content

Commit e47c955

Browse files
committed
more api thgouhts
1 parent 07c61f2 commit e47c955

16 files changed

+924
-627
lines changed

Sources/SwiftAwsLambda/Lambda+Codable.swift

Lines changed: 90 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,88 +19,145 @@ import NIOFoundationCompat
1919
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `Codable` payloads.
2020
/// This is the most common way to use this library in AWS Lambda, since its JSON based.
2121
extension Lambda {
22-
/// Run a Lambda defined by implementing the `LambdaCodableClosure` closure, having `In` and `Out` extending `Decodable` and `Encodable` respectively.
22+
/// Run a Lambda defined by implementing the `CodableLambda.Closure` closure, having `In` and `Out` extending `Decodable` and `Encodable` respectively.
2323
///
2424
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
25-
public static func run<In: Decodable, Out: Encodable>(_ closure: @escaping LambdaCodableClosure<In, Out>) {
26-
self.run(LambdaClosureWrapper(closure))
25+
public static func run<In: Decodable, Out: Encodable>(_ closure: @escaping CodableLambda.Closure<In, Out>) {
26+
self.run(ClosureWrapper(closure))
2727
}
2828

29-
/// Run a Lambda defined by implementing the `LambdaCodableHandler` protocol, having `In` and `Out` are `Decodable` and `Encodable` respectively.
29+
/// Run a Lambda defined by implementing the `CodableLambdaHandler` protocol, having `In` and `Out` are `Decodable` and `Encodable` respectively.
3030
///
3131
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
32-
public static func run<Handler>(_ handler: Handler) where Handler: LambdaCodableHandler {
33-
self.run(handler as LambdaHandler)
32+
public static func run<Handler>(_ handler: Handler) where Handler: CodableLambdaHandler {
33+
self.run { _ in handler }
34+
}
35+
36+
/// Run a Lambda defined by implementing the `CodableLambdaHandler` protocol, having `In` and `Out` are `Decodable` and `Encodable` respectively.
37+
///
38+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
39+
public static func run<Handler>(_ provider: @escaping (EventLoop) throws -> Handler) where Handler: CodableLambdaHandler {
40+
self.run { try provider($0) as LambdaHandler }
3441
}
3542

3643
// for testing
37-
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping LambdaCodableClosure<In, Out>) -> Result<Int, Error> {
38-
return self.run(handler: LambdaClosureWrapper(closure), configuration: configuration)
44+
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping CodableLambda.Closure<In, Out>) -> Result<Int, Error> {
45+
return self.run(provider: { _ in ClosureWrapper(closure) }, configuration: configuration)
3946
}
4047

4148
// for testing
42-
internal static func run<Handler>(handler: Handler, configuration: Configuration = .init()) -> Result<Int, Error> where Handler: LambdaCodableHandler {
49+
internal static func run<Handler>(handler: Handler, configuration: Configuration = .init()) -> Result<Int, Error> where Handler: CodableLambdaHandler {
4350
return self.run(handler: handler as LambdaHandler, configuration: configuration)
4451
}
52+
53+
// for testing
54+
internal static func run<Handler>(provider: @escaping (EventLoop) throws -> Handler, configuration: Configuration = .init()) -> Result<Int, Error> where Handler: CodableLambdaHandler {
55+
return self.run(provider: { try provider($0) as LambdaHandler }, configuration: configuration)
56+
}
4557
}
4658

47-
/// A callback for a Lambda that returns a `Result<Out, Error>` result type, having `Out` extend `Encodable`.
48-
public typealias LambdaCodableCallback<Out> = (Result<Out, Error>) -> Void
59+
public enum CodableLambda {
60+
/// A completion handler for a Lambda that returns a `Result<Out, Error>` result type.
61+
public typealias CompletionHandler<Out> = (Result<Out, Error>?) -> Void
4962

50-
/// A processing closure for a Lambda that takes an `In` and returns an `Out` via `LambdaCodableCallback<Out>` asynchronously,
51-
/// having `In` and `Out` extending `Decodable` and `Encodable` respectively.
52-
public typealias LambdaCodableClosure<In, Out> = (LambdaContext, In, LambdaCodableCallback<Out>) -> Void
63+
/// A processing closure for a Lambda that takes a `String` and returns a `Result<Out, Error>` via `CompletionHandler` asynchronously.
64+
public typealias Closure<In, Out> = (Lambda.Context, In, CompletionHandler<Out>) -> Void
65+
}
5366

54-
/// A processing protocol for a Lambda that takes an `In` and returns an `Out` via `LambdaCodableCallback<Out>` asynchronously,
67+
/// A processing protocol for a Lambda that takes an `In` and returns an optional `Out`asynchronously via a `CompletionHandler<Out>` ,
5568
/// having `In` and `Out` extending `Decodable` and `Encodable` respectively.
56-
public protocol LambdaCodableHandler: LambdaHandler {
69+
public protocol CodableLambdaHandler: LambdaHandler {
5770
associatedtype In: Decodable
5871
associatedtype Out: Encodable
5972

60-
func handle(context: LambdaContext, payload: In, callback: @escaping LambdaCodableCallback<Out>)
6173
var codec: LambdaCodableCodec<In, Out> { get }
74+
75+
func handle(context: Lambda.Context, payload: In, callback: @escaping CodableLambda.CompletionHandler<Out>)
6276
}
6377

64-
/// Default implementation for `LambdaCodableHandler` codec which uses JSON via `LambdaCodableJsonCodec`.
78+
/// A processing protocol for a Lambda that takes a `In` and returns an optional `Out` asynchronously via an `EventLoopPromise`.
79+
public protocol CodablePromiseLambdaHandler: LambdaHandler {
80+
associatedtype In: Decodable
81+
associatedtype Out: Encodable
82+
83+
var codec: LambdaCodableCodec<In, Out> { get }
84+
85+
func handle(context: Lambda.Context, payload: In, promise: EventLoopPromise<Out?>)
86+
}
87+
88+
/// Default implementation for `CodableLambdaHandler` codec which uses JSON via `LambdaCodableJsonCodec`.
6589
/// Advanced users that want to inject their own codec can do it by overriding this.
66-
public extension LambdaCodableHandler {
90+
public extension CodableLambdaHandler {
6791
var codec: LambdaCodableCodec<In, Out> {
68-
return LambdaCodableJsonCodec<In, Out>()
92+
LambdaCodableJsonCodec<In, Out>()
6993
}
7094
}
7195

72-
/// LambdaCodableCodec is an abstract/empty implementation for codec which does `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding.
96+
/// Default implementation for `CodableLambdaHandler` codec which uses JSON via `LambdaCodableJsonCodec`.
97+
/// Advanced users that want to inject their own codec can do it by overriding this.
98+
public extension CodablePromiseLambdaHandler {
99+
var codec: LambdaCodableCodec<In, Out> {
100+
LambdaCodableJsonCodec<In, Out>()
101+
}
102+
}
103+
104+
/// LambdaCodableCodec is an abstract/empty implementation for codec which does `Encodable` -> `ByteBuffer` encoding and `ByteBuffer` -> `Decodable` decoding.
73105
// TODO: would be nicer to use a protocol instead of this "abstract class", but generics get in the way
74106
public class LambdaCodableCodec<In: Decodable, Out: Encodable> {
75107
func encode(_: Out) -> Result<ByteBuffer, Error> { fatalError("not implmented") }
76108
func decode(_: ByteBuffer) -> Result<In, Error> { fatalError("not implmented") }
77109
}
78110

79-
/// Default implementation of `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding
80-
public extension LambdaCodableHandler {
81-
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>) {
111+
/// Default implementation of `Encodable` -> `ByteBuffer` encoding and `ByteBuffer` -> `Decodable` decoding
112+
public extension CodableLambdaHandler {
113+
func handle(context: Lambda.Context, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer?>) {
82114
switch self.codec.decode(payload) {
83115
case .failure(let error):
84116
return promise.fail(Errors.requestDecoding(error))
85117
case .success(let payloadAsCodable):
86118
self.handle(context: context, payload: payloadAsCodable) { result in
87119
switch result {
120+
case .none:
121+
promise.succeed(nil)
88122
case .failure(let error):
89-
return promise.fail(error)
123+
promise.fail(error)
90124
case .success(let encodable):
91125
switch self.codec.encode(encodable) {
92126
case .failure(let error):
93-
return promise.fail(Errors.responseEncoding(error))
127+
promise.fail(Errors.responseEncoding(error))
94128
case .success(let buffer):
95-
return promise.succeed(buffer)
129+
promise.succeed(buffer)
96130
}
97131
}
98132
}
99133
}
100134
}
101135
}
102136

103-
/// LambdaCodableJsonCodec is an implementation of `LambdaCodableCodec` which does `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding
137+
/// Default implementation of `Encodable` -> `ByteBuffer` encoding and `ByteBuffer` -> `Decodable'`decoding
138+
public extension CodablePromiseLambdaHandler {
139+
func handle(context: Lambda.Context, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer?>) {
140+
switch self.codec.decode(payload) {
141+
case .failure(let error):
142+
return promise.fail(Errors.requestDecoding(error))
143+
case .success(let decodable):
144+
let encodablePromise = context.eventLoop.makePromise(of: Out?.self)
145+
encodablePromise.futureResult.flatMapThrowing { encodable in
146+
try encodable.flatMap { encodable in
147+
switch self.codec.encode(encodable) {
148+
case .failure(let error):
149+
throw Errors.responseEncoding(error)
150+
case .success(let buffer):
151+
return buffer
152+
}
153+
}
154+
}.cascade(to: promise)
155+
self.handle(context: context, payload: decodable, promise: encodablePromise)
156+
}
157+
}
158+
}
159+
160+
/// LambdaCodableJsonCodec is an implementation of `LambdaCodableCodec` which does `Encodable` -> `ByteBuffer` encoding and `ByteBuffer` -> `Decodable' decoding
104161
/// using JSONEncoder and JSONDecoder respectively.
105162
// This is a class as encoder amd decoder are a class, which means its cheaper to hold a reference to both in a class then a struct.
106163
private final class LambdaCodableJsonCodec<In: Decodable, Out: Encodable>: LambdaCodableCodec<In, Out> {
@@ -131,15 +188,16 @@ private final class LambdaCodableJsonCodec<In: Decodable, Out: Encodable>: Lambd
131188
}
132189
}
133190

134-
private struct LambdaClosureWrapper<In: Decodable, Out: Encodable>: LambdaCodableHandler {
191+
private struct ClosureWrapper<In: Decodable, Out: Encodable>: CodableLambdaHandler {
135192
typealias Codec = LambdaCodableJsonCodec<In, Out>
136193

137-
private let closure: LambdaCodableClosure<In, Out>
138-
init(_ closure: @escaping LambdaCodableClosure<In, Out>) {
194+
private let closure: CodableLambda.Closure<In, Out>
195+
196+
init(_ closure: @escaping CodableLambda.Closure<In, Out>) {
139197
self.closure = closure
140198
}
141199

142-
public func handle(context: LambdaContext, payload: In, callback: @escaping LambdaCodableCallback<Out>) {
200+
public func handle(context: Lambda.Context, payload: In, callback: @escaping CodableLambda.CompletionHandler<Out>) {
143201
self.closure(context, payload, callback)
144202
}
145203
}

Sources/SwiftAwsLambda/Lambda+String.swift

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,68 +16,108 @@ import NIO
1616

1717
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `String` payloads.
1818
extension Lambda {
19-
/// Run a Lambda defined by implementing the `LambdaStringClosure` protocol.
19+
/// Run a Lambda defined by implementing the `StringLambda.Closure` protocol.
2020
///
2121
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
22-
public static func run(_ closure: @escaping LambdaStringClosure) {
23-
self.run(LambdaClosureWrapper(closure))
22+
public static func run(_ closure: @escaping StringLambda.Closure) {
23+
self.run(ClosureWrapper(closure))
2424
}
2525

26-
/// Run a Lambda defined by implementing the `LambdaStringHandler` protocol.
26+
/// Run a Lambda defined by implementing the `StringLambdaHandler` protocol.
2727
///
2828
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
29-
public static func run(_ handler: LambdaStringHandler) {
30-
self.run(handler as LambdaHandler)
29+
public static func run(_ handler: StringLambdaHandler) {
30+
self.run { _ in handler }
31+
}
32+
33+
/// Run a Lambda defined by implementing the `StringLambdaHandler` protocol.
34+
///
35+
/// - note: This is a blocking operation that will run forever, as it's lifecycle is managed by the AWS Lambda Runtime Engine.
36+
public static func run(_ provider: @escaping (EventLoop) throws -> StringLambdaHandler) {
37+
self.run(provider: { try provider($0) as LambdaHandler })
3138
}
3239

3340
// for testing
34-
internal static func run(configuration: Configuration = .init(), _ closure: @escaping LambdaStringClosure) -> Result<Int, Error> {
35-
return self.run(handler: LambdaClosureWrapper(closure), configuration: configuration)
41+
internal static func run(configuration: Configuration = .init(), closure: @escaping StringLambda.Closure) -> Result<Int, Error> {
42+
return self.run(handler: ClosureWrapper(closure), configuration: configuration)
3643
}
3744

3845
// for testing
39-
internal static func run(handler: LambdaStringHandler, configuration: Configuration = .init()) -> Result<Int, Error> {
46+
internal static func run(handler: StringLambdaHandler, configuration: Configuration = .init()) -> Result<Int, Error> {
4047
return self.run(handler: handler as LambdaHandler, configuration: configuration)
4148
}
49+
50+
// for testing
51+
internal static func run(provider: @escaping (EventLoop) throws -> StringLambdaHandler, configuration: Configuration = .init()) -> Result<Int, Error> {
52+
return self.run(provider: { try provider($0) as LambdaHandler }, configuration: configuration)
53+
}
4254
}
4355

44-
/// A callback for a Lambda that returns a `Result<String, Error>` result type.
45-
public typealias LambdaStringCallback = (Result<String, Error>) -> Void
56+
public enum StringLambda {
57+
/// A completion handler for a Lambda that returns a `Result<String, Error>` result type.
58+
public typealias CompletionHandler = (Result<String, Error>?) -> Void
4659

47-
/// A processing closure for a Lambda that takes a `String` and returns a `LambdaStringResult` via `LambdaStringCallback` asynchronously.
48-
public typealias LambdaStringClosure = (LambdaContext, String, LambdaStringCallback) -> Void
60+
/// A processing closure for a Lambda that takes a `String` and returns a `Result<String, Error>` via `CompletionHandler` asynchronously.
61+
public typealias Closure = (Lambda.Context, String, CompletionHandler) -> Void
62+
}
4963

50-
/// A processing protocol for a Lambda that takes a `String` and returns a `LambdaStringResult` via `LambdaStringCallback` asynchronously.
51-
public protocol LambdaStringHandler: LambdaHandler {
52-
func handle(context: LambdaContext, payload: String, callback: @escaping LambdaStringCallback)
64+
/// A processing protocol for a Lambda that takes a `String` and returns an optional `String` asynchronously via an `CompletionHandler`.
65+
public protocol StringLambdaHandler: LambdaHandler {
66+
func handle(context: Lambda.Context, payload: String, callback: @escaping StringLambda.CompletionHandler)
5367
}
5468

55-
/// Default implementation of `String` -> `[UInt8]` encoding and `[UInt8]` -> `String' decoding
56-
public extension LambdaStringHandler {
57-
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>) {
69+
/// A processing protocol for a Lambda that takes a `String` and returns an optional `String` asynchronously via an `EventLoopPromise`.
70+
public protocol StringPromiseLambdaHandler: LambdaHandler {
71+
func handle(context: Lambda.Context, payload: String, promise: EventLoopPromise<String?>)
72+
}
73+
74+
/// Default implementation of `String` -> `ByteBuffer` encoding and `ByteBuffer` -> `String` decoding
75+
public extension StringLambdaHandler {
76+
func handle(context: Lambda.Context, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer?>) {
5877
guard let payload = payload.getString(at: payload.readerIndex, length: payload.readableBytes) else {
5978
return promise.fail(Errors.invalidBuffer)
6079
}
6180
self.handle(context: context, payload: payload) { result in
6281
switch result {
82+
case .none:
83+
promise.succeed(nil)
84+
case .failure(let error):
85+
promise.fail(error)
6386
case .success(let string):
6487
var buffer = context.allocator.buffer(capacity: string.utf8.count)
6588
buffer.writeString(string)
66-
return promise.succeed(buffer)
67-
case .failure(let error):
68-
return promise.fail(error)
89+
promise.succeed(buffer)
6990
}
7091
}
7192
}
7293
}
7394

74-
private struct LambdaClosureWrapper: LambdaStringHandler {
75-
private let closure: LambdaStringClosure
76-
init(_ closure: @escaping LambdaStringClosure) {
95+
/// Default implementation of `String` -> `ByteBuffer` encoding and `ByteBuffer` -> `String` decoding
96+
public extension StringPromiseLambdaHandler {
97+
func handle(context: Lambda.Context, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer?>) {
98+
guard let payload = payload.getString(at: payload.readerIndex, length: payload.readableBytes) else {
99+
return promise.fail(Errors.invalidBuffer)
100+
}
101+
let stringPromise = context.eventLoop.makePromise(of: String?.self)
102+
stringPromise.futureResult.map { string in
103+
string.flatMap { string in
104+
var buffer = context.allocator.buffer(capacity: string.utf8.count)
105+
buffer.writeString(string)
106+
return buffer
107+
}
108+
}.cascade(to: promise)
109+
self.handle(context: context, payload: payload, promise: stringPromise)
110+
}
111+
}
112+
113+
private struct ClosureWrapper: StringLambdaHandler {
114+
private let closure: StringLambda.Closure
115+
116+
init(_ closure: @escaping StringLambda.Closure) {
77117
self.closure = closure
78118
}
79119

80-
func handle(context: LambdaContext, payload: String, callback: @escaping LambdaStringCallback) {
120+
func handle(context: Lambda.Context, payload: String, callback: @escaping StringLambda.CompletionHandler) {
81121
self.closure(context, payload, callback)
82122
}
83123
}

0 commit comments

Comments
 (0)