Skip to content

Commit 1bd641d

Browse files
authored
Merge branch 'main' into main
2 parents b8180d8 + 850df86 commit 1bd641d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2180
-1006
lines changed

.github/workflows/pull_request.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ on:
44
pull_request:
55
types: [opened, reopened, synchronize]
66

7+
# As per Checkov CKV2_GHA_1
8+
permissions: read-all
9+
710
jobs:
811
soundness:
912
name: Soundness
@@ -23,7 +26,7 @@ jobs:
2326
with:
2427
linux_5_9_enabled: false
2528
linux_5_10_enabled: false
26-
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error"
29+
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error"
2730
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error"
2831

2932
swift-6-language-mode:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ xcuserdata
1010
Package.resolved
1111
.serverless
1212
.devcontainer
13+
.amazonq
14+
.ash

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version:5.8
1+
// swift-tools-version:6.0
22

33
import PackageDescription
44

[email protected]

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// swift-tools-version:5.10
2+
3+
import PackageDescription
4+
5+
let swiftSettings: [SwiftSetting] = [.enableExperimentalFeature("StrictConcurrency=complete")]
6+
7+
let package = Package(
8+
name: "swift-aws-lambda-events",
9+
platforms: [.macOS(.v14)],
10+
products: [
11+
.library(name: "AWSLambdaEvents", targets: ["AWSLambdaEvents"])
12+
],
13+
dependencies: [
14+
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
15+
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.0.0"),
16+
],
17+
targets: [
18+
.target(
19+
name: "AWSLambdaEvents",
20+
dependencies: [
21+
.product(name: "HTTPTypes", package: "swift-http-types")
22+
],
23+
swiftSettings: swiftSettings
24+
)
25+
]
26+
)

Sources/AWSLambdaEvents/APIGateway+V2.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ public struct APIGatewayV2Request: Encodable, Sendable {
126126
}
127127
}
128128

129+
extension APIGatewayV2Request: DecodableRequest {}
130+
129131
public struct APIGatewayV2Response: Codable, Sendable {
130132
public var statusCode: HTTPResponse.Status
131133
public var headers: HTTPHeaders?
@@ -148,6 +150,8 @@ public struct APIGatewayV2Response: Codable, Sendable {
148150
}
149151
}
150152

153+
extension APIGatewayV2Response: EncodableResponse {}
154+
151155
extension APIGatewayV2Request: Decodable {
152156
public init(from decoder: Decoder) throws {
153157
let container = try decoder.container(keyedBy: CodingKeys.self)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) YEARS Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
/// `APIGatewayWebSocketRequest` is a variation of the`APIGatewayV2Request`
16+
/// and contains data coming from the WebSockets API Gateway.
17+
public struct APIGatewayWebSocketRequest: Codable {
18+
/// `Context` contains information to identify the AWS account and resources invoking the Lambda function.
19+
public struct Context: Codable {
20+
public struct Identity: Codable {
21+
public let sourceIp: String
22+
}
23+
24+
public let routeKey: String
25+
public let eventType: String
26+
public let extendedRequestId: String
27+
/// The request time in format: 23/Apr/2020:11:08:18 +0000
28+
public let requestTime: String
29+
public let messageDirection: String
30+
public let stage: String
31+
public let connectedAt: UInt64
32+
public let requestTimeEpoch: UInt64
33+
public let identity: Identity
34+
public let requestId: String
35+
public let domainName: String
36+
public let connectionId: String
37+
public let apiId: String
38+
}
39+
40+
public let headers: HTTPHeaders?
41+
public let multiValueHeaders: HTTPMultiValueHeaders?
42+
public let context: Context
43+
public let body: String?
44+
public let isBase64Encoded: Bool?
45+
46+
enum CodingKeys: String, CodingKey {
47+
case headers
48+
case multiValueHeaders
49+
case context = "requestContext"
50+
case body
51+
case isBase64Encoded
52+
}
53+
}
54+
55+
/// `APIGatewayWebSocketResponse` is a type alias for `APIGatewayV2Request`.
56+
/// Typically, lambda WebSockets servers send clients data via
57+
/// the ApiGatewayManagementApi mechanism. However, APIGateway does require
58+
/// lambda servers to return some kind of status when APIGateway invokes them.
59+
/// This can be as simple as always returning a 200 "OK" response for all
60+
/// WebSockets requests (the ApiGatewayManagementApi can return any errors to
61+
/// WebSockets clients).
62+
public typealias APIGatewayWebSocketResponse = APIGatewayV2Response
63+
64+
#if swift(>=5.6)
65+
extension APIGatewayWebSocketRequest: Sendable {}
66+
extension APIGatewayWebSocketRequest.Context: Sendable {}
67+
extension APIGatewayWebSocketRequest.Context.Identity: Sendable {}
68+
#endif
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import HTTPTypes
16+
17+
#if canImport(FoundationEssentials)
18+
import FoundationEssentials
19+
#else
20+
import Foundation
21+
#endif
22+
23+
public protocol DecodableRequest {
24+
var body: String? { get }
25+
var isBase64Encoded: Bool { get }
26+
27+
func decodeBody() throws -> Data?
28+
func decodeBody<T>(
29+
_ type: T.Type,
30+
using decoder: JSONDecoder
31+
) throws -> T where T: Decodable
32+
}
33+
34+
extension DecodableRequest {
35+
/// Decodes the body of the request into a `Data` object.
36+
///
37+
/// - Returns: The decoded body as `Data` or `nil` if the body is empty.
38+
public func decodeBody() throws -> Data? {
39+
guard let body else { return nil }
40+
41+
if isBase64Encoded,
42+
let base64Decoded = Data(base64Encoded: body)
43+
{
44+
return base64Decoded
45+
}
46+
47+
return body.data(using: .utf8)
48+
}
49+
50+
/// Decodes the body of the request into a decodable object. When the
51+
/// body is empty, an error is thrown.
52+
///
53+
/// - Parameters:
54+
/// - type: The type to decode the body into.
55+
/// - decoder: The decoder to use. Defaults to `JSONDecoder()`.
56+
///
57+
/// - Returns: The decoded body as `T`.
58+
/// - Throws: An error if the body cannot be decoded.
59+
public func decodeBody<T>(
60+
_ type: T.Type,
61+
using decoder: JSONDecoder = JSONDecoder()
62+
) throws -> T where T: Decodable {
63+
let bodyData = body?.data(using: .utf8) ?? Data()
64+
65+
var requestData = bodyData
66+
67+
if isBase64Encoded,
68+
let base64Decoded = Data(base64Encoded: requestData)
69+
{
70+
requestData = base64Decoded
71+
}
72+
73+
return try decoder.decode(T.self, from: requestData)
74+
}
75+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import HTTPTypes
16+
17+
#if canImport(FoundationEssentials)
18+
import FoundationEssentials
19+
#else
20+
import Foundation
21+
#endif
22+
23+
public protocol EncodableResponse {
24+
static func encoding<T>(
25+
_ encodable: T,
26+
status: HTTPResponse.Status,
27+
using encoder: JSONEncoder,
28+
headers: HTTPHeaders?,
29+
cookies: [String]?,
30+
onError: ((Error) -> Self)
31+
) -> Self where T: Encodable
32+
33+
init(
34+
statusCode: HTTPResponse.Status,
35+
headers: HTTPHeaders?,
36+
body: String?,
37+
isBase64Encoded: Bool?,
38+
cookies: [String]?
39+
)
40+
}
41+
42+
extension EncodableResponse {
43+
/// Encodes a given encodable object into a response object.
44+
///
45+
/// - Parameters:
46+
/// - encodable: The object to encode.
47+
/// - status: The status code to use. Defaults to `ok`.
48+
/// - encoder: The encoder to use. Defaults to a new `JSONEncoder`.
49+
/// - onError: A closure to handle errors, and transform them into a `APIGatewayV2Response`.
50+
/// Defaults to converting the error into a 500 (Internal Server Error) response with the error message as
51+
/// the body.
52+
///
53+
/// - Returns: a response object whose body is the encoded `encodable` type and with the
54+
/// other response parameters
55+
public static func encoding<T>(
56+
_ encodable: T,
57+
status: HTTPResponse.Status = .ok,
58+
using encoder: JSONEncoder = JSONEncoder(),
59+
headers: HTTPHeaders? = nil,
60+
cookies: [String]? = nil,
61+
onError: ((Error) -> Self) = Self.defaultErrorHandler
62+
) -> Self where T: Encodable {
63+
do {
64+
let encodedResponse = try encoder.encode(encodable)
65+
return Self(
66+
statusCode: status,
67+
headers: headers,
68+
body: String(data: encodedResponse, encoding: .utf8),
69+
isBase64Encoded: nil,
70+
cookies: cookies
71+
)
72+
} catch {
73+
return onError(error)
74+
}
75+
}
76+
77+
public static var defaultErrorHandler: ((Error) -> Self) {
78+
{ error in
79+
Self(
80+
statusCode: .internalServerError,
81+
headers: nil,
82+
body: "Internal Server Error: \(String(describing: error))",
83+
isBase64Encoded: nil,
84+
cookies: nil
85+
)
86+
}
87+
}
88+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2022 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#if canImport(FoundationEssentials)
16+
import FoundationEssentials
17+
#else
18+
import Foundation
19+
#endif
20+
21+
extension SQSEvent {
22+
/// Decodes the records included in the event into an array of decodable objects.
23+
///
24+
/// - Parameters:
25+
/// - type: The type to decode the body into.
26+
/// - decoder: The decoder to use. Defaults to a new `JSONDecoder`.
27+
///
28+
/// - Returns: The decoded records as `[T]`.
29+
/// - Throws: An error if any of the records cannot be decoded.
30+
public func decodeBody<T>(
31+
_ type: T.Type,
32+
using decoder: JSONDecoder = JSONDecoder()
33+
) throws -> [T] where T: Decodable {
34+
try records.map {
35+
try $0.decodeBody(type, using: decoder)
36+
}
37+
}
38+
}
39+
40+
extension SQSEvent.Message {
41+
/// Decodes the body of the message into a decodable object.
42+
///
43+
/// - Parameters:
44+
/// - type: The type to decode the body into.
45+
/// - decoder: The decoder to use. Defaults to a new `JSONDecoder`.
46+
///
47+
/// - Returns: The decoded body as `T`.
48+
/// - Throws: An error if the body cannot be decoded.
49+
public func decodeBody<T>(
50+
_ type: T.Type,
51+
using decoder: JSONDecoder = JSONDecoder()
52+
) throws -> T where T: Decodable {
53+
try decoder.decode(T.self, from: body.data(using: .utf8) ?? Data())
54+
}
55+
}

0 commit comments

Comments
 (0)