Skip to content

Commit dbef3a1

Browse files
authored
LambdaCoding Changes and Tests (#8)
* Lint tests * Make decode synchronous like other methods * Make custom error message required * Support error body customization * Add helper for use with APIGatewayV2Requests * Add coder tests and helpers * Remove comment * Fix possible test error
1 parent 6f73df0 commit dbef3a1

File tree

9 files changed

+478
-46
lines changed

9 files changed

+478
-46
lines changed

Package.swift

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,10 @@ let package = Package(
1818
.package(url: "https://github.com/apple/swift-nio.git", .upToNextMajor(from: "2.43.1")),
1919
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", branch: "main"),
2020
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main")
21-
],
22-
targets: [
23-
.testTarget(
24-
name: "LambdaExtrasTests",
25-
dependencies: [
26-
"LambdaExtras"
27-
]
28-
)
2921
]
3022
)
3123

32-
let genericTargets: [Target] = [
24+
let targets: [Target] = [
3325
.target(
3426
name: "LambdaExtrasCore",
3527
dependencies: [
@@ -50,14 +42,21 @@ let genericTargets: [Target] = [
5042
dependencies: [
5143
"LambdaExtrasCore"
5244
]
45+
),
46+
.testTarget(
47+
name: "LambdaExtrasTests",
48+
dependencies: [
49+
"LambdaExtras",
50+
"LambdaMocks"
51+
]
5352
)
5453
]
5554

5655
#if os(macOS)
5756
package.dependencies.append(.package(url: "https://github.com/realm/SwiftLint.git", exact: "0.54.0"))
58-
for target in genericTargets {
57+
for target in targets {
5958
target.plugins = [.plugin(name: "SwiftLintPlugin", package: "SwiftLint")]
6059
}
6160
#endif
6261

63-
package.targets.append(contentsOf: genericTargets)
62+
package.targets = targets

Sources/LambdaExtras/APIGatewayCoder.swift

Lines changed: 16 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,28 @@ public struct APIGatewayCoder<E, O>: LambdaCoding where E: Codable, E: Sendable,
1616
let decoder: JSONDecoder
1717

1818
/// A closure returning a response body from the given underlying output.
19-
let responseBodyProvider: @Sendable (O) -> String?
19+
let responseBodyProvider: @Sendable (O) throws -> String?
20+
21+
/// A closure returns a response body from the given error.
22+
let errorBodyProvider: @Sendable (Error) throws -> String?
2023

2124
/// Creates an instance.
2225
///
2326
/// - Parameters:
2427
/// - decoder: A JSON decoder.
2528
/// - responseBody: A closure returning a response body from the given output.
29+
/// - errorBody: A closure returning a response body for the given error.
2630
public init(
2731
decoder: JSONDecoder = .init(),
28-
responseBody: @escaping @Sendable (O) -> String? = { _ in nil }
32+
responseBody: @escaping @Sendable (O) throws -> String? = { _ in nil },
33+
errorBody: @escaping @Sendable (Error) throws -> String? = { $0.localizedDescription }
2934
) {
3035
self.decoder = decoder
3136
self.responseBodyProvider = responseBody
37+
self.errorBodyProvider = errorBody
3238
}
3339

34-
public func decode(event: APIGatewayV2Request) async throws -> E {
40+
public func decode(event: APIGatewayV2Request) throws -> E {
3541
guard let body = event.body else {
3642
throw HandlerError.emptyBody
3743
}
@@ -42,42 +48,20 @@ public struct APIGatewayCoder<E, O>: LambdaCoding where E: Codable, E: Sendable,
4248
public func encode(output: O) throws -> APIGatewayV2Response {
4349
APIGatewayV2Response(
4450
statusCode: .ok,
45-
body: responseBodyProvider(output))
51+
body: try responseBodyProvider(output))
4652
}
4753

4854
public func encode(error: Error) throws -> APIGatewayV2Response {
55+
let statusCode: HTTPResponseStatus
4956
switch error {
5057
case HandlerError.emptyBody:
51-
return APIGatewayV2Response(
52-
statusCode: .badRequest,
53-
body: errorResponseBody(error.localizedDescription))
54-
55-
case HandlerError.envError:
56-
return APIGatewayV2Response(
57-
statusCode: .internalServerError,
58-
body: errorResponseBody(error.localizedDescription))
59-
60-
case HandlerError.custom(let message):
61-
return APIGatewayV2Response(
62-
statusCode: .internalServerError,
63-
body: message.flatMap(errorResponseBody))
64-
58+
statusCode = .badRequest
6559
default:
66-
return APIGatewayV2Response(
67-
statusCode: .internalServerError,
68-
body: errorResponseBody(error.localizedDescription))
60+
statusCode = .internalServerError
6961
}
70-
}
71-
}
7262

73-
private extension APIGatewayCoder {
74-
/// Returns a response body for the given error.
75-
///
76-
/// - Parameter message: The error message
77-
/// - Returns: The response body.
78-
private func errorResponseBody(_ message: String) -> String {
79-
"""
80-
{"reason": "\(message)"}
81-
"""
63+
return APIGatewayV2Response(
64+
statusCode: statusCode,
65+
body: try errorBodyProvider(error.localizedDescription))
8266
}
8367
}

Sources/LambdaExtrasCore/Extensions.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ public extension JSONDecoder {
5151
}
5252
}
5353

54+
public extension JSONSerialization {
55+
/// Returns an escaped string for the given JSON object data.
56+
///
57+
/// - Parameters:
58+
/// - jsonData: The data from which to generate the JSON string.
59+
/// - options: Options for creating the JSON data.
60+
/// - Returns: An escaped string representation of the given object `jsonData`.
61+
static func escapedString(with jsonData: Data, options: WritingOptions = []) throws -> String {
62+
let jsonString = String(decoding: jsonData, as: UTF8.self)
63+
let data = try data(withJSONObject: [jsonString], options: options)
64+
let encodedString = String(decoding: data, as: UTF8.self)
65+
return String(encodedString.dropLast().dropFirst())
66+
}
67+
}
68+
5469
public extension Optional {
5570
/// Convienence method to `throw` if an optional type has a `nil` value.
5671
///

Sources/LambdaExtrasCore/HandlerError.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public enum HandlerError: Error, Equatable, LocalizedError {
1414
/// The lambda context is missing an expected environment variable.
1515
case envError(String)
1616
/// A custom error.
17-
case custom(_ message: String?)
17+
case custom(_ message: String)
1818

1919
/// Retrieve the description for this error.
2020
public var errorDescription: String? {
@@ -24,7 +24,7 @@ public enum HandlerError: Error, Equatable, LocalizedError {
2424
case .envError(let variable):
2525
return "The environment does not contain the expected variable `\(variable)`."
2626
case .custom(let message):
27-
return message ?? "Unknown error."
27+
return message
2828
}
2929
}
3030
}

Sources/LambdaExtrasCore/Protocols/LambdaCoding.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public protocol LambdaCoding<Event, Output, UnderlyingEvent, UnderlyingOutput> {
2525
///
2626
/// - Parameter event: The event to encode.
2727
/// - Returns: An underlying event.
28-
func decode(event: Event) async throws -> UnderlyingEvent
28+
func decode(event: Event) throws -> UnderlyingEvent
2929

3030
/// Encodes the given output to that of a lambda.
3131
///

0 commit comments

Comments
 (0)