Skip to content

Commit b4a6a4f

Browse files
committed
Add documentation, add new internal LambdaRuntimeClientResponseStreamWriter, move Foundation import from AWSLambdaRuntimeCore
1 parent 804bed7 commit b4a6a4f

File tree

5 files changed

+145
-14
lines changed

5 files changed

+145
-14
lines changed

Sources/AWSLambdaRuntime/Lambda+Codable.swift

+7
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
import NIOCore
1717
import NIOFoundationCompat
1818

19+
#if canImport(FoundationEssentials)
20+
import FoundationEssentials
21+
#else
1922
import struct Foundation.Data
2023
import class Foundation.JSONDecoder
2124
import class Foundation.JSONEncoder
25+
#endif
2226

2327
// MARK: - SimpleLambdaHandler Codable support
2428

@@ -138,3 +142,6 @@ extension Lambda {
138142
extension JSONDecoder: LambdaCodableDecoder {}
139143

140144
extension JSONEncoder: LambdaCodableEncoder {}
145+
146+
extension JSONDecoder: AWSLambdaRuntimeCore.LambdaEventDecoder {}
147+
extension JSONEncoder: AWSLambdaRuntimeCore.LambdaOutputEncoder {}

Sources/AWSLambdaRuntimeCore/LambdaRuntimeClientProtocol.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,15 @@
1414

1515
import NIOCore
1616

17+
package protocol LambdaRuntimeClientResponseStreamWriter: LambdaResponseStreamWriter {
18+
mutating func write(_ buffer: ByteBuffer) async throws
19+
func finish() async throws
20+
func writeAndFinish(_ buffer: ByteBuffer) async throws
21+
func reportError(_ error: any Error) async throws
22+
}
23+
1724
package protocol LambdaRuntimeClientProtocol {
18-
associatedtype Writer: LambdaResponseStreamWriter
25+
associatedtype Writer: LambdaRuntimeClientResponseStreamWriter
1926

2027
func nextInvocation() async throws -> (Invocation, Writer)
2128
}

Sources/AWSLambdaRuntimeCore/NewLambda+JSON.swift

+45-8
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,55 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import NIOCore
16-
import NIOFoundationCompat
17-
18-
import class Foundation.JSONDecoder
19-
import class Foundation.JSONEncoder
2016

17+
/// The protocol a decoder must conform to so that it can be used with ``LambdaCodableAdapter`` to decode incoming
18+
/// ``ByteBuffer`` events.
2119
package protocol LambdaEventDecoder {
20+
/// Decode the ``ByteBuffer`` representing the received event into the generic ``Event`` type
21+
/// the handler will receive.
22+
/// - Parameters:
23+
/// - type: The type of the object to decode the buffer into.
24+
/// - buffer: The buffer to be decoded.
25+
/// - Returns: An object containing the decoded data.
2226
func decode<Event: Decodable>(_ type: Event.Type, from buffer: ByteBuffer) throws -> Event
2327
}
2428

29+
/// The protocol an encoder must conform to so that it can be used with ``LambdaCodableAdapter`` to encode the generic
30+
/// ``Output`` object into a ``ByteBuffer``.
2531
package protocol LambdaOutputEncoder {
32+
/// Encode the generic type `Output` the handler has returned into a ``ByteBuffer``.
33+
/// - Parameters:
34+
/// - value: The object to encode into a ``ByteBuffer``.
35+
/// - buffer: The ``ByteBuffer`` where the encoded value will be written to.
2636
func encode<Output: Encodable>(_ value: Output, into buffer: inout ByteBuffer) throws
2737
}
2838

29-
extension JSONEncoder: LambdaOutputEncoder {}
30-
31-
extension JSONDecoder: LambdaEventDecoder {}
32-
3339
package struct VoidEncoder: LambdaOutputEncoder {
3440
package func encode<Output>(_ value: Output, into buffer: inout NIOCore.ByteBuffer) throws where Output: Encodable {
3541
fatalError("LambdaOutputEncoder must never be called on a void output")
3642
}
3743
}
3844

45+
/// Adapts a ``NewLambdaHandler`` conforming handler to conform to ``LambdaWithBackgroundProcessingHandler``.
3946
package struct LambdaHandlerAdapter<
4047
Event: Decodable,
4148
Output,
4249
Handler: NewLambdaHandler
4350
>: LambdaWithBackgroundProcessingHandler where Handler.Event == Event, Handler.Output == Output {
4451
let handler: Handler
4552

53+
/// Initializes an instance given a concrete handler.
54+
/// - Parameter handler: The ``NewLambdaHandler`` conforming handler that is to be adapted to ``LambdaWithBackgroundProcessingHandler``.
4655
package init(handler: Handler) {
4756
self.handler = handler
4857
}
4958

59+
/// Passes the generic ``Event`` object to the ``NewLambdaHandler/handle(_:context:)`` function, and
60+
/// the resulting output is then written to ``LambdaWithBackgroundProcessingHandler``'s `outputWriter`.
61+
/// - Parameters:
62+
/// - event: The received event.
63+
/// - outputWriter: The writer to write the computed response to.
64+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
5065
package func handle(
5166
_ event: Event,
5267
outputWriter: consuming some LambdaResponseWriter<Output>,
@@ -57,6 +72,7 @@ package struct LambdaHandlerAdapter<
5772
}
5873
}
5974

75+
/// Adapts a ``LambdaWithBackgroundProcessingHandler`` conforming handler to conform to ``StreamingLambdaHandler``.
6076
package struct LambdaCodableAdapter<
6177
Handler: LambdaWithBackgroundProcessingHandler,
6278
Event: Decodable,
@@ -69,18 +85,32 @@ package struct LambdaCodableAdapter<
6985
let decoder: Decoder
7086
private var byteBuffer: ByteBuffer = .init()
7187

88+
/// Initializes an instance given an encoder, decoder, and a handler with a non-`Void` output.
89+
/// - Parameters:
90+
/// - encoder: The encoder object that will be used to encode the generic ``Output`` obtained from the `handler`'s `outputWriter` into a ``ByteBuffer``.
91+
/// - decoder: The decoder object that will be used to decode the received ``ByteBuffer`` event into the generic ``Event`` type served to the `handler`.
92+
/// - handler: The handler object.
7293
package init(encoder: Encoder, decoder: Decoder, handler: Handler) where Output: Encodable {
7394
self.encoder = encoder
7495
self.decoder = decoder
7596
self.handler = handler
7697
}
7798

99+
/// Initializes an instance given a decoder, and a handler with a `Void` output.
100+
/// - Parameters:
101+
/// - decoder: The decoder object that will be used to decode the received ``ByteBuffer`` event into the generic ``Event`` type served to the `handler`.
102+
/// - handler: The handler object.
78103
package init(decoder: Decoder, handler: Handler) where Output == Void, Encoder == VoidEncoder {
79104
self.encoder = VoidEncoder()
80105
self.decoder = decoder
81106
self.handler = handler
82107
}
83108

109+
/// A ``StreamingLambdaHandler/handle(_:responseWriter:context:)`` wrapper.
110+
/// - Parameters:
111+
/// - event: The received event.
112+
/// - outputWriter: The writer to write the computed response to.
113+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
84114
package mutating func handle(
85115
_ request: ByteBuffer,
86116
responseWriter: some LambdaResponseStreamWriter,
@@ -93,16 +123,23 @@ package struct LambdaCodableAdapter<
93123
}
94124
}
95125

