Skip to content

Commit 07c61f2

Browse files
committed
alternative api
1 parent b8a6466 commit 07c61f2

File tree

10 files changed

+150
-112
lines changed

10 files changed

+150
-112
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let package = Package(
1313
.package(url: "https://github.com/swift-server/swift-backtrace.git", from: "1.1.0"),
1414
],
1515
targets: [
16-
.target(name: "SwiftAwsLambda", dependencies: ["Logging", "Backtrace", "NIOHTTP1"]),
16+
.target(name: "SwiftAwsLambda", dependencies: ["Logging", "Backtrace", "NIOHTTP1", "NIOFoundationCompat"]),
1717
.testTarget(name: "SwiftAwsLambdaTests", dependencies: ["SwiftAwsLambda"]),
1818
// samples
1919
.target(name: "SwiftAwsLambdaSample", dependencies: ["SwiftAwsLambda"]),

Sources/SwiftAwsLambda/Lambda+Codable.swift

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import Foundation // for JSON
16+
import NIO
17+
import NIOFoundationCompat
1618

1719
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `Codable` payloads.
1820
/// This is the most common way to use this library in AWS Lambda, since its JSON based.
@@ -32,21 +34,18 @@ extension Lambda {
3234
}
3335

3436
// for testing
35-
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping LambdaCodableClosure<In, Out>) -> LambdaLifecycleResult {
37+
internal static func run<In: Decodable, Out: Encodable>(configuration: Configuration = .init(), closure: @escaping LambdaCodableClosure<In, Out>) -> Result<Int, Error> {
3638
return self.run(handler: LambdaClosureWrapper(closure), configuration: configuration)
3739
}
3840

3941
// for testing
40-
internal static func run<Handler>(handler: Handler, configuration: Configuration = .init()) -> LambdaLifecycleResult where Handler: LambdaCodableHandler {
42+
internal static func run<Handler>(handler: Handler, configuration: Configuration = .init()) -> Result<Int, Error> where Handler: LambdaCodableHandler {
4143
return self.run(handler: handler as LambdaHandler, configuration: configuration)
4244
}
4345
}
4446

45-
/// A result type for a Lambda that returns a generic `Out`, having `Out` extend `Encodable`.
46-
public typealias LambdaCodableResult<Out> = Result<Out, Error>
47-
48-
/// A callback for a Lambda that returns a `LambdaCodableResult<Out>` result type, having `Out` extend `Encodable`.
49-
public typealias LambdaCodableCallback<Out> = (LambdaCodableResult<Out>) -> Void
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
5049

5150
/// A processing closure for a Lambda that takes an `In` and returns an `Out` via `LambdaCodableCallback<Out>` asynchronously,
5251
/// having `In` and `Out` extending `Decodable` and `Encodable` respectively.
@@ -73,27 +72,27 @@ public extension LambdaCodableHandler {
7372
/// LambdaCodableCodec is an abstract/empty implementation for codec which does `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding.
7473
// TODO: would be nicer to use a protocol instead of this "abstract class", but generics get in the way
7574
public class LambdaCodableCodec<In: Decodable, Out: Encodable> {
76-
func encode(_: Out) -> Result<[UInt8], Error> { fatalError("not implmented") }
77-
func decode(_: [UInt8]) -> Result<In, Error> { fatalError("not implmented") }
75+
func encode(_: Out) -> Result<ByteBuffer, Error> { fatalError("not implmented") }
76+
func decode(_: ByteBuffer) -> Result<In, Error> { fatalError("not implmented") }
7877
}
7978

8079
/// Default implementation of `Encodable` -> `[UInt8]` encoding and `[UInt8]` -> `Decodable' decoding
8180
public extension LambdaCodableHandler {
82-
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping (LambdaResult) -> Void) {
81+
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>) {
8382
switch self.codec.decode(payload) {
8483
case .failure(let error):
85-
return callback(.failure(Errors.requestDecoding(error)))
84+
return promise.fail(Errors.requestDecoding(error))
8685
case .success(let payloadAsCodable):
8786
self.handle(context: context, payload: payloadAsCodable) { result in
8887
switch result {
8988
case .failure(let error):
90-
return callback(.failure(error))
89+
return promise.fail(error)
9190
case .success(let encodable):
9291
switch self.codec.encode(encodable) {
9392
case .failure(let error):
94-
return callback(.failure(Errors.responseEncoding(error)))
95-
case .success(let codableAsBytes):
96-
return callback(.success(codableAsBytes))
93+
return promise.fail(Errors.responseEncoding(error))
94+
case .success(let buffer):
95+
return promise.succeed(buffer)
9796
}
9897
}
9998
}
@@ -107,18 +106,25 @@ public extension LambdaCodableHandler {
107106
private final class LambdaCodableJsonCodec<In: Decodable, Out: Encodable>: LambdaCodableCodec<In, Out> {
108107
private let encoder = JSONEncoder()
109108
private let decoder = JSONDecoder()
109+
private let allocator = ByteBufferAllocator()
110110

111-
public override func encode(_ value: Out) -> Result<[UInt8], Error> {
111+
public override func encode(_ value: Out) -> Result<ByteBuffer, Error> {
112112
do {
113-
return .success(try [UInt8](self.encoder.encode(value)))
113+
let data = try self.encoder.encode(value)
114+
var buffer = self.allocator.buffer(capacity: data.count)
115+
buffer.writeBytes(data)
116+
return .success(buffer)
114117
} catch {
115118
return .failure(error)
116119
}
117120
}
118121

119-
public override func decode(_ data: [UInt8]) -> Result<In, Error> {
122+
public override func decode(_ buffer: ByteBuffer) -> Result<In, Error> {
120123
do {
121-
return .success(try self.decoder.decode(In.self, from: Data(data)))
124+
guard let data = buffer.getData(at: buffer.readerIndex, length: buffer.readableBytes) else {
125+
throw Errors.invalidBuffer
126+
}
127+
return .success(try self.decoder.decode(In.self, from: data))
122128
} catch {
123129
return .failure(error)
124130
}
@@ -141,4 +147,5 @@ private struct LambdaClosureWrapper<In: Decodable, Out: Encodable>: LambdaCodabl
141147
private enum Errors: Error {
142148
case responseEncoding(Error)
143149
case requestDecoding(Error)
150+
case invalidBuffer
144151
}

Sources/SwiftAwsLambda/Lambda+String.swift

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15+
import NIO
16+
1517
/// Extension to the `Lambda` companion to enable execution of Lambdas that take and return `String` payloads.
1618
extension Lambda {
1719
/// Run a Lambda defined by implementing the `LambdaStringClosure` protocol.
@@ -29,21 +31,18 @@ extension Lambda {
2931
}
3032

3133
// for testing
32-
internal static func run(configuration: Configuration = .init(), _ closure: @escaping LambdaStringClosure) -> LambdaLifecycleResult {
34+
internal static func run(configuration: Configuration = .init(), _ closure: @escaping LambdaStringClosure) -> Result<Int, Error> {
3335
return self.run(handler: LambdaClosureWrapper(closure), configuration: configuration)
3436
}
3537

3638
// for testing
37-
internal static func run(handler: LambdaStringHandler, configuration: Configuration = .init()) -> LambdaLifecycleResult {
39+
internal static func run(handler: LambdaStringHandler, configuration: Configuration = .init()) -> Result<Int, Error> {
3840
return self.run(handler: handler as LambdaHandler, configuration: configuration)
3941
}
4042
}
4143

42-
/// A result type for a Lambda that returns a `String`.
43-
public typealias LambdaStringResult = Result<String, Error>
44-
45-
/// A callback for a Lambda that returns a `LambdaStringResult` result type.
46-
public typealias LambdaStringCallback = (LambdaStringResult) -> Void
44+
/// A callback for a Lambda that returns a `Result<String, Error>` result type.
45+
public typealias LambdaStringCallback = (Result<String, Error>) -> Void
4746

4847
/// A processing closure for a Lambda that takes a `String` and returns a `LambdaStringResult` via `LambdaStringCallback` asynchronously.
4948
public typealias LambdaStringClosure = (LambdaContext, String, LambdaStringCallback) -> Void
@@ -55,13 +54,18 @@ public protocol LambdaStringHandler: LambdaHandler {
5554

5655
/// Default implementation of `String` -> `[UInt8]` encoding and `[UInt8]` -> `String' decoding
5756
public extension LambdaStringHandler {
58-
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping LambdaCallback) {
59-
self.handle(context: context, payload: String(decoding: payload, as: UTF8.self)) { result in
57+
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>) {
58+
guard let payload = payload.getString(at: payload.readerIndex, length: payload.readableBytes) else {
59+
return promise.fail(Errors.invalidBuffer)
60+
}
61+
self.handle(context: context, payload: payload) { result in
6062
switch result {
6163
case .success(let string):
62-
return callback(.success([UInt8](string.utf8)))
64+
var buffer = context.allocator.buffer(capacity: string.utf8.count)
65+
buffer.writeString(string)
66+
return promise.succeed(buffer)
6367
case .failure(let error):
64-
return callback(.failure(error))
68+
return promise.fail(error)
6569
}
6670
}
6771
}
@@ -77,3 +81,7 @@ private struct LambdaClosureWrapper: LambdaStringHandler {
7781
self.closure(context, payload, callback)
7882
}
7983
}
84+
85+
private enum Errors: Error {
86+
case invalidBuffer
87+
}

Sources/SwiftAwsLambda/Lambda.swift

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,14 @@ public enum Lambda {
4444
// for testing and internal use
4545
@usableFromInline
4646
@discardableResult
47-
internal static func run(configuration: Configuration = .init(), closure: @escaping LambdaClosure) -> LambdaLifecycleResult {
47+
internal static func run(configuration: Configuration = .init(), closure: @escaping LambdaClosure) -> Result<Int, Error> {
4848
return self.run(handler: LambdaClosureWrapper(closure), configuration: configuration)
4949
}
5050

5151
// for testing and internal use
5252
@usableFromInline
5353
@discardableResult
54-
internal static func run(handler: LambdaHandler, configuration: Configuration = .init()) -> LambdaLifecycleResult {
54+
internal static func run(handler: LambdaHandler, configuration: Configuration = .init()) -> Result<Int, Error> {
5555
do {
5656
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1) // only need one thread, will improve performance
5757
defer { try! eventLoopGroup.syncShutdownGracefully() }
@@ -252,32 +252,20 @@ public enum Lambda {
252252
}
253253
}
254254

255-
/// A result type for a Lambda that returns a `[UInt8]`.
256-
public typealias LambdaResult = Result<[UInt8], Error>
257-
258-
public typealias LambdaCallback = (LambdaResult) -> Void
255+
/// A callback for a Lambda that returns a `Result<[UInt8], Error>`.
256+
public typealias LambdaCallback = (Result<[UInt8], Error>) -> Void
259257

260258
/// A processing closure for a Lambda that takes a `[UInt8]` and returns a `LambdaResult` result type asynchronously.
261259
public typealias LambdaClosure = (LambdaContext, [UInt8], LambdaCallback) -> Void
262260

263-
/// A result type for a Lambda initialization.
264-
public typealias LambdaInitResult = Result<Void, Error>
265-
266-
/// A callback to provide the result of Lambda initialization.
267-
public typealias LambdaInitCallBack = (LambdaInitResult) -> Void
268-
269261
/// A processing protocol for a Lambda that takes a `[UInt8]` and returns a `LambdaResult` result type asynchronously.
270262
public protocol LambdaHandler {
271-
/// Initializes the `LambdaHandler`.
272-
func initialize(callback: @escaping LambdaInitCallBack)
273-
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping LambdaCallback)
263+
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>)
274264
}
275265

276-
extension LambdaHandler {
277-
@inlinable
278-
public func initialize(callback: @escaping LambdaInitCallBack) {
279-
callback(.success(()))
280-
}
266+
public protocol InitializableLambdaHandler {
267+
/// Initializes the `LambdaHandler`.
268+
func initialize(promise: EventLoopPromise<Void>)
281269
}
282270

283271
public struct LambdaContext {
@@ -289,6 +277,8 @@ public struct LambdaContext {
289277
public let clientContext: String?
290278
public let deadline: String?
291279
// utliity
280+
public let eventLoop: EventLoop
281+
public let allocator: ByteBufferAllocator
292282
public let logger: Logger
293283

294284
public init(requestId: String,
@@ -297,13 +287,17 @@ public struct LambdaContext {
297287
cognitoIdentity: String? = nil,
298288
clientContext: String? = nil,
299289
deadline: String? = nil,
290+
eventLoop: EventLoop,
300291
logger: Logger) {
301292
self.requestId = requestId
302293
self.traceId = traceId
303294
self.invokedFunctionArn = invokedFunctionArn
304295
self.cognitoIdentity = cognitoIdentity
305296
self.clientContext = clientContext
306297
self.deadline = deadline
298+
// utility
299+
self.eventLoop = eventLoop
300+
self.allocator = ByteBufferAllocator()
307301
// mutate logger with context
308302
var logger = logger
309303
logger[metadataKey: "awsRequestId"] = .string(requestId)
@@ -314,9 +308,6 @@ public struct LambdaContext {
314308
}
315309
}
316310

317-
@usableFromInline
318-
internal typealias LambdaLifecycleResult = Result<Int, Error>
319-
320311
private struct LambdaClosureWrapper: LambdaHandler {
321312
private let closure: LambdaClosure
322313
init(_ closure: @escaping LambdaClosure) {
@@ -326,4 +317,17 @@ private struct LambdaClosureWrapper: LambdaHandler {
326317
func handle(context: LambdaContext, payload: [UInt8], callback: @escaping LambdaCallback) {
327318
self.closure(context, payload, callback)
328319
}
320+
321+
func handle(context: LambdaContext, payload: ByteBuffer, promise: EventLoopPromise<ByteBuffer>) {
322+
self.closure(context, payload.getBytes(at: payload.readerIndex, length: payload.readableBytes) ?? []) { result in
323+
switch result {
324+
case .success(let bytes):
325+
var buffer = context.allocator.buffer(capacity: bytes.count)
326+
buffer.writeBytes(bytes)
327+
promise.succeed(buffer)
328+
case .failure(let error):
329+
promise.fail(error)
330+
}
331+
}
332+
}
329333
}

0 commit comments

Comments
 (0)