Skip to content

Commit 25c8dd4

Browse files
committed
Added ALB and APIGateway Events
1 parent dede451 commit 25c8dd4

File tree

5 files changed

+909
-0
lines changed

5 files changed

+909
-0
lines changed

Sources/AWSLambdaEvents/ALB.swift

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2020 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 class Foundation.JSONEncoder
16+
17+
// https://github.com/aws/aws-lambda-go/blob/master/events/alb.go
18+
public enum ALB {
19+
/// ALBTargetGroupRequest contains data originating from the ALB Lambda target group integration
20+
public struct TargetGroupRequest {
21+
/// ALBTargetGroupRequestContext contains the information to identify the load balancer invoking the lambda
22+
public struct Context: Codable {
23+
public let elb: ELBContext
24+
}
25+
26+
public let httpMethod: HTTPMethod
27+
public let path: String
28+
public let queryStringParameters: [String: [String]]
29+
public let headers: HTTPHeaders
30+
public let requestContext: Context
31+
public let isBase64Encoded: Bool
32+
public let body: String?
33+
}
34+
35+
/// ELBContext contains the information to identify the ARN invoking the lambda
36+
public struct ELBContext: Codable {
37+
public let targetGroupArn: String
38+
}
39+
40+
public struct TargetGroupResponse {
41+
public let statusCode: HTTPResponseStatus
42+
public let statusDescription: String?
43+
public let headers: HTTPHeaders?
44+
public let body: String
45+
public let isBase64Encoded: Bool
46+
47+
public init(
48+
statusCode: HTTPResponseStatus,
49+
statusDescription: String? = nil,
50+
headers: HTTPHeaders? = nil,
51+
body: String = "",
52+
isBase64Encoded: Bool = false
53+
) {
54+
self.statusCode = statusCode
55+
self.statusDescription = statusDescription
56+
self.headers = headers
57+
self.body = body
58+
self.isBase64Encoded = isBase64Encoded
59+
}
60+
}
61+
}
62+
63+
// MARK: - Request -
64+
65+
extension ALB.TargetGroupRequest: Decodable {
66+
enum CodingKeys: String, CodingKey {
67+
case httpMethod
68+
case path
69+
case queryStringParameters
70+
case multiValueQueryStringParameters
71+
case headers
72+
case multiValueHeaders
73+
case requestContext
74+
case isBase64Encoded
75+
case body
76+
}
77+
78+
public init(from decoder: Decoder) throws {
79+
let container = try decoder.container(keyedBy: CodingKeys.self)
80+
81+
let rawMethod = try container.decode(String.self, forKey: .httpMethod)
82+
guard let method = HTTPMethod(rawValue: rawMethod) else {
83+
throw DecodingError.dataCorruptedError(
84+
forKey: .httpMethod,
85+
in: container,
86+
debugDescription: #"Method "\#(rawMethod)" does not conform to allowed http method syntax defined in RFC 7230 Section 3.2.6"#
87+
)
88+
}
89+
self.httpMethod = method
90+
91+
self.path = try container.decode(String.self, forKey: .path)
92+
93+
// crazy multiple headers
94+
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
95+
96+
if let multiValueQueryStringParameters =
97+
try container.decodeIfPresent([String: [String]].self, forKey: .multiValueQueryStringParameters) {
98+
self.queryStringParameters = multiValueQueryStringParameters
99+
} else {
100+
let singleValueQueryStringParameters = try container.decode(
101+
[String: String].self,
102+
forKey: .queryStringParameters
103+
)
104+
self.queryStringParameters = singleValueQueryStringParameters.mapValues { [$0] }
105+
}
106+
107+
if let multiValueHeaders =
108+
try container.decodeIfPresent([String: [String]].self, forKey: .multiValueHeaders) {
109+
self.headers = HTTPHeaders(multiValueHeaders)
110+
} else {
111+
let singleValueHeaders = try container.decode(
112+
[String: String].self,
113+
forKey: .headers
114+
)
115+
let multiValueHeaders = singleValueHeaders.mapValues { [$0] }
116+
self.headers = HTTPHeaders(multiValueHeaders)
117+
}
118+
119+
self.requestContext = try container.decode(Context.self, forKey: .requestContext)
120+
self.isBase64Encoded = try container.decode(Bool.self, forKey: .isBase64Encoded)
121+
122+
let body = try container.decode(String.self, forKey: .body)
123+
self.body = body != "" ? body : nil
124+
}
125+
}
126+
127+
// MARK: - Response -
128+
129+
extension ALB.TargetGroupResponse: Encodable {
130+
static let MultiValueHeadersEnabledKey =
131+
CodingUserInfoKey(rawValue: "ALB.TargetGroupResponse.MultiValueHeadersEnabledKey")!
132+
133+
enum CodingKeys: String, CodingKey {
134+
case statusCode
135+
case statusDescription
136+
case headers
137+
case multiValueHeaders
138+
case body
139+
case isBase64Encoded
140+
}
141+
142+
public func encode(to encoder: Encoder) throws {
143+
var container = encoder.container(keyedBy: CodingKeys.self)
144+
try container.encode(statusCode.code, forKey: .statusCode)
145+
146+
let multiValueHeaderSupport =
147+
encoder.userInfo[ALB.TargetGroupResponse.MultiValueHeadersEnabledKey] as? Bool ?? false
148+
149+
switch (multiValueHeaderSupport, headers) {
150+
case (true, .none):
151+
try container.encode([String: String](), forKey: .multiValueHeaders)
152+
case (false, .none):
153+
try container.encode([String: [String]](), forKey: .headers)
154+
case (true, .some(let headers)):
155+
try container.encode(headers.headers, forKey: .multiValueHeaders)
156+
case (false, .some(let headers)):
157+
let singleValueHeaders = headers.headers.mapValues { (values) -> String in
158+
#warning("Is this correct?")
159+
return values.joined(separator: ", ")
160+
}
161+
try container.encode(singleValueHeaders, forKey: .headers)
162+
}
163+
164+
try container.encodeIfPresent(statusDescription, forKey: .statusDescription)
165+
try container.encodeIfPresent(body, forKey: .body)
166+
try container.encodeIfPresent(isBase64Encoded, forKey: .isBase64Encoded)
167+
}
168+
}
169+
170+
extension ALB.TargetGroupResponse {
171+
public init<Payload: Encodable>(
172+
statusCode: HTTPResponseStatus,
173+
statusDescription: String? = nil,
174+
headers: HTTPHeaders? = nil,
175+
payload: Payload,
176+
encoder: JSONEncoder = JSONEncoder()
177+
) throws {
178+
var headers = headers ?? HTTPHeaders()
179+
if !headers.contains(name: "Content-Type") {
180+
headers.add(name: "Content-Type", value: "application/json")
181+
}
182+
183+
self.statusCode = statusCode
184+
self.statusDescription = statusDescription
185+
self.headers = headers
186+
187+
let data = try encoder.encode(payload)
188+
self.body = String(decoding: data, as: Unicode.UTF8.self)
189+
self.isBase64Encoded = false
190+
}
191+
192+
public init(
193+
statusCode: HTTPResponseStatus,
194+
statusDescription: String? = nil,
195+
headers: HTTPHeaders? = nil,
196+
bytes: [UInt8]?
197+
) {
198+
let headers = headers ?? HTTPHeaders()
199+
200+
self.statusCode = statusCode
201+
self.statusDescription = statusDescription
202+
self.headers = headers
203+
if let bytes = bytes {
204+
self.body = String(base64Encoding: bytes)
205+
} else {
206+
self.body = ""
207+
}
208+
self.isBase64Encoded = true
209+
}
210+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2017-2020 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 class Foundation.JSONEncoder
16+
17+
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
18+
19+
public enum APIGateway {
20+
/// APIGatewayRequest contains data coming from the API Gateway
21+
public struct Request {
22+
public struct Context: Codable {
23+
public struct Identity: Codable {
24+
public let cognitoIdentityPoolId: String?
25+
26+
public let apiKey: String?
27+
public let userArn: String?
28+
public let cognitoAuthenticationType: String?
29+
public let caller: String?
30+
public let userAgent: String?
31+
public let user: String?
32+
33+
public let cognitoAuthenticationProvider: String?
34+
public let sourceIp: String?
35+
public let accountId: String?
36+
}
37+
38+
public let resourceId: String
39+
public let apiId: String
40+
public let resourcePath: String
41+
public let httpMethod: String
42+
public let requestId: String
43+
public let accountId: String
44+
public let stage: String
45+
46+
public let identity: Identity
47+
public let extendedRequestId: String?
48+
public let path: String
49+
}
50+
51+
public let resource: String
52+
public let path: String
53+
public let httpMethod: HTTPMethod
54+
55+
public let queryStringParameters: [String: String]?
56+
public let multiValueQueryStringParameters: [String: [String]]?
57+
public let headers: HTTPHeaders
58+
public let pathParameters: [String: String]?
59+
public let stageVariables: [String: String]?
60+
61+
public let requestContext: Context
62+
public let body: String?
63+
public let isBase64Encoded: Bool
64+
}
65+
}
66+
67+
// MARK: - Request -
68+
69+
extension APIGateway.Request: Decodable {
70+
enum CodingKeys: String, CodingKey {
71+
case resource
72+
case path
73+
case httpMethod
74+
75+
case queryStringParameters
76+
case multiValueQueryStringParameters
77+
case headers = "multiValueHeaders"
78+
case pathParameters
79+
case stageVariables
80+
81+
case requestContext
82+
case body
83+
case isBase64Encoded
84+
}
85+
}
86+
87+
// MARK: - Response -
88+
89+
extension APIGateway {
90+
public struct Response {
91+
public let statusCode: HTTPResponseStatus
92+
public let headers: HTTPHeaders?
93+
public let body: String?
94+
public let isBase64Encoded: Bool?
95+
96+
public init(
97+
statusCode: HTTPResponseStatus,
98+
headers: HTTPHeaders? = nil,
99+
body: String? = nil,
100+
isBase64Encoded: Bool? = nil
101+
) {
102+
self.statusCode = statusCode
103+
self.headers = headers
104+
self.body = body
105+
self.isBase64Encoded = isBase64Encoded
106+
}
107+
}
108+
}
109+
110+
extension APIGateway.Response: Encodable {
111+
enum CodingKeys: String, CodingKey {
112+
case statusCode
113+
case headers = "multiValueHeaders"
114+
case body
115+
case isBase64Encoded
116+
}
117+
}

0 commit comments

Comments
 (0)