126+
/// A ``LambdaResponseStreamWriter`` wrapper that conforms to ``LambdaResponseWriter``.
96127
package struct ResponseWriter<Output>: LambdaResponseWriter {
97128
let underlyingStreamWriter: LambdaResponseStreamWriter
98129
let encoder: LambdaOutputEncoder
99130
var byteBuffer = ByteBuffer()
100131

132+
/// Initializes an instance given an encoder and an underlying ``LambdaResponseStreamWriter``.
133+
/// - Parameters:
134+
/// - encoder: The encoder object that will be used to encode the generic ``Output`` into a ``ByteBuffer``, which will then be passed to `streamWriter`.
135+
/// - streamWriter: The underlying ``LambdaResponseStreamWriter`` that will be wrapped.
101136
package init(encoder: LambdaOutputEncoder, streamWriter: LambdaResponseStreamWriter) {
102137
self.encoder = encoder
103138
self.underlyingStreamWriter = streamWriter
104139
}
105140

141+
/// Passes the `response` argument to ``LambdaResponseStreamWriter/writeAndFinish(_:)``.
142+
/// - Parameter response: The generic ``Output`` object that will be passed to ``LambdaResponseStreamWriter/writeAndFinish(_:)``.
106143
package mutating func write(response: Output) async throws {
107144
if Output.self == Void.self {
108145
try await self.underlyingStreamWriter.finish()

Sources/AWSLambdaRuntimeCore/NewLambdaHandlers.swift

+84-4
Original file line numberDiff line numberDiff line change
@@ -14,59 +14,129 @@
1414

1515
import NIOCore
1616

17+
/// The base handler protocol that receives a ``ByteBuffer`` representing the incoming event and returns the response as a ``ByteBuffer`` too.
18+
/// This handler protocol supports response streaming. Bytes can be streamed outwards through the ``LambdaResponseStreamWriter``
19+
/// passed as an argument in the ``handle(_:responseWriter:context:)`` function.
20+
/// Background work can also be executed after returning the response. After closing the response stream by calling
21+
/// ``LambdaResponseStreamWriter/finish()`` or ``LambdaResponseStreamWriter/writeAndFinish(_:)``,
22+
/// the ``handle(_:responseWriter:context:)`` function is free to execute any background work.
1723
package protocol StreamingLambdaHandler {
24+
/// The handler function -- implement the business logic of the Lambda function here.
25+
/// - Parameters:
26+
/// - event: The invocation's input data.
27+
/// - responseWriter: A ``LambdaResponseStreamWriter`` to write the invocation's response to.
28+
/// If no response or error is written to `responseWriter` an error will be reported to the invoker.
29+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
30+
/// - Throws:
31+
/// How the thrown error will be handled by the runtime:
32+
/// - An invocation error will be reported if the error is thrown before the first call to
33+
/// ``LambdaResponseStreamWriter/write(_:)``.
34+
/// - If the error is thrown after call(s) to ``LambdaResponseStreamWriter/write(_:)`` but before
35+
/// a call to ``LambdaResponseStreamWriter/finish()``, the response stream will be closed and trailing
36+
/// headers will be sent.
37+
/// - If ``LambdaResponseStreamWriter/finish()`` has already been called before the error is thrown, the
38+
/// error will be logged.
1839
mutating func handle(
1940
_ event: ByteBuffer,
2041
responseWriter: some LambdaResponseStreamWriter,
2142
context: NewLambdaContext
2243
) async throws
2344
}
2445

46+
/// A writer object to write the Lambda response stream into. The HTTP response is started lazily.
47+
/// before the first call to ``write(_:)`` or ``writeAndFinish(_:)``.
2548
package protocol LambdaResponseStreamWriter {
49+
/// Write a response part into the stream. Bytes written are streamed continually.
50+
/// - Parameter buffer: The buffer to write.
2651
mutating func write(_ buffer: ByteBuffer) async throws
52+
53+
/// End the response stream and the underlying HTTP response.
2754
func finish() async throws
55+
56+
/// Write a response part into the stream and then end the stream as well as the underlying HTTP response.
57+
/// - Parameter buffer: The buffer to write.
2858
func writeAndFinish(_ buffer: ByteBuffer) async throws
29-
func reportError(_ error: any Error) async throws
3059
}
3160

61+
/// This handler protocol is intended to serve the most common use-cases.
62+
/// This protocol is completely agnostic to any encoding/decoding -- decoding the received event invocation into an ``Event`` object and encoding the returned ``Output`` object is handled by the library.
63+
/// The``handle(_:context:)`` function simply receives the generic ``Event`` object as input and returns the generic ``Output`` object.
64+
///
65+
/// - note: This handler protocol does not support response streaming because the output has to be encoded prior to it being sent, e.g. it is not possible to encode a partial/incomplete JSON string.
66+
/// This protocol also does not support the execution of background work after the response has been returned -- the ``LambdaWithBackgroundProcessingHandler`` protocol caters for such use-cases.
3267
package protocol NewLambdaHandler {
68+
/// Generic input type.
69+
/// The body of the request sent to Lambda will be decoded into this type for the handler to consume.
3370
associatedtype Event: Decodable
71+
/// Generic output type.
72+
/// This is the return type of the ``handle(_:context:)`` function.
3473
associatedtype Output
3574

75+
/// Implement the business logic of the Lambda function here.
76+
/// - Parameters:
77+
/// - event: The generic ``Event`` object representing the invocation's input data.
78+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
79+
/// - Returns: A generic ``Output`` object representing the computed result.
3680
func handle(_ event: Event, context: NewLambdaContext) async throws -> Output
3781
}
3882

83+
/// This protocol is exactly like ``NewLambdaHandler``, with the only difference being the added support for executing background
84+
/// work after the result has been sent to the AWS Lambda control plane.
85+
/// This is achieved by not having a return type in the `handle` function. The output is instead written into a
86+
/// ``LambdaResponseWriter``that is passed in as an argument, meaning that the ``handle(_:)`` function is then free to implement
87+
/// any background work after the result has been sent to the AWS Lambda control plane.
3988
package protocol LambdaWithBackgroundProcessingHandler {
89+
/// Generic input type.
90+
/// The body of the request sent to Lambda will be decoded into this type for the handler to consume.
4091
associatedtype Event: Decodable
92+
/// Generic output type.
93+
/// This is the type that the `handle` function will send through the ``LambdaResponseWriter``.
4194
associatedtype Output
4295

43-
/// The business logic of the Lambda function. Receives a generic input type and returns a generic output type.
44-
/// Agnostic to JSON encoding/decoding
96+
/// Implement the business logic of the Lambda function here.
97+
/// - Parameters:
98+
/// - event: The generic ``Event`` object representing the invocation's input data.
99+
/// - outputWriter: The writer to send the computed response to. A call to `outputWriter.write(_:)` will return the response to the AWS Lambda response endpoint.
100+
/// Any background work can then be executed before returning.
101+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
45102
func handle(
46103
_ event: Event,
47104
outputWriter: some LambdaResponseWriter<Output>,
48105
context: NewLambdaContext
49106
) async throws
50107
}
51108

109+
/// Used with ``LambdaWithBackgroundProcessingHandler``.
110+
/// A mechanism to "return" an output from ``LambdaWithBackgroundProcessingHandler/handle(_:outputWriter:context:)`` without the function needing to
111+
/// have a return type and exit at that point. This allows for background work to be executed _after_ a response has been sent to the AWS Lambda response endpoint.
52112
package protocol LambdaResponseWriter<Output>: ~Copyable {
53113
associatedtype Output
54-
/// Sends the generic Output object (representing the computed result of the handler)
114+
/// Sends the generic ``Output`` object (representing the computed result of the handler)
55115
/// to the AWS Lambda response endpoint.
56116
/// This function simply serves as a mechanism to return the computed result from a handler function
57117
/// without an explicit `return`.
58118
mutating func write(response: Output) async throws
59119
}
60120

121+
/// A ``StreamingLambdaHandler`` conforming handler object that can be constructed with a closure.
122+
/// Allows for a handler to be defined in a clean manner, leveraging Swift's trailing closure syntax.
61123
package struct StreamingClosureHandler: StreamingLambdaHandler {
62124
let body: @Sendable (ByteBuffer, LambdaResponseStreamWriter, NewLambdaContext) async throws -> Void
63125

126+
/// Initialize an instance from a handler function in the form of a closure.
127+
/// - Parameter body: The handler function written as a closure.
64128
package init(
65129
body: @Sendable @escaping (ByteBuffer, LambdaResponseStreamWriter, NewLambdaContext) async throws -> Void
66130
) {
67131
self.body = body
68132
}
69133

134+
/// Calls the provided `self.body` closure with the ``ByteBuffer`` invocation event, the ``LambdaResponseStreamWriter``, and the ``NewLambdaContext``
135+
/// - Parameters:
136+
/// - event: The invocation's input data.
137+
/// - responseWriter: A ``LambdaResponseStreamWriter`` to write the invocation's response to.
138+
/// If no response or error is written to `responseWriter` an error will be reported to the invoker.
139+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
70140
package func handle(
71141
_ request: ByteBuffer,
72142
responseWriter: some LambdaResponseStreamWriter,
@@ -76,17 +146,27 @@ package struct StreamingClosureHandler: StreamingLambdaHandler {
76146
}
77147
}
78148

149+
/// A ``NewLambdaHandler`` conforming handler object that can be constructed with a closure.
150+
/// Allows for a handler to be defined in a clean manner, leveraging Swift's trailing closure syntax.
79151
package struct ClosureHandler<Event: Decodable, Output>: NewLambdaHandler {
80152
let body: (Event, NewLambdaContext) async throws -> Output
81153

154+
/// Initialize with a closure handler over generic `Input` and `Output` types.
155+
/// - Parameter body: The handler function written as a closure.
82156
package init(body: @escaping (Event, NewLambdaContext) async throws -> Output) where Output: Encodable {
83157
self.body = body
84158
}
85159

160+
/// Initialize with a closure handler over a generic `Input` type, and a `Void` `Output`.
161+
/// - Parameter body: The handler function written as a closure.
86162
package init(body: @escaping (Event, NewLambdaContext) async throws -> Void) where Output == Void {
87163
self.body = body
88164
}
89165

166+
/// Calls the provided `self.body` closure with the generic ``Event`` object representing the incoming event, and the ``NewLambdaContext``
167+
/// - Parameters:
168+
/// - event: The generic ``Event`` object representing the invocation's input data.
169+
/// - context: The ``NewLambdaContext`` containing the invocation's metadata.
90170
package func handle(_ event: Event, context: NewLambdaContext) async throws -> Output {
91171
try await self.body(event, context)
92172
}

Tests/AWSLambdaRuntimeCoreTests/LambdaMockClient.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import Foundation
1717
import Logging
1818
import NIOCore
1919

20-
struct LambdaMockWriter: LambdaResponseStreamWriter {
20+
struct LambdaMockWriter: LambdaRuntimeClientResponseStreamWriter {
2121
var underlying: LambdaMockClient
2222

2323
init(underlying: LambdaMockClient) {

0 commit comments

Comments
 (0)