split events into spearate package (#216)

motivation:
the runtime library has a stable API while the events are still moving target. In order to provide a 1.0 stable version we separate them out

changes:
* remove Events module
* update readme
* update Samples
* remove gateway example

Co-authored-by: Fabian Fett <fabianfett@apple.com>
This commit is contained in:
tomer doron
2021-08-19 00:36:01 -07:00
committed by GitHub
parent 245674cd6b
commit 8525b6334e
30 changed files with 38 additions and 4115 deletions
-6
View File
@@ -14,8 +14,6 @@ let package = Package(
.executable(name: "Benchmark", targets: ["Benchmark"]),
// demonstrate different types of error handling
.executable(name: "ErrorHandling", targets: ["ErrorHandling"]),
// demostrate how to integrate with AWS API Gateway
.executable(name: "APIGateway", targets: ["APIGateway"]),
// fully featured example with domain specific business logic
.executable(name: "CurrencyExchange", targets: ["CurrencyExchange"]),
],
@@ -35,10 +33,6 @@ let package = Package(
.target(name: "ErrorHandling", dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
]),
.target(name: "APIGateway", dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-runtime"),
]),
.target(name: "CurrencyExchange", dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
]),
@@ -1,34 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import AWSLambdaEvents
import AWSLambdaRuntime
import NIO
// MARK: - Run Lambda
Lambda.run(APIGatewayProxyLambda())
// MARK: - Handler, Request and Response
// FIXME: Use proper Event abstractions once added to AWSLambdaRuntime
struct APIGatewayProxyLambda: EventLoopLambdaHandler {
public typealias In = APIGateway.V2.Request
public typealias Out = APIGateway.V2.Response
public func handle(context: Lambda.Context, event: APIGateway.V2.Request) -> EventLoopFuture<APIGateway.V2.Response> {
context.logger.debug("hello, api gateway!")
return context.eventLoop.makeSucceededFuture(APIGateway.V2.Response(statusCode: .ok, body: "hello, world!"))
}
}
-4
View File
@@ -9,8 +9,6 @@ let package = Package(
.library(name: "AWSLambdaRuntime", targets: ["AWSLambdaRuntime"]),
// this has all the main functionality for lambda and it does not link Foundation
.library(name: "AWSLambdaRuntimeCore", targets: ["AWSLambdaRuntimeCore"]),
// common AWS events
.library(name: "AWSLambdaEvents", targets: ["AWSLambdaEvents"]),
// for testing only
.library(name: "AWSLambdaTesting", targets: ["AWSLambdaTesting"]),
],
@@ -41,8 +39,6 @@ let package = Package(
.byName(name: "AWSLambdaRuntimeCore"),
.byName(name: "AWSLambdaRuntime"),
]),
.target(name: "AWSLambdaEvents", dependencies: []),
.testTarget(name: "AWSLambdaEventsTests", dependencies: ["AWSLambdaEvents"]),
// testing helper
.target(name: "AWSLambdaTesting", dependencies: [
.byName(name: "AWSLambdaRuntime"),
-77
View File
@@ -1,77 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import class Foundation.JSONEncoder
// https://github.com/aws/aws-lambda-go/blob/master/events/alb.go
public enum ALB {
/// ALBTargetGroupRequest contains data originating from the ALB Lambda target group integration
public struct TargetGroupRequest: Codable {
/// ALBTargetGroupRequestContext contains the information to identify the load balancer invoking the lambda
public struct Context: Codable {
public let elb: ELBContext
}
public let httpMethod: HTTPMethod
public let path: String
public let queryStringParameters: [String: String]
/// Depending on your configuration of your target group either `headers` or `multiValueHeaders`
/// are set.
///
/// For more information visit:
/// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
public let headers: HTTPHeaders?
/// Depending on your configuration of your target group either `headers` or `multiValueHeaders`
/// are set.
///
/// For more information visit:
/// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
public let multiValueHeaders: HTTPMultiValueHeaders?
public let requestContext: Context
public let isBase64Encoded: Bool
public let body: String?
}
/// ELBContext contains the information to identify the ARN invoking the lambda
public struct ELBContext: Codable {
public let targetGroupArn: String
}
public struct TargetGroupResponse: Codable {
public var statusCode: HTTPResponseStatus
public var statusDescription: String?
public var headers: HTTPHeaders?
public var multiValueHeaders: HTTPMultiValueHeaders?
public var body: String
public var isBase64Encoded: Bool
public init(
statusCode: HTTPResponseStatus,
statusDescription: String? = nil,
headers: HTTPHeaders? = nil,
multiValueHeaders: HTTPMultiValueHeaders? = nil,
body: String = "",
isBase64Encoded: Bool = false
) {
self.statusCode = statusCode
self.statusDescription = statusDescription
self.headers = headers
self.multiValueHeaders = multiValueHeaders
self.body = body
self.isBase64Encoded = isBase64Encoded
}
}
}
-116
View File
@@ -1,116 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
extension APIGateway {
public struct V2 {}
}
extension APIGateway.V2 {
/// APIGateway.V2.Request contains data coming from the new HTTP API Gateway
public struct Request: Codable {
/// Context contains the information to identify the AWS account and resources invoking the Lambda function.
public struct Context: Codable {
public struct HTTP: Codable {
public let method: HTTPMethod
public let path: String
public let `protocol`: String
public let sourceIp: String
public let userAgent: String
}
/// Authorizer contains authorizer information for the request context.
public struct Authorizer: Codable {
/// JWT contains JWT authorizer information for the request context.
public struct JWT: Codable {
public let claims: [String: String]
public let scopes: [String]?
}
public let jwt: JWT
}
public let accountId: String
public let apiId: String
public let domainName: String
public let domainPrefix: String
public let stage: String
public let requestId: String
public let http: HTTP
public let authorizer: Authorizer?
/// The request time in format: 23/Apr/2020:11:08:18 +0000
public let time: String
public let timeEpoch: UInt64
}
public let version: String
public let routeKey: String
public let rawPath: String
public let rawQueryString: String
public let cookies: [String]?
public let headers: HTTPHeaders
public let queryStringParameters: [String: String]?
public let pathParameters: [String: String]?
public let context: Context
public let stageVariables: [String: String]?
public let body: String?
public let isBase64Encoded: Bool
enum CodingKeys: String, CodingKey {
case version
case routeKey
case rawPath
case rawQueryString
case cookies
case headers
case queryStringParameters
case pathParameters
case context = "requestContext"
case stageVariables
case body
case isBase64Encoded
}
}
}
extension APIGateway.V2 {
public struct Response: Codable {
public var statusCode: HTTPResponseStatus
public var headers: HTTPHeaders?
public var body: String?
public var isBase64Encoded: Bool?
public var cookies: [String]?
public init(
statusCode: HTTPResponseStatus,
headers: HTTPHeaders? = nil,
body: String? = nil,
isBase64Encoded: Bool? = nil,
cookies: [String]? = nil
) {
self.statusCode = statusCode
self.headers = headers
self.body = body
self.isBase64Encoded = isBase64Encoded
self.cookies = cookies
}
}
}
-93
View File
@@ -1,93 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import class Foundation.JSONEncoder
// https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html
public enum APIGateway {
/// APIGatewayRequest contains data coming from the API Gateway
public struct Request: Codable {
public struct Context: Codable {
public struct Identity: Codable {
public let cognitoIdentityPoolId: String?
public let apiKey: String?
public let userArn: String?
public let cognitoAuthenticationType: String?
public let caller: String?
public let userAgent: String?
public let user: String?
public let cognitoAuthenticationProvider: String?
public let sourceIp: String?
public let accountId: String?
}
public let resourceId: String
public let apiId: String
public let resourcePath: String
public let httpMethod: String
public let requestId: String
public let accountId: String
public let stage: String
public let identity: Identity
public let extendedRequestId: String?
public let path: String
}
public let resource: String
public let path: String
public let httpMethod: HTTPMethod
public let queryStringParameters: [String: String]?
public let multiValueQueryStringParameters: [String: [String]]?
public let headers: HTTPHeaders
public let multiValueHeaders: HTTPMultiValueHeaders
public let pathParameters: [String: String]?
public let stageVariables: [String: String]?
public let requestContext: Context
public let body: String?
public let isBase64Encoded: Bool
}
}
// MARK: - Response -
extension APIGateway {
public struct Response: Codable {
public var statusCode: HTTPResponseStatus
public var headers: HTTPHeaders?
public var multiValueHeaders: HTTPMultiValueHeaders?
public var body: String?
public var isBase64Encoded: Bool?
public init(
statusCode: HTTPResponseStatus,
headers: HTTPHeaders? = nil,
multiValueHeaders: HTTPMultiValueHeaders? = nil,
body: String? = nil,
isBase64Encoded: Bool? = nil
) {
self.statusCode = statusCode
self.headers = headers
self.multiValueHeaders = multiValueHeaders
self.body = body
self.isBase64Encoded = isBase64Encoded
}
}
}
-92
View File
@@ -1,92 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// list all available regions using aws cli:
// $ aws ssm get-parameters-by-path --path /aws/service/global-infrastructure/services/lambda/regions --output json
/// Enumeration of the AWS Regions.
public struct AWSRegion: RawRepresentable, Equatable {
public typealias RawValue = String
public let rawValue: String
public init?(rawValue: String) {
self.rawValue = rawValue
}
static var all: [AWSRegion] = [
Self.ap_northeast_1,
Self.ap_northeast_2,
Self.ap_east_1,
Self.ap_southeast_1,
Self.ap_southeast_2,
Self.ap_south_1,
Self.cn_north_1,
Self.cn_northwest_1,
Self.eu_north_1,
Self.eu_west_1,
Self.eu_west_2,
Self.eu_west_3,
Self.eu_central_1,
Self.us_east_1,
Self.us_east_2,
Self.us_west_1,
Self.us_west_2,
Self.us_gov_east_1,
Self.us_gov_west_1,
Self.ca_central_1,
Self.sa_east_1,
Self.me_south_1,
]
public static var ap_northeast_1: Self { AWSRegion(rawValue: "ap-northeast-1")! }
public static var ap_northeast_2: Self { AWSRegion(rawValue: "ap-northeast-2")! }
public static var ap_east_1: Self { AWSRegion(rawValue: "ap-east-1")! }
public static var ap_southeast_1: Self { AWSRegion(rawValue: "ap-southeast-1")! }
public static var ap_southeast_2: Self { AWSRegion(rawValue: "ap-southeast-2")! }
public static var ap_south_1: Self { AWSRegion(rawValue: "ap-south-1")! }
public static var cn_north_1: Self { AWSRegion(rawValue: "cn-north-1")! }
public static var cn_northwest_1: Self { AWSRegion(rawValue: "cn-northwest-1")! }
public static var eu_north_1: Self { AWSRegion(rawValue: "eu-north-1")! }
public static var eu_west_1: Self { AWSRegion(rawValue: "eu-west-1")! }
public static var eu_west_2: Self { AWSRegion(rawValue: "eu-west-2")! }
public static var eu_west_3: Self { AWSRegion(rawValue: "eu-west-3")! }
public static var eu_central_1: Self { AWSRegion(rawValue: "eu-central-1")! }
public static var us_east_1: Self { AWSRegion(rawValue: "us-east-1")! }
public static var us_east_2: Self { AWSRegion(rawValue: "us-east-2")! }
public static var us_west_1: Self { AWSRegion(rawValue: "us-west-1")! }
public static var us_west_2: Self { AWSRegion(rawValue: "us-west-2")! }
public static var us_gov_east_1: Self { AWSRegion(rawValue: "us-gov-east-1")! }
public static var us_gov_west_1: Self { AWSRegion(rawValue: "us-gov-west-1")! }
public static var ca_central_1: Self { AWSRegion(rawValue: "ca-central-1")! }
public static var sa_east_1: Self { AWSRegion(rawValue: "sa-east-1")! }
public static var me_south_1: Self { AWSRegion(rawValue: "me-south-1")! }
}
extension AWSRegion: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let region = try container.decode(String.self)
self.init(rawValue: region)!
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
}
}
-169
View File
@@ -1,169 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// https://docs.aws.amazon.com/appsync/latest/devguide/resolver-context-reference.html
public enum AppSync {
public struct Event: Decodable {
public let arguments: [String: ArgumentValue]
public enum ArgumentValue: Codable {
case string(String)
case dictionary([String: String])
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let strValue = try? container.decode(String.self) {
self = .string(strValue)
} else if let dictionaryValue = try? container.decode([String: String].self) {
self = .dictionary(dictionaryValue)
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: """
Unexpected AppSync argument.
Expected a String or a Dictionary.
""")
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .dictionary(let array):
try container.encode(array)
case .string(let str):
try container.encode(str)
}
}
}
public let request: Request
public struct Request: Decodable {
let headers: HTTPHeaders
}
public let source: [String: String]?
public let stash: [String: String]?
public let info: Info
public struct Info: Codable {
public var selectionSetList: [String]
public var selectionSetGraphQL: String
public var parentTypeName: String
public var fieldName: String
public var variables: [String: String]
}
public let identity: Identity?
public enum Identity: Codable {
case iam(IAMIdentity)
case cognitoUserPools(CognitoUserPoolIdentity)
public struct IAMIdentity: Codable {
public let accountId: String
public let cognitoIdentityPoolId: String
public let cognitoIdentityId: String
public let sourceIp: [String]
public let username: String?
public let userArn: String
public let cognitoIdentityAuthType: String
public let cognitoIdentityAuthProvider: String
}
public struct CognitoUserPoolIdentity: Codable {
public let defaultAuthStrategy: String
public let issuer: String
public let sourceIp: [String]
public let sub: String
public let username: String?
public struct Claims {
let sub: String
let emailVerified: Bool
let iss: String
let phoneNumberVerified: Bool
let cognitoUsername: String
let aud: String
let eventId: String
let tokenUse: String
let authTime: Int
let phoneNumber: String?
let exp: Int
let iat: Int
let email: String?
enum CodingKeys: String, CodingKey {
case sub
case emailVerified = "email_verified"
case iss
case phoneNumberVerified = "phone_number_verified"
case cognitoUsername = "cognito:username"
case aud
case eventId = "event_id"
case tokenUse = "token_use"
case authTime = "auth_time"
case phoneNumber = "phone_number"
case exp
case iat
case email
}
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let iamIdentity = try? container.decode(IAMIdentity.self) {
self = .iam(iamIdentity)
} else if let cognitoIdentity = try? container.decode(CognitoUserPoolIdentity.self) {
self = .cognitoUserPools(cognitoIdentity)
} else {
throw DecodingError.dataCorruptedError(in: container, debugDescription: """
Unexpected Identity argument.
Expected a IAM Identity or a Cognito User Pool Identity.
""")
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .iam(let iamIdentity):
try container.encode(iamIdentity)
case .cognitoUserPools(let cognitoUserPool):
try container.encode(cognitoUserPool)
}
}
}
}
}
extension AppSync {
public enum Response<ResultType: Encodable>: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .array(let array):
try container.encode(array)
case .object(let object):
try container.encode(object)
case .dictionary(let dictionary):
try container.encode(dictionary)
}
}
case object(ResultType)
case array([ResultType])
case dictionary([String: ResultType])
}
public typealias JSONStringResponse = Response<String>
}
-129
View File
@@ -1,129 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
/// EventBridge has the same events/notification types as CloudWatch
typealias EventBridge = Cloudwatch
public protocol CloudwatchDetail: Decodable {
static var name: String { get }
}
extension CloudwatchDetail {
public var detailType: String {
Self.name
}
}
public enum Cloudwatch {
/// CloudWatch.Event is the outer structure of an event sent via CloudWatch Events.
///
/// **NOTE**: For examples of events that come via CloudWatch Events, see
/// https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents.html
/// https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html
/// https://docs.aws.amazon.com/eventbridge/latest/userguide/event-types.html
public struct Event<Detail: CloudwatchDetail>: Decodable {
public let id: String
public let source: String
public let accountId: String
public let time: Date
public let region: AWSRegion
public let resources: [String]
public let detail: Detail
enum CodingKeys: String, CodingKey {
case id
case source
case accountId = "account"
case time
case region
case resources
case detailType = "detail-type"
case detail
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(String.self, forKey: .id)
self.source = try container.decode(String.self, forKey: .source)
self.accountId = try container.decode(String.self, forKey: .accountId)
self.time = (try container.decode(ISO8601Coding.self, forKey: .time)).wrappedValue
self.region = try container.decode(AWSRegion.self, forKey: .region)
self.resources = try container.decode([String].self, forKey: .resources)
let detailType = try container.decode(String.self, forKey: .detailType)
guard detailType.lowercased() == Detail.name.lowercased() else {
throw DetailTypeMismatch(name: detailType, type: Detail.self)
}
self.detail = try container.decode(Detail.self, forKey: .detail)
}
}
// MARK: - Common Event Types
public typealias ScheduledEvent = Event<Scheduled>
public struct Scheduled: CloudwatchDetail {
public static let name = "Scheduled Event"
}
public enum EC2 {
public typealias InstanceStateChangeNotificationEvent = Event<InstanceStateChangeNotification>
public struct InstanceStateChangeNotification: CloudwatchDetail {
public static let name = "EC2 Instance State-change Notification"
public enum State: String, Codable {
case running
case shuttingDown = "shutting-down"
case stopped
case stopping
case terminated
}
public let instanceId: String
public let state: State
enum CodingKeys: String, CodingKey {
case instanceId = "instance-id"
case state
}
}
public typealias SpotInstanceInterruptionNoticeEvent = Event<SpotInstanceInterruptionNotice>
public struct SpotInstanceInterruptionNotice: CloudwatchDetail {
public static let name = "EC2 Spot Instance Interruption Warning"
public enum Action: String, Codable {
case hibernate
case stop
case terminate
}
public let instanceId: String
public let action: Action
enum CodingKeys: String, CodingKey {
case instanceId = "instance-id"
case action = "instance-action"
}
}
}
struct DetailTypeMismatch: Error {
let name: String
let type: Any
}
}
-934
View File
@@ -1,934 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
// https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html
public enum DynamoDB {
public struct Event: Decodable {
public let records: [EventRecord]
public enum CodingKeys: String, CodingKey {
case records = "Records"
}
}
public enum KeyType: String, Codable {
case hash = "HASH"
case range = "RANGE"
}
public enum OperationType: String, Codable {
case insert = "INSERT"
case modify = "MODIFY"
case remove = "REMOVE"
}
public enum SharedIteratorType: String, Codable {
case trimHorizon = "TRIM_HORIZON"
case latest = "LATEST"
case atSequenceNumber = "AT_SEQUENCE_NUMBER"
case afterSequenceNumber = "AFTER_SEQUENCE_NUMBER"
}
public enum StreamStatus: String, Codable {
case enabling = "ENABLING"
case enabled = "ENABLED"
case disabling = "DISABLING"
case disabled = "DISABLED"
}
public enum StreamViewType: String, Codable {
/// the entire item, as it appeared after it was modified.
case newImage = "NEW_IMAGE"
/// the entire item, as it appeared before it was modified.
case oldImage = "OLD_IMAGE"
/// both the new and the old item images of the item.
case newAndOldImages = "NEW_AND_OLD_IMAGES"
/// only the key attributes of the modified item.
case keysOnly = "KEYS_ONLY"
}
public struct EventRecord: Decodable {
/// The region in which the GetRecords request was received.
public let awsRegion: AWSRegion
/// The main body of the stream record, containing all of the DynamoDB-specific
/// fields.
public let change: StreamRecord
/// A globally unique identifier for the event that was recorded in this stream
/// record.
public let eventId: String
/// The type of data modification that was performed on the DynamoDB table:
/// * INSERT - a new item was added to the table.
/// * MODIFY - one or more of an existing item's attributes were modified.
/// * REMOVE - the item was deleted from the table
public let eventName: OperationType
/// The AWS service from which the stream record originated. For DynamoDB Streams,
/// this is aws:dynamodb.
public let eventSource: String
/// The version number of the stream record format. This number is updated whenever
/// the structure of Record is modified.
///
/// Client applications must not assume that eventVersion will remain at a particular
/// value, as this number is subject to change at any time. In general, eventVersion
/// will only increase as the low-level DynamoDB Streams API evolves.
public let eventVersion: String
/// The event source ARN of DynamoDB
public let eventSourceArn: String
/// Items that are deleted by the Time to Live process after expiration have
/// the following fields:
/// * Records[].userIdentity.type
///
/// "Service"
/// * Records[].userIdentity.principalId
///
/// "dynamodb.amazonaws.com"
public let userIdentity: UserIdentity?
public enum CodingKeys: String, CodingKey {
case awsRegion
case change = "dynamodb"
case eventId = "eventID"
case eventName
case eventSource
case eventVersion
case eventSourceArn = "eventSourceARN"
case userIdentity
}
}
public struct StreamRecord {
/// The approximate date and time when the stream record was created, in UNIX
/// epoch time (http://www.epochconverter.com/) format.
public let approximateCreationDateTime: Date?
/// The primary key attribute(s) for the DynamoDB item that was modified.
public let keys: [String: AttributeValue]
/// The item in the DynamoDB table as it appeared after it was modified.
public let newImage: [String: AttributeValue]?
/// The item in the DynamoDB table as it appeared before it was modified.
public let oldImage: [String: AttributeValue]?
/// The sequence number of the stream record.
public let sequenceNumber: String
/// The size of the stream record, in bytes.
public let sizeBytes: Int64
/// The type of data from the modified DynamoDB item that was captured in this
/// stream record.
public let streamViewType: StreamViewType
}
public struct UserIdentity: Codable {
public let type: String
public let principalId: String
}
}
extension DynamoDB.StreamRecord: Decodable {
enum CodingKeys: String, CodingKey {
case approximateCreationDateTime = "ApproximateCreationDateTime"
case keys = "Keys"
case newImage = "NewImage"
case oldImage = "OldImage"
case sequenceNumber = "SequenceNumber"
case sizeBytes = "SizeBytes"
case streamViewType = "StreamViewType"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.keys = try container.decode(
[String: DynamoDB.AttributeValue].self,
forKey: .keys
)
self.newImage = try container.decodeIfPresent(
[String: DynamoDB.AttributeValue].self,
forKey: .newImage
)
self.oldImage = try container.decodeIfPresent(
[String: DynamoDB.AttributeValue].self,
forKey: .oldImage
)
self.sequenceNumber = try container.decode(String.self, forKey: .sequenceNumber)
self.sizeBytes = try container.decode(Int64.self, forKey: .sizeBytes)
self.streamViewType = try container.decode(DynamoDB.StreamViewType.self, forKey: .streamViewType)
if let timestamp = try container.decodeIfPresent(Double.self, forKey: .approximateCreationDateTime) {
self.approximateCreationDateTime = Date(timeIntervalSince1970: timestamp)
} else {
self.approximateCreationDateTime = nil
}
}
}
// MARK: - AttributeValue -
extension DynamoDB {
public enum AttributeValue {
case boolean(Bool)
case binary([UInt8])
case binarySet([[UInt8]])
case string(String)
case stringSet([String])
case null
case number(String)
case numberSet([String])
case list([AttributeValue])
case map([String: AttributeValue])
}
}
extension DynamoDB.AttributeValue: Decodable {
enum CodingKeys: String, CodingKey {
case binary = "B"
case bool = "BOOL"
case binarySet = "BS"
case list = "L"
case map = "M"
case number = "N"
case numberSet = "NS"
case null = "NULL"
case string = "S"
case stringSet = "SS"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard container.allKeys.count == 1, let key = container.allKeys.first else {
let context = DecodingError.Context(
codingPath: container.codingPath,
debugDescription: "Expected exactly one key, but got \(container.allKeys.count)"
)
throw DecodingError.dataCorrupted(context)
}
switch key {
case .binary:
let encoded = try container.decode(String.self, forKey: .binary)
self = .binary(try encoded.base64decoded())
case .bool:
let value = try container.decode(Bool.self, forKey: .bool)
self = .boolean(value)
case .binarySet:
let values = try container.decode([String].self, forKey: .binarySet)
let buffers = try values.map { try $0.base64decoded() }
self = .binarySet(buffers)
case .list:
let values = try container.decode([DynamoDB.AttributeValue].self, forKey: .list)
self = .list(values)
case .map:
let value = try container.decode([String: DynamoDB.AttributeValue].self, forKey: .map)
self = .map(value)
case .number:
let value = try container.decode(String.self, forKey: .number)
self = .number(value)
case .numberSet:
let values = try container.decode([String].self, forKey: .numberSet)
self = .numberSet(values)
case .null:
self = .null
case .string:
let value = try container.decode(String.self, forKey: .string)
self = .string(value)
case .stringSet:
let values = try container.decode([String].self, forKey: .stringSet)
self = .stringSet(values)
}
}
}
extension DynamoDB.AttributeValue: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.boolean(let lhs), .boolean(let rhs)):
return lhs == rhs
case (.binary(let lhs), .binary(let rhs)):
return lhs == rhs
case (.binarySet(let lhs), .binarySet(let rhs)):
return lhs == rhs
case (.string(let lhs), .string(let rhs)):
return lhs == rhs
case (.stringSet(let lhs), .stringSet(let rhs)):
return lhs == rhs
case (.null, .null):
return true
case (.number(let lhs), .number(let rhs)):
return lhs == rhs
case (.numberSet(let lhs), .numberSet(let rhs)):
return lhs == rhs
case (.list(let lhs), .list(let rhs)):
return lhs == rhs
case (.map(let lhs), .map(let rhs)):
return lhs == rhs
default:
return false
}
}
}
// MARK: DynamoDB AttributeValue Decoding
extension DynamoDB {
public struct Decoder {
@usableFromInline var userInfo: [CodingUserInfoKey: Any] = [:]
public init() {}
@inlinable public func decode<T: Decodable>(_ type: T.Type, from image: [String: AttributeValue])
throws -> T {
try self.decode(type, from: .map(image))
}
@inlinable public func decode<T: Decodable>(_ type: T.Type, from value: AttributeValue)
throws -> T {
let decoder = _DecoderImpl(userInfo: userInfo, from: value, codingPath: [])
return try decoder.decode(T.self)
}
}
@usableFromInline internal struct _DecoderImpl: Swift.Decoder {
@usableFromInline let codingPath: [CodingKey]
@usableFromInline let userInfo: [CodingUserInfoKey: Any]
@usableFromInline let value: AttributeValue
@inlinable init(userInfo: [CodingUserInfoKey: Any], from value: AttributeValue, codingPath: [CodingKey]) {
self.userInfo = userInfo
self.codingPath = codingPath
self.value = value
}
@inlinable public func decode<T: Decodable>(_: T.Type) throws -> T {
try T(from: self)
}
@usableFromInline func container<Key>(keyedBy type: Key.Type) throws ->
KeyedDecodingContainer<Key> where Key: CodingKey {
guard case .map(let dictionary) = self.value else {
throw DecodingError.typeMismatch([String: AttributeValue].self, DecodingError.Context(
codingPath: self.codingPath,
debugDescription: "Expected to decode \([String: AttributeValue].self) but found \(self.value.debugDataTypeDescription) instead."
))
}
let container = _KeyedDecodingContainer<Key>(
impl: self,
codingPath: self.codingPath,
dictionary: dictionary
)
return KeyedDecodingContainer(container)
}
@usableFromInline func unkeyedContainer() throws -> UnkeyedDecodingContainer {
guard case .list(let array) = self.value else {
throw DecodingError.typeMismatch([AttributeValue].self, DecodingError.Context(
codingPath: self.codingPath,
debugDescription: "Expected to decode \([AttributeValue].self) but found \(self.value.debugDataTypeDescription) instead."
))
}
return _UnkeyedDecodingContainer(
impl: self,
codingPath: self.codingPath,
array: array
)
}
@usableFromInline func singleValueContainer() throws -> SingleValueDecodingContainer {
_SingleValueDecodingContainter(
impl: self,
codingPath: self.codingPath,
value: self.value
)
}
}
struct ArrayKey: CodingKey, Equatable {
init(index: Int) {
self.intValue = index
}
init?(stringValue _: String) {
preconditionFailure("Did not expect to be initialized with a string")
}
init?(intValue: Int) {
self.intValue = intValue
}
var intValue: Int?
var stringValue: String {
"Index \(self.intValue!)"
}
static func == (lhs: ArrayKey, rhs: ArrayKey) -> Bool {
precondition(lhs.intValue != nil)
precondition(rhs.intValue != nil)
return lhs.intValue == rhs.intValue
}
}
struct _KeyedDecodingContainer<K: CodingKey>: KeyedDecodingContainerProtocol {
typealias Key = K
let impl: _DecoderImpl
let codingPath: [CodingKey]
let dictionary: [String: AttributeValue]
init(impl: _DecoderImpl, codingPath: [CodingKey], dictionary: [String: AttributeValue]) {
self.impl = impl
self.codingPath = codingPath
self.dictionary = dictionary
}
var allKeys: [K] {
self.dictionary.keys.compactMap { K(stringValue: $0) }
}
func contains(_ key: K) -> Bool {
if let _ = self.dictionary[key.stringValue] {
return true
}
return false
}
func decodeNil(forKey key: K) throws -> Bool {
let value = try getValue(forKey: key)
return value == .null
}
func decode(_ type: Bool.Type, forKey key: K) throws -> Bool {
let value = try getValue(forKey: key)
guard case .boolean(let bool) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
}
return bool
}
func decode(_ type: String.Type, forKey key: K) throws -> String {
let value = try getValue(forKey: key)
guard case .string(let string) = value else {
throw self.createTypeMismatchError(type: type, forKey: key, value: value)
}
return string
}
func decode(_ type: Double.Type, forKey key: K) throws -> Double {
try self.decodeLosslessStringConvertible(key: key)
}
func decode(_ type: Float.Type, forKey key: K) throws -> Float {
try self.decodeLosslessStringConvertible(key: key)
}
func decode(_ type: Int.Type, forKey key: K) throws -> Int {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: Int8.Type, forKey key: K) throws -> Int8 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: Int16.Type, forKey key: K) throws -> Int16 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: Int32.Type, forKey key: K) throws -> Int32 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: Int64.Type, forKey key: K) throws -> Int64 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: UInt.Type, forKey key: K) throws -> UInt {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: UInt8.Type, forKey key: K) throws -> UInt8 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: UInt16.Type, forKey key: K) throws -> UInt16 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: UInt32.Type, forKey key: K) throws -> UInt32 {
try self.decodeFixedWidthInteger(key: key)
}
func decode(_ type: UInt64.Type, forKey key: K) throws -> UInt64 {
try self.decodeFixedWidthInteger(key: key)
}
func decode<T>(_ type: T.Type, forKey key: K) throws -> T where T: Decodable {
let decoder = try self.decoderForKey(key)
return try T(from: decoder)
}
func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: K) throws
-> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
try self.decoderForKey(key).container(keyedBy: type)
}
func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer {
try self.decoderForKey(key).unkeyedContainer()
}
func superDecoder() throws -> Swift.Decoder {
self.impl
}
func superDecoder(forKey key: K) throws -> Swift.Decoder {
self.impl
}
private func decoderForKey(_ key: K) throws -> _DecoderImpl {
let value = try getValue(forKey: key)
var newPath = self.codingPath
newPath.append(key)
return _DecoderImpl(
userInfo: self.impl.userInfo,
from: value,
codingPath: newPath
)
}
@inline(__always) private func getValue(forKey key: K) throws -> AttributeValue {
guard let value = self.dictionary[key.stringValue] else {
throw DecodingError.keyNotFound(key, .init(
codingPath: self.codingPath,
debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")."
))
}
return value
}
@inline(__always) private func createTypeMismatchError(type: Any.Type, forKey key: K, value: AttributeValue) -> DecodingError {
let codingPath = self.codingPath + [key]
return DecodingError.typeMismatch(type, .init(
codingPath: codingPath, debugDescription: "Expected to decode \(type) but found \(value.debugDataTypeDescription) instead."
))
}
@inline(__always) private func decodeFixedWidthInteger<T: FixedWidthInteger>(key: Self.Key)
throws -> T {
let value = try getValue(forKey: key)
guard case .number(let number) = value else {
throw self.createTypeMismatchError(type: T.self, forKey: key, value: value)
}
guard let integer = T(number) else {
throw DecodingError.dataCorruptedError(
forKey: key,
in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self)."
)
}
return integer
}
@inline(__always) private func decodeLosslessStringConvertible<T: LosslessStringConvertible>(
key: Self.Key) throws -> T {
let value = try getValue(forKey: key)
guard case .number(let number) = value else {
throw self.createTypeMismatchError(type: T.self, forKey: key, value: value)
}
guard let floatingPoint = T(number) else {
throw DecodingError.dataCorruptedError(
forKey: key,
in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self)."
)
}
return floatingPoint
}
}
struct _SingleValueDecodingContainter: SingleValueDecodingContainer {
let impl: _DecoderImpl
let value: AttributeValue
let codingPath: [CodingKey]
init(impl: _DecoderImpl, codingPath: [CodingKey], value: AttributeValue) {
self.impl = impl
self.codingPath = codingPath
self.value = value
}
func decodeNil() -> Bool {
self.value == .null
}
func decode(_: Bool.Type) throws -> Bool {
guard case .boolean(let bool) = self.value else {
throw self.createTypeMismatchError(type: Bool.self, value: self.value)
}
return bool
}
func decode(_: String.Type) throws -> String {
guard case .string(let string) = self.value else {
throw self.createTypeMismatchError(type: String.self, value: self.value)
}
return string
}
func decode(_: Double.Type) throws -> Double {
try self.decodeLosslessStringConvertible()
}
func decode(_: Float.Type) throws -> Float {
try self.decodeLosslessStringConvertible()
}
func decode(_: Int.Type) throws -> Int {
try self.decodeFixedWidthInteger()
}
func decode(_: Int8.Type) throws -> Int8 {
try self.decodeFixedWidthInteger()
}
func decode(_: Int16.Type) throws -> Int16 {
try self.decodeFixedWidthInteger()
}
func decode(_: Int32.Type) throws -> Int32 {
try self.decodeFixedWidthInteger()
}
func decode(_: Int64.Type) throws -> Int64 {
try self.decodeFixedWidthInteger()
}
func decode(_: UInt.Type) throws -> UInt {
try self.decodeFixedWidthInteger()
}
func decode(_: UInt8.Type) throws -> UInt8 {
try self.decodeFixedWidthInteger()
}
func decode(_: UInt16.Type) throws -> UInt16 {
try self.decodeFixedWidthInteger()
}
func decode(_: UInt32.Type) throws -> UInt32 {
try self.decodeFixedWidthInteger()
}
func decode(_: UInt64.Type) throws -> UInt64 {
try self.decodeFixedWidthInteger()
}
func decode<T>(_: T.Type) throws -> T where T: Decodable {
try T(from: self.impl)
}
@inline(__always) private func createTypeMismatchError(type: Any.Type, value: AttributeValue) -> DecodingError {
DecodingError.typeMismatch(type, .init(
codingPath: self.codingPath,
debugDescription: "Expected to decode \(type) but found \(value.debugDataTypeDescription) instead."
))
}
@inline(__always) private func decodeFixedWidthInteger<T: FixedWidthInteger>() throws
-> T {
guard case .number(let number) = self.value else {
throw self.createTypeMismatchError(type: T.self, value: self.value)
}
guard let integer = T(number) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self)."
)
}
return integer
}
@inline(__always) private func decodeLosslessStringConvertible<T: LosslessStringConvertible>()
throws -> T {
guard case .number(let number) = self.value else {
throw self.createTypeMismatchError(type: T.self, value: self.value)
}
guard let floatingPoint = T(number) else {
throw DecodingError.dataCorruptedError(
in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self)."
)
}
return floatingPoint
}
}
struct _UnkeyedDecodingContainer: UnkeyedDecodingContainer {
let impl: _DecoderImpl
let codingPath: [CodingKey]
let array: [AttributeValue]
let count: Int? // protocol requirement to be optional
var isAtEnd = false
var currentIndex = 0
init(impl: _DecoderImpl, codingPath: [CodingKey], array: [AttributeValue]) {
self.impl = impl
self.codingPath = codingPath
self.array = array
self.count = array.count
}
mutating func decodeNil() throws -> Bool {
if self.array[self.currentIndex] == .null {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
return true
}
// The protocol states:
// If the value is not null, does not increment currentIndex.
return false
}
mutating func decode(_ type: Bool.Type) throws -> Bool {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
guard case .boolean(let bool) = self.array[self.currentIndex] else {
throw self.createTypeMismatchError(type: type, value: self.array[self.currentIndex])
}
return bool
}
mutating func decode(_ type: String.Type) throws -> String {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
guard case .string(let string) = self.array[self.currentIndex] else {
throw self.createTypeMismatchError(type: type, value: self.array[self.currentIndex])
}
return string
}
mutating func decode(_: Double.Type) throws -> Double {
try self.decodeLosslessStringConvertible()
}
mutating func decode(_: Float.Type) throws -> Float {
try self.decodeLosslessStringConvertible()
}
mutating func decode(_: Int.Type) throws -> Int {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: Int8.Type) throws -> Int8 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: Int16.Type) throws -> Int16 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: Int32.Type) throws -> Int32 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: Int64.Type) throws -> Int64 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: UInt.Type) throws -> UInt {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: UInt8.Type) throws -> UInt8 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: UInt16.Type) throws -> UInt16 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: UInt32.Type) throws -> UInt32 {
try self.decodeFixedWidthInteger()
}
mutating func decode(_: UInt64.Type) throws -> UInt64 {
try self.decodeFixedWidthInteger()
}
mutating func decode<T>(_: T.Type) throws -> T where T: Decodable {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
let json = self.array[self.currentIndex]
var newPath = self.codingPath
newPath.append(ArrayKey(index: self.currentIndex))
let decoder = _DecoderImpl(userInfo: impl.userInfo, from: json, codingPath: newPath)
return try T(from: decoder)
}
mutating func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type) throws
-> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
try self.impl.container(keyedBy: type)
}
mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer {
try self.impl.unkeyedContainer()
}
mutating func superDecoder() throws -> Swift.Decoder {
self.impl
}
@inline(__always) private func createTypeMismatchError(type: Any.Type, value: AttributeValue) -> DecodingError {
let codingPath = self.codingPath + [ArrayKey(index: self.currentIndex)]
return DecodingError.typeMismatch(type, .init(
codingPath: codingPath, debugDescription: "Expected to decode \(type) but found \(value.debugDataTypeDescription) instead."
))
}
@inline(__always) private mutating func decodeFixedWidthInteger<T: FixedWidthInteger>() throws
-> T {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
guard case .number(let number) = self.array[self.currentIndex] else {
throw self.createTypeMismatchError(type: T.self, value: self.array[self.currentIndex])
}
guard let integer = T(number) else {
throw DecodingError.dataCorruptedError(in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self).")
}
return integer
}
@inline(__always) private mutating func decodeLosslessStringConvertible<T: LosslessStringConvertible>()
throws -> T {
defer {
currentIndex += 1
if currentIndex == count {
isAtEnd = true
}
}
guard case .number(let number) = self.array[self.currentIndex] else {
throw self.createTypeMismatchError(type: T.self, value: self.array[self.currentIndex])
}
guard let float = T(number) else {
throw DecodingError.dataCorruptedError(in: self,
debugDescription: "Parsed JSON number <\(number)> does not fit in \(T.self).")
}
return float
}
}
}
extension DynamoDB.AttributeValue {
fileprivate var debugDataTypeDescription: String {
switch self {
case .list:
return "a list"
case .boolean:
return "boolean"
case .number:
return "a number"
case .string:
return "a string"
case .map:
return "a map"
case .null:
return "null"
case .binary:
return "bytes"
case .binarySet:
return "a set of bytes"
case .stringSet:
return "a set of strings"
case .numberSet:
return "a set of numbers"
}
}
}
-81
View File
@@ -1,81 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
// https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html
public enum S3 {
public struct Event: Decodable {
public struct Record: Decodable {
public let eventVersion: String
public let eventSource: String
public let awsRegion: AWSRegion
@ISO8601WithFractionalSecondsCoding
public var eventTime: Date
public let eventName: String
public let userIdentity: UserIdentity
public let requestParameters: RequestParameters
public let responseElements: [String: String]
public let s3: Entity
}
public let records: [Record]
public enum CodingKeys: String, CodingKey {
case records = "Records"
}
}
public struct RequestParameters: Codable, Equatable {
public let sourceIPAddress: String
}
public struct UserIdentity: Codable, Equatable {
public let principalId: String
}
public struct Entity: Codable {
public let configurationId: String
public let schemaVersion: String
public let bucket: Bucket
public let object: Object
enum CodingKeys: String, CodingKey {
case configurationId
case schemaVersion = "s3SchemaVersion"
case bucket
case object
}
}
public struct Bucket: Codable {
public let name: String
public let ownerIdentity: UserIdentity
public let arn: String
}
public struct Object: Codable {
public let key: String
/// The object's size in bytes.
///
/// Note: This property is available for all event types except "ObjectRemoved:*"
public let size: UInt64?
public let urlDecodedKey: String?
public let versionId: String?
public let eTag: String
public let sequencer: String
}
}
-100
View File
@@ -1,100 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
// https://docs.aws.amazon.com/lambda/latest/dg/services-ses.html
public enum SES {
public struct Event: Decodable {
public struct Record: Decodable {
public let eventSource: String
public let eventVersion: String
public let ses: Message
}
public let records: [Record]
public enum CodingKeys: String, CodingKey {
case records = "Records"
}
}
public struct Message: Decodable {
public let mail: Mail
public let receipt: Receipt
}
public struct Mail: Decodable {
public let commonHeaders: CommonHeaders
public let destination: [String]
public let headers: [Header]
public let headersTruncated: Bool
public let messageId: String
public let source: String
@ISO8601WithFractionalSecondsCoding public var timestamp: Date
}
public struct CommonHeaders: Decodable {
public let bcc: [String]?
public let cc: [String]?
@RFC5322DateTimeCoding public var date: Date
public let from: [String]
public let messageId: String
public let returnPath: String?
public let subject: String?
public let to: [String]?
}
public struct Header: Decodable {
public let name: String
public let value: String
}
public struct Receipt: Decodable {
public let action: Action
public let dmarcPolicy: DMARCPolicy?
public let dmarcVerdict: Verdict?
public let dkimVerdict: Verdict
public let processingTimeMillis: Int
public let recipients: [String]
public let spamVerdict: Verdict
public let spfVerdict: Verdict
@ISO8601WithFractionalSecondsCoding public var timestamp: Date
public let virusVerdict: Verdict
}
public struct Action: Decodable {
public let functionArn: String
public let invocationType: String
public let type: String
}
public struct Verdict: Decodable {
public let status: Status
}
public enum DMARCPolicy: String, Decodable {
case none
case quarantine
case reject
}
public enum Status: String, Decodable {
case pass = "PASS"
case fail = "FAIL"
case gray = "GRAY"
case processingFailed = "PROCESSING_FAILED"
}
}
-108
View File
@@ -1,108 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
// https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html
public enum SNS {
public struct Event: Decodable {
public struct Record: Decodable {
public let eventVersion: String
public let eventSubscriptionArn: String
public let eventSource: String
public let sns: Message
public enum CodingKeys: String, CodingKey {
case eventVersion = "EventVersion"
case eventSubscriptionArn = "EventSubscriptionArn"
case eventSource = "EventSource"
case sns = "Sns"
}
}
public let records: [Record]
public enum CodingKeys: String, CodingKey {
case records = "Records"
}
}
public struct Message {
public enum Attribute {
case string(String)
case binary([UInt8])
}
public let signature: String
public let messageId: String
public let type: String
public let topicArn: String
public let messageAttributes: [String: Attribute]?
public let signatureVersion: String
@ISO8601WithFractionalSecondsCoding
public var timestamp: Date
public let signingCertURL: String
public let message: String
public let unsubscribeUrl: String
public let subject: String?
}
}
extension SNS.Message: Decodable {
enum CodingKeys: String, CodingKey {
case signature = "Signature"
case messageId = "MessageId"
case type = "Type"
case topicArn = "TopicArn"
case messageAttributes = "MessageAttributes"
case signatureVersion = "SignatureVersion"
case timestamp = "Timestamp"
case signingCertURL = "SigningCertUrl"
case message = "Message"
case unsubscribeUrl = "UnsubscribeUrl"
case subject = "Subject"
}
}
extension SNS.Message.Attribute: Equatable {}
extension SNS.Message.Attribute: Decodable {
enum CodingKeys: String, CodingKey {
case dataType = "Type"
case dataValue = "Value"
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dataType = try container.decode(String.self, forKey: .dataType)
// https://docs.aws.amazon.com/sns/latest/dg/sns-message-attributes.html#SNSMessageAttributes.DataTypes
switch dataType {
case "String":
let value = try container.decode(String.self, forKey: .dataValue)
self = .string(value)
case "Binary":
let base64encoded = try container.decode(String.self, forKey: .dataValue)
let bytes = try base64encoded.base64decoded()
self = .binary(bytes)
default:
throw DecodingError.dataCorruptedError(forKey: .dataType, in: container, debugDescription: """
Unexpected value \"\(dataType)\" for key \(CodingKeys.dataType).
Expected `String` or `Binary`.
""")
}
}
}
-97
View File
@@ -1,97 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
public enum SQS {
public struct Event: Decodable {
public let records: [Message]
enum CodingKeys: String, CodingKey {
case records = "Records"
}
}
public struct Message {
/// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_MessageAttributeValue.html
public enum Attribute {
case string(String)
case binary([UInt8])
case number(String)
}
public let messageId: String
public let receiptHandle: String
public var body: String
public let md5OfBody: String
public let md5OfMessageAttributes: String?
public let attributes: [String: String]
public let messageAttributes: [String: Attribute]
public let eventSourceArn: String
public let eventSource: String
public let awsRegion: AWSRegion
}
}
extension SQS.Message: Decodable {
enum CodingKeys: String, CodingKey {
case messageId
case receiptHandle
case body
case md5OfBody
case md5OfMessageAttributes
case attributes
case messageAttributes
case eventSourceArn = "eventSourceARN"
case eventSource
case awsRegion
}
}
extension SQS.Message.Attribute: Equatable {}
extension SQS.Message.Attribute: Decodable {
enum CodingKeys: String, CodingKey {
case dataType
case stringValue
case binaryValue
// BinaryListValue and StringListValue are unimplemented since
// they are not implemented as discussed here:
// https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_MessageAttributeValue.html
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let dataType = try container.decode(String.self, forKey: .dataType)
switch dataType {
case "String":
let value = try container.decode(String.self, forKey: .stringValue)
self = .string(value)
case "Number":
let value = try container.decode(String.self, forKey: .stringValue)
self = .number(value)
case "Binary":
let base64encoded = try container.decode(String.self, forKey: .binaryValue)
let bytes = try base64encoded.base64decoded()
self = .binary(bytes)
default:
throw DecodingError.dataCorruptedError(forKey: .dataType, in: container, debugDescription: """
Unexpected value \"\(dataType)\" for key \(CodingKeys.dataType).
Expected `String`, `Binary` or `Number`.
""")
}
}
}
-219
View File
@@ -1,219 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// This is a vendored version from:
// https://github.com/fabianfett/swift-base64-kit
struct Base64 {}
// MARK: - Decode -
extension Base64 {
struct DecodingOptions: OptionSet {
let rawValue: UInt
init(rawValue: UInt) { self.rawValue = rawValue }
static let base64UrlAlphabet = DecodingOptions(rawValue: UInt(1 << 0))
}
enum DecodingError: Error, Equatable {
case invalidLength
case invalidCharacter(UInt8)
case unexpectedPaddingCharacter
case unexpectedEnd
}
@inlinable
static func decode<Buffer: Collection>(encoded: Buffer, options: DecodingOptions = [])
throws -> [UInt8] where Buffer.Element == UInt8 {
let alphabet = options.contains(.base64UrlAlphabet)
? Base64.decodeBase64Url
: Base64.decodeBase64
// In Base64 4 encoded bytes, become 3 decoded bytes. We pad to the
// nearest multiple of three.
let inputLength = encoded.count
guard inputLength > 0 else { return [] }
guard inputLength % 4 == 0 else {
throw DecodingError.invalidLength
}
let inputBlocks = (inputLength + 3) / 4
let fullQualified = inputBlocks - 1
let outputLength = ((encoded.count + 3) / 4) * 3
var iterator = encoded.makeIterator()
var outputBytes = [UInt8]()
outputBytes.reserveCapacity(outputLength)
// fast loop. we don't expect any padding in here.
for _ in 0 ..< fullQualified {
let firstValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
let secondValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
let thirdValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
let forthValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
outputBytes.append((firstValue << 2) | (secondValue >> 4))
outputBytes.append((secondValue << 4) | (thirdValue >> 2))
outputBytes.append((thirdValue << 6) | forthValue)
}
// last 4 bytes. we expect padding characters in three and four
let firstValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
let secondValue: UInt8 = try iterator.nextValue(alphabet: alphabet)
let thirdValue: UInt8? = try iterator.nextValueOrEmpty(alphabet: alphabet)
let forthValue: UInt8? = try iterator.nextValueOrEmpty(alphabet: alphabet)
outputBytes.append((firstValue << 2) | (secondValue >> 4))
if let thirdValue = thirdValue {
outputBytes.append((secondValue << 4) | (thirdValue >> 2))
if let forthValue = forthValue {
outputBytes.append((thirdValue << 6) | forthValue)
}
}
return outputBytes
}
@inlinable
static func decode(encoded: String, options: DecodingOptions = []) throws -> [UInt8] {
// A string can be backed by a contiguous storage (pure swift string)
// or a nsstring (bridged string from objc). We only get a pointer
// to the contiguous storage, if the input string is a swift string.
// Therefore to transform the nsstring backed input into a swift
// string we concat the input with nothing, causing a copy on write
// into a swift string.
let decoded = try encoded.utf8.withContiguousStorageIfAvailable { pointer in
try self.decode(encoded: pointer, options: options)
}
if decoded != nil {
return decoded!
}
return try self.decode(encoded: encoded + "", options: options)
}
// MARK: Internal
@usableFromInline
static let decodeBase64: [UInt8] = [
// 0 1 2 3 4 5 6 7 8 9
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 3
255, 255, 255, 62, 255, 255, 255, 63, 52, 53, // 4
54, 55, 56, 57, 58, 59, 60, 61, 255, 255, // 5
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, // 6
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 7
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 8
25, 255, 255, 255, 255, 255, 255, 26, 27, 28, // 9
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 10
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 11
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, // 12
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 13
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 14
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 15
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 16
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 17
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 18
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 19
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 20
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 21
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 22
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 23
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 24
255, 255, 255, 255, 255, // 25
]
@usableFromInline
static let decodeBase64Url: [UInt8] = [
// 0 1 2 3 4 5 6 7 8 9
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 0
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 1
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 2
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 3
255, 255, 255, 255, 255, 62, 255, 255, 52, 53, // 4
54, 55, 56, 57, 58, 59, 60, 61, 255, 255, // 5
255, 254, 255, 255, 255, 0, 1, 2, 3, 4, // 6
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 7
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 8
25, 255, 255, 255, 255, 63, 255, 26, 27, 28, // 9
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 10
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 11
49, 50, 51, 255, 255, 255, 255, 255, 255, 255, // 12
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 13
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 14
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 15
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 16
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 17
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 18
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 19
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 20
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 21
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 22
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 23
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // 24
255, 255, 255, 255, 255, // 25
]
@usableFromInline
static let paddingCharacter: UInt8 = 254
}
extension IteratorProtocol where Self.Element == UInt8 {
mutating func nextValue(alphabet: [UInt8]) throws -> UInt8 {
let ascii = self.next()!
let value = alphabet[Int(ascii)]
if value < 64 {
return value
}
if value == Base64.paddingCharacter {
throw Base64.DecodingError.unexpectedPaddingCharacter
}
throw Base64.DecodingError.invalidCharacter(ascii)
}
mutating func nextValueOrEmpty(alphabet: [UInt8]) throws -> UInt8? {
let ascii = self.next()!
let value = alphabet[Int(ascii)]
if value < 64 {
return value
}
if value == Base64.paddingCharacter {
return nil
}
throw Base64.DecodingError.invalidCharacter(ascii)
}
}
// MARK: - Extensions -
extension String {
func base64decoded(options: Base64.DecodingOptions = []) throws -> [UInt8] {
// In Base64, 3 bytes become 4 output characters, and we pad to the nearest multiple
// of four.
try Base64.decode(encoded: self, options: options)
}
}
@@ -1,108 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
import struct Foundation.Date
import class Foundation.DateFormatter
import struct Foundation.Locale
import struct Foundation.TimeZone
@propertyWrapper
public struct ISO8601Coding: Decodable {
public let wrappedValue: Date
public init(wrappedValue: Date) {
self.wrappedValue = wrappedValue
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.dateFormatter.date(from: dateString) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription:
"Expected date to be in ISO8601 date format, but `\(dateString)` is not in the correct format")
}
self.wrappedValue = date
}
private static let dateFormatter: DateFormatter = Self.createDateFormatter()
private static func createDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
return formatter
}
}
@propertyWrapper
public struct ISO8601WithFractionalSecondsCoding: Decodable {
public let wrappedValue: Date
public init(wrappedValue: Date) {
self.wrappedValue = wrappedValue
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
guard let date = Self.dateFormatter.date(from: dateString) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription:
"Expected date to be in ISO8601 date format with fractional seconds, but `\(dateString)` is not in the correct format")
}
self.wrappedValue = date
}
private static let dateFormatter: DateFormatter = Self.createDateFormatter()
private static func createDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
return formatter
}
}
@propertyWrapper
public struct RFC5322DateTimeCoding: Decodable {
public let wrappedValue: Date
public init(wrappedValue: Date) {
self.wrappedValue = wrappedValue
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
var string = try container.decode(String.self)
// RFC5322 dates sometimes have the alphabetic version of the timezone in brackets after the numeric version. The date formatter
// fails to parse this so we need to remove this before parsing.
if let bracket = string.firstIndex(of: "(") {
string = String(string[string.startIndex ..< bracket].trimmingCharacters(in: .whitespaces))
}
guard let date = Self.dateFormatter.date(from: string) else {
throw DecodingError.dataCorruptedError(in: container, debugDescription:
"Expected date to be in RFC5322 date-time format with fractional seconds, but `\(string)` is not in the correct format")
}
self.wrappedValue = date
}
private static let dateFormatter: DateFormatter = Self.createDateFormatter()
private static func createDateFormatter() -> DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "EEE, d MMM yyy HH:mm:ss z"
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}
}
-187
View File
@@ -1,187 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
// MARK: HTTPMethod
public typealias HTTPHeaders = [String: String]
public typealias HTTPMultiValueHeaders = [String: [String]]
public struct HTTPMethod: RawRepresentable, Equatable {
public var rawValue: String
public init?(rawValue: String) {
guard rawValue.isValidHTTPToken else {
return nil
}
self.rawValue = rawValue
}
public static var GET: HTTPMethod { HTTPMethod(rawValue: "GET")! }
public static var POST: HTTPMethod { HTTPMethod(rawValue: "POST")! }
public static var PUT: HTTPMethod { HTTPMethod(rawValue: "PUT")! }
public static var PATCH: HTTPMethod { HTTPMethod(rawValue: "PATCH")! }
public static var DELETE: HTTPMethod { HTTPMethod(rawValue: "DELETE")! }
public static var OPTIONS: HTTPMethod { HTTPMethod(rawValue: "OPTIONS")! }
public static var HEAD: HTTPMethod { HTTPMethod(rawValue: "HEAD")! }
public static func RAW(value: String) -> HTTPMethod? { HTTPMethod(rawValue: value) }
}
extension HTTPMethod: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let rawMethod = try container.decode(String.self)
guard let method = HTTPMethod(rawValue: rawMethod) else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: #"Method "\#(rawMethod)" does not conform to allowed http method syntax defined in RFC 7230 Section 3.2.6"#
)
}
self = method
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
}
}
// MARK: HTTPResponseStatus
public struct HTTPResponseStatus {
public let code: UInt
public let reasonPhrase: String?
public init(code: UInt, reasonPhrase: String? = nil) {
self.code = code
self.reasonPhrase = reasonPhrase
}
public static var `continue`: HTTPResponseStatus { HTTPResponseStatus(code: 100) }
public static var switchingProtocols: HTTPResponseStatus { HTTPResponseStatus(code: 101) }
public static var processing: HTTPResponseStatus { HTTPResponseStatus(code: 102) }
public static var earlyHints: HTTPResponseStatus { HTTPResponseStatus(code: 103) }
public static var ok: HTTPResponseStatus { HTTPResponseStatus(code: 200) }
public static var created: HTTPResponseStatus { HTTPResponseStatus(code: 201) }
public static var accepted: HTTPResponseStatus { HTTPResponseStatus(code: 202) }
public static var nonAuthoritativeInformation: HTTPResponseStatus { HTTPResponseStatus(code: 203) }
public static var noContent: HTTPResponseStatus { HTTPResponseStatus(code: 204) }
public static var resetContent: HTTPResponseStatus { HTTPResponseStatus(code: 205) }
public static var partialContent: HTTPResponseStatus { HTTPResponseStatus(code: 206) }
public static var multiStatus: HTTPResponseStatus { HTTPResponseStatus(code: 207) }
public static var alreadyReported: HTTPResponseStatus { HTTPResponseStatus(code: 208) }
public static var imUsed: HTTPResponseStatus { HTTPResponseStatus(code: 226) }
public static var multipleChoices: HTTPResponseStatus { HTTPResponseStatus(code: 300) }
public static var movedPermanently: HTTPResponseStatus { HTTPResponseStatus(code: 301) }
public static var found: HTTPResponseStatus { HTTPResponseStatus(code: 302) }
public static var seeOther: HTTPResponseStatus { HTTPResponseStatus(code: 303) }
public static var notModified: HTTPResponseStatus { HTTPResponseStatus(code: 304) }
public static var useProxy: HTTPResponseStatus { HTTPResponseStatus(code: 305) }
public static var temporaryRedirect: HTTPResponseStatus { HTTPResponseStatus(code: 307) }
public static var permanentRedirect: HTTPResponseStatus { HTTPResponseStatus(code: 308) }
public static var badRequest: HTTPResponseStatus { HTTPResponseStatus(code: 400) }
public static var unauthorized: HTTPResponseStatus { HTTPResponseStatus(code: 401) }
public static var paymentRequired: HTTPResponseStatus { HTTPResponseStatus(code: 402) }
public static var forbidden: HTTPResponseStatus { HTTPResponseStatus(code: 403) }
public static var notFound: HTTPResponseStatus { HTTPResponseStatus(code: 404) }
public static var methodNotAllowed: HTTPResponseStatus { HTTPResponseStatus(code: 405) }
public static var notAcceptable: HTTPResponseStatus { HTTPResponseStatus(code: 406) }
public static var proxyAuthenticationRequired: HTTPResponseStatus { HTTPResponseStatus(code: 407) }
public static var requestTimeout: HTTPResponseStatus { HTTPResponseStatus(code: 408) }
public static var conflict: HTTPResponseStatus { HTTPResponseStatus(code: 409) }
public static var gone: HTTPResponseStatus { HTTPResponseStatus(code: 410) }
public static var lengthRequired: HTTPResponseStatus { HTTPResponseStatus(code: 411) }
public static var preconditionFailed: HTTPResponseStatus { HTTPResponseStatus(code: 412) }
public static var payloadTooLarge: HTTPResponseStatus { HTTPResponseStatus(code: 413) }
public static var uriTooLong: HTTPResponseStatus { HTTPResponseStatus(code: 414) }
public static var unsupportedMediaType: HTTPResponseStatus { HTTPResponseStatus(code: 415) }
public static var rangeNotSatisfiable: HTTPResponseStatus { HTTPResponseStatus(code: 416) }
public static var expectationFailed: HTTPResponseStatus { HTTPResponseStatus(code: 417) }
public static var imATeapot: HTTPResponseStatus { HTTPResponseStatus(code: 418) }
public static var misdirectedRequest: HTTPResponseStatus { HTTPResponseStatus(code: 421) }
public static var unprocessableEntity: HTTPResponseStatus { HTTPResponseStatus(code: 422) }
public static var locked: HTTPResponseStatus { HTTPResponseStatus(code: 423) }
public static var failedDependency: HTTPResponseStatus { HTTPResponseStatus(code: 424) }
public static var upgradeRequired: HTTPResponseStatus { HTTPResponseStatus(code: 426) }
public static var preconditionRequired: HTTPResponseStatus { HTTPResponseStatus(code: 428) }
public static var tooManyRequests: HTTPResponseStatus { HTTPResponseStatus(code: 429) }
public static var requestHeaderFieldsTooLarge: HTTPResponseStatus { HTTPResponseStatus(code: 431) }
public static var unavailableForLegalReasons: HTTPResponseStatus { HTTPResponseStatus(code: 451) }
public static var internalServerError: HTTPResponseStatus { HTTPResponseStatus(code: 500) }
public static var notImplemented: HTTPResponseStatus { HTTPResponseStatus(code: 501) }
public static var badGateway: HTTPResponseStatus { HTTPResponseStatus(code: 502) }
public static var serviceUnavailable: HTTPResponseStatus { HTTPResponseStatus(code: 503) }
public static var gatewayTimeout: HTTPResponseStatus { HTTPResponseStatus(code: 504) }
public static var httpVersionNotSupported: HTTPResponseStatus { HTTPResponseStatus(code: 505) }
public static var variantAlsoNegotiates: HTTPResponseStatus { HTTPResponseStatus(code: 506) }
public static var insufficientStorage: HTTPResponseStatus { HTTPResponseStatus(code: 507) }
public static var loopDetected: HTTPResponseStatus { HTTPResponseStatus(code: 508) }
public static var notExtended: HTTPResponseStatus { HTTPResponseStatus(code: 510) }
public static var networkAuthenticationRequired: HTTPResponseStatus { HTTPResponseStatus(code: 511) }
}
extension HTTPResponseStatus: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.code == rhs.code
}
}
extension HTTPResponseStatus: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.code = try container.decode(UInt.self)
self.reasonPhrase = nil
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.code)
}
}
extension String {
internal var isValidHTTPToken: Bool {
self.utf8.allSatisfy { (char) -> Bool in
switch char {
case UInt8(ascii: "a") ... UInt8(ascii: "z"),
UInt8(ascii: "A") ... UInt8(ascii: "Z"),
UInt8(ascii: "0") ... UInt8(ascii: "9"),
UInt8(ascii: "!"),
UInt8(ascii: "#"),
UInt8(ascii: "$"),
UInt8(ascii: "%"),
UInt8(ascii: "&"),
UInt8(ascii: "'"),
UInt8(ascii: "*"),
UInt8(ascii: "+"),
UInt8(ascii: "-"),
UInt8(ascii: "."),
UInt8(ascii: "^"),
UInt8(ascii: "_"),
UInt8(ascii: "`"),
UInt8(ascii: "|"),
UInt8(ascii: "~"):
return true
default:
return false
}
}
}
}
-64
View File
@@ -1,64 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class ALBTests: XCTestCase {
static let exampleSingleValueHeadersEventBody = """
{
"requestContext":{
"elb":{
"targetGroupArn": "arn:aws:elasticloadbalancing:eu-central-1:079477498937:targetgroup/EinSternDerDeinenNamenTraegt/621febf5a44b2ce5"
}
},
"httpMethod": "GET",
"path": "/",
"queryStringParameters": {},
"headers":{
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"accept-encoding": "gzip, deflate",
"accept-language": "en-us",
"connection": "keep-alive",
"host": "event-testl-1wa3wrvmroilb-358275751.eu-central-1.elb.amazonaws.com",
"upgrade-insecure-requests": "1",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.2 Safari/605.1.15",
"x-amzn-trace-id": "Root=1-5e189143-ad18a2b0a7728cd0dac45e10",
"x-forwarded-for": "90.187.8.137",
"x-forwarded-port": "80",
"x-forwarded-proto": "http"
},
"body":"",
"isBase64Encoded":false
}
"""
func testRequestWithSingleValueHeadersEvent() {
let data = ALBTests.exampleSingleValueHeadersEventBody.data(using: .utf8)!
do {
let decoder = JSONDecoder()
let event = try decoder.decode(ALB.TargetGroupRequest.self, from: data)
XCTAssertEqual(event.httpMethod, .GET)
XCTAssertEqual(event.body, "")
XCTAssertEqual(event.isBase64Encoded, false)
XCTAssertEqual(event.headers?.count, 11)
XCTAssertEqual(event.path, "/")
XCTAssertEqual(event.queryStringParameters, [:])
} catch {
XCTFail("Unexpected error: \(error)")
}
}
}
@@ -1,91 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class APIGatewayV2Tests: XCTestCase {
static let exampleGetEventBody = """
{
"routeKey":"GET /hello",
"version":"2.0",
"rawPath":"/hello",
"stageVariables":{
"foo":"bar"
},
"requestContext":{
"timeEpoch":1587750461466,
"domainPrefix":"hello",
"authorizer":{
"jwt":{
"scopes":[
"hello"
],
"claims":{
"aud":"customers",
"iss":"https://hello.test.com/",
"iat":"1587749276",
"exp":"1587756476"
}
}
},
"accountId":"0123456789",
"stage":"$default",
"domainName":"hello.test.com",
"apiId":"pb5dg6g3rg",
"requestId":"LgLpnibOFiAEPCA=",
"http":{
"path":"/hello",
"userAgent":"Paw/3.1.10 (Macintosh; OS X/10.15.4) GCDHTTPRequest",
"method":"GET",
"protocol":"HTTP/1.1",
"sourceIp":"91.64.117.86"
},
"time":"24/Apr/2020:17:47:41 +0000"
},
"isBase64Encoded":false,
"rawQueryString":"foo=bar",
"queryStringParameters":{
"foo":"bar"
},
"headers":{
"x-forwarded-proto":"https",
"x-forwarded-for":"91.64.117.86",
"x-forwarded-port":"443",
"authorization":"Bearer abc123",
"host":"hello.test.com",
"x-amzn-trace-id":"Root=1-5ea3263d-07c5d5ddfd0788bed7dad831",
"user-agent":"Paw/3.1.10 (Macintosh; OS X/10.15.4) GCDHTTPRequest",
"content-length":"0"
}
}
"""
// MARK: - Request -
// MARK: Decoding
func testRequestDecodingExampleGetRequest() {
let data = APIGatewayV2Tests.exampleGetEventBody.data(using: .utf8)!
var req: APIGateway.V2.Request?
XCTAssertNoThrow(req = try JSONDecoder().decode(APIGateway.V2.Request.self, from: data))
XCTAssertEqual(req?.rawPath, "/hello")
XCTAssertEqual(req?.context.http.method, .GET)
XCTAssertEqual(req?.queryStringParameters?.count, 1)
XCTAssertEqual(req?.rawQueryString, "foo=bar")
XCTAssertEqual(req?.headers.count, 8)
XCTAssertNil(req?.body)
}
}
@@ -1,77 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class APIGatewayTests: XCTestCase {
static let exampleGetEventBody = """
{"httpMethod": "GET", "body": null, "resource": "/test", "requestContext": {"resourceId": "123456", "apiId": "1234567890", "resourcePath": "/test", "httpMethod": "GET", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "accountId": "123456789012", "stage": "Prod", "identity": {"apiKey": null, "userArn": null, "cognitoAuthenticationType": null, "caller": null, "userAgent": "Custom User Agent String", "user": null, "cognitoIdentityPoolId": null, "cognitoAuthenticationProvider": null, "sourceIp": "127.0.0.1", "accountId": null}, "extendedRequestId": null, "path": "/test"}, "queryStringParameters": null, "multiValueQueryStringParameters": null, "headers": {"Host": "127.0.0.1:3000", "Connection": "keep-alive", "Cache-Control": "max-age=0", "Dnt": "1", "Upgrade-Insecure-Requests": "1", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36 Edg/78.0.276.24", "Sec-Fetch-User": "?1", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", "Sec-Fetch-Site": "none", "Sec-Fetch-Mode": "navigate", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "en-US,en;q=0.9", "X-Forwarded-Proto": "http", "X-Forwarded-Port": "3000"}, "multiValueHeaders": {"Host": ["127.0.0.1:3000"], "Connection": ["keep-alive"], "Cache-Control": ["max-age=0"], "Dnt": ["1"], "Upgrade-Insecure-Requests": ["1"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36 Edg/78.0.276.24"], "Sec-Fetch-User": ["?1"], "Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3"], "Sec-Fetch-Site": ["none"], "Sec-Fetch-Mode": ["navigate"], "Accept-Encoding": ["gzip, deflate, br"], "Accept-Language": ["en-US,en;q=0.9"], "X-Forwarded-Proto": ["http"], "X-Forwarded-Port": ["3000"]}, "pathParameters": null, "stageVariables": null, "path": "/test", "isBase64Encoded": false}
"""
static let todoPostEventBody = """
{"httpMethod": "POST", "body": "{\\"title\\":\\"a todo\\"}", "resource": "/todos", "requestContext": {"resourceId": "123456", "apiId": "1234567890", "resourcePath": "/todos", "httpMethod": "POST", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "accountId": "123456789012", "stage": "test", "identity": {"apiKey": null, "userArn": null, "cognitoAuthenticationType": null, "caller": null, "userAgent": "Custom User Agent String", "user": null, "cognitoIdentityPoolId": null, "cognitoAuthenticationProvider": null, "sourceIp": "127.0.0.1", "accountId": null}, "extendedRequestId": null, "path": "/todos"}, "queryStringParameters": null, "multiValueQueryStringParameters": null, "headers": {"Host": "127.0.0.1:3000", "Connection": "keep-alive", "Content-Length": "18", "Pragma": "no-cache", "Cache-Control": "no-cache", "Accept": "text/plain, */*; q=0.01", "Origin": "http://todobackend.com", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.36 Safari/537.36 Edg/79.0.309.25", "Dnt": "1", "Content-Type": "application/json", "Sec-Fetch-Site": "cross-site", "Sec-Fetch-Mode": "cors", "Referer": "http://todobackend.com/specs/index.html?http://127.0.0.1:3000/todos", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "en-US,en;q=0.9", "X-Forwarded-Proto": "http", "X-Forwarded-Port": "3000"}, "multiValueHeaders": {"Host": ["127.0.0.1:3000"], "Connection": ["keep-alive"], "Content-Length": ["18"], "Pragma": ["no-cache"], "Cache-Control": ["no-cache"], "Accept": ["text/plain, */*; q=0.01"], "Origin": ["http://todobackend.com"], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.36 Safari/537.36 Edg/79.0.309.25"], "Dnt": ["1"], "Content-Type": ["application/json"], "Sec-Fetch-Site": ["cross-site"], "Sec-Fetch-Mode": ["cors"], "Referer": ["http://todobackend.com/specs/index.html?http://127.0.0.1:3000/todos"], "Accept-Encoding": ["gzip, deflate, br"], "Accept-Language": ["en-US,en;q=0.9"], "X-Forwarded-Proto": ["http"], "X-Forwarded-Port": ["3000"]}, "pathParameters": null, "stageVariables": null, "path": "/todos", "isBase64Encoded": false}
"""
// MARK: - Request -
// MARK: Decoding
func testRequestDecodingExampleGetRequest() {
let data = APIGatewayTests.exampleGetEventBody.data(using: .utf8)!
var req: APIGateway.Request?
XCTAssertNoThrow(req = try JSONDecoder().decode(APIGateway.Request.self, from: data))
XCTAssertEqual(req?.path, "/test")
XCTAssertEqual(req?.httpMethod, .GET)
}
func testRequestDecodingTodoPostRequest() {
let data = APIGatewayTests.todoPostEventBody.data(using: .utf8)!
var req: APIGateway.Request?
XCTAssertNoThrow(req = try JSONDecoder().decode(APIGateway.Request.self, from: data))
XCTAssertEqual(req?.path, "/todos")
XCTAssertEqual(req?.httpMethod, .POST)
}
// MARK: - Response -
// MARK: Encoding
struct JSONResponse: Codable {
let statusCode: UInt
let headers: [String: String]?
let body: String?
let isBase64Encoded: Bool?
}
func testResponseEncoding() {
let resp = APIGateway.Response(
statusCode: .ok,
headers: ["Server": "Test"],
body: "abc123"
)
var data: Data?
XCTAssertNoThrow(data = try JSONEncoder().encode(resp))
var json: JSONResponse?
XCTAssertNoThrow(json = try JSONDecoder().decode(JSONResponse.self, from: XCTUnwrap(data)))
XCTAssertEqual(json?.statusCode, resp.statusCode.code)
XCTAssertEqual(json?.body, resp.body)
XCTAssertEqual(json?.isBase64Encoded, resp.isBase64Encoded)
XCTAssertEqual(json?.headers?["Server"], "Test")
}
}
@@ -1,271 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class AppSyncTests: XCTestCase {
static let exampleEventBody = """
{
"arguments": {
"id": "my identifier"
},
"identity": {
"claims": {
"sub": "192879fc-a240-4bf1-ab5a-d6a00f3063f9",
"email_verified": true,
"iss": "https://cognito-idp.us-west-2.amazonaws.com/us-west-xxxxxxxxxxx",
"phone_number_verified": false,
"cognito:username": "jdoe",
"aud": "7471s60os7h0uu77i1tk27sp9n",
"event_id": "bc334ed8-a938-4474-b644-9547e304e606",
"token_use": "id",
"auth_time": 1599154213,
"phone_number": "+19999999999",
"exp": 1599157813,
"iat": 1599154213,
"email": "jdoe@email.com"
},
"defaultAuthStrategy": "ALLOW",
"groups": null,
"issuer": "https://cognito-idp.us-west-2.amazonaws.com/us-west-xxxxxxxxxxx",
"sourceIp": [
"1.1.1.1"
],
"sub": "192879fc-a240-4bf1-ab5a-d6a00f3063f9",
"username": "jdoe"
},
"source": null,
"request": {
"headers": {
"x-forwarded-for": "1.1.1.1, 2.2.2.2",
"cloudfront-viewer-country": "US",
"cloudfront-is-tablet-viewer": "false",
"via": "2.0 xxxxxxxxxxxxxxxx.cloudfront.net (CloudFront)",
"cloudfront-forwarded-proto": "https",
"origin": "https://us-west-1.console.aws.amazon.com",
"content-length": "217",
"accept-language": "en-US,en;q=0.9",
"host": "xxxxxxxxxxxxxxxx.appsync-api.us-west-1.amazonaws.com",
"x-forwarded-proto": "https",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36",
"accept": "*/*",
"cloudfront-is-mobile-viewer": "false",
"cloudfront-is-smarttv-viewer": "false",
"accept-encoding": "gzip, deflate, br",
"referer": "https://us-west-1.console.aws.amazon.com/appsync/home?region=us-west-1",
"content-type": "application/json",
"sec-fetch-mode": "cors",
"x-amz-cf-id": "3aykhqlUwQeANU-HGY7E_guV5EkNeMMtwyOgiA==",
"x-amzn-trace-id": "Root=1-5f512f51-fac632066c5e848ae714",
"authorization": "eyJraWQiOiJScWFCSlJqYVJlM0hrSnBTUFpIcVRXazNOW...",
"sec-fetch-dest": "empty",
"x-amz-user-agent": "AWS-Console-AppSync/",
"cloudfront-is-desktop-viewer": "true",
"sec-fetch-site": "cross-site",
"x-forwarded-port": "443"
}
},
"prev": null,
"info": {
"selectionSetList": [
"id",
"field1",
"field2"
],
"selectionSetGraphQL": "{ id }",
"parentTypeName": "Mutation",
"fieldName": "createSomething",
"variables": {}
},
"stash": {}
}
"""
// MARK: Decoding
func testRequestDecodingExampleEvent() {
let data = AppSyncTests.exampleEventBody.data(using: .utf8)!
var event: AppSync.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(AppSync.Event.self, from: data))
XCTAssertNotNil(event?.arguments)
XCTAssertEqual(event?.arguments["id"], .string("my identifier"))
XCTAssertEqual(event?.info.fieldName, "createSomething")
XCTAssertEqual(event?.info.parentTypeName, "Mutation")
XCTAssertEqual(event?.info.selectionSetList, ["id", "field1", "field2"])
XCTAssertEqual(event?.request.headers["accept-language"], "en-US,en;q=0.9")
switch event?.identity {
case .cognitoUserPools(let cognitoIdentity):
XCTAssertEqual(cognitoIdentity.defaultAuthStrategy, "ALLOW")
XCTAssertEqual(cognitoIdentity.issuer, "https://cognito-idp.us-west-2.amazonaws.com/us-west-xxxxxxxxxxx")
XCTAssertEqual(cognitoIdentity.sourceIp, ["1.1.1.1"])
XCTAssertEqual(cognitoIdentity.username, "jdoe")
XCTAssertEqual(cognitoIdentity.sub, "192879fc-a240-4bf1-ab5a-d6a00f3063f9")
default:
XCTAssertTrue(false, "a cognito identity was expected, but didn't find one.")
}
}
func testRequestDecodingEventWithSource() {
let eventBody = """
{
"arguments": {},
"identity": null,
"source": {
"name": "Hello",
"id": "1"
},
"request": {
"headers": {
"x-forwarded-for": "1.1.1.1, 2.2.2.2",
"accept-encoding": "gzip, deflate, br",
"cloudfront-viewer-country": "CA",
"cloudfront-is-tablet-viewer": "false",
"referer": "https://us-west-2.console.aws.amazon.com/",
"via": "2.0 xxxxxx.cloudfront.net (CloudFront)",
"cloudfront-forwarded-proto": "https",
"origin": "https://us-west-2.console.aws.amazon.com",
"x-api-key": "xxxxxxxxxxxxxxxxxxxxx",
"content-type": "application/json",
"x-amzn-trace-id": "Root=1-5fcd9a24-364c62405b418bd53c7984ce",
"x-amz-cf-id": "3aykhqlUwQeANU-HGY7E_guV5EkNeMMtwyOgiA==",
"content-length": "173",
"x-amz-user-agent": "AWS-Console-AppSync/",
"x-forwarded-proto": "https",
"host": "xxxxxxxxxxxxxxxx.appsync-api.us-west-2.amazonaws.com",
"accept-language": "en-ca",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15",
"cloudfront-is-desktop-viewer": "true",
"cloudfront-is-mobile-viewer": "false",
"accept": "*/*",
"x-forwarded-port": "443",
"cloudfront-is-smarttv-viewer": "false"
}
},
"prev": null,
"info": {
"selectionSetList": [
"address",
"id"
],
"selectionSetGraphQL": "{ address id}",
"parentTypeName": "Customer",
"fieldName": "address",
"variables": {}
},
"stash": {}
}
"""
let data = eventBody.data(using: .utf8)!
var event: AppSync.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(AppSync.Event.self, from: data))
XCTAssertEqual(event?.source?["name"], "Hello")
XCTAssertTrue(event?.stash?.isEmpty ?? false, "stash dictionary must be empty")
XCTAssertNil(event?.identity)
}
func testRequestDecodingIamIdentity() {
let eventBody = """
{
"arguments": {},
"identity": {
"accountId" : "accountId1",
"cognitoIdentityPoolId" : "cognitoIdentityPool2",
"cognitoIdentityId" : "cognitoIdentity3",
"sourceIp" : ["1.1.1.1"],
"username" : null,
"userArn" : "arn123",
"cognitoIdentityAuthType" : "authenticated",
"cognitoIdentityAuthProvider" : "authprovider"
},
"source": {
"name": "Hello",
"id": "1"
},
"request": {
"headers": {
"x-forwarded-for": "1.1.1.1, 2.2.2.2",
"accept-encoding": "gzip, deflate, br",
"cloudfront-viewer-country": "CA",
"cloudfront-is-tablet-viewer": "false",
"referer": "https://us-west-2.console.aws.amazon.com/",
"via": "2.0 xxxxxx.cloudfront.net (CloudFront)",
"cloudfront-forwarded-proto": "https",
"origin": "https://us-west-2.console.aws.amazon.com",
"x-api-key": "xxxxxxxxxxxxxxxxxxxxx",
"content-type": "application/json",
"x-amzn-trace-id": "Root=1-5fcd9a24-364c62405b418bd53c7984ce",
"x-amz-cf-id": "3aykhqlUwQeANU-HGY7E_guV5EkNeMMtwyOgiA==",
"content-length": "173",
"x-amz-user-agent": "AWS-Console-AppSync/",
"x-forwarded-proto": "https",
"host": "xxxxxxxxxxxxxxxx.appsync-api.us-west-2.amazonaws.com",
"accept-language": "en-ca",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.1 Safari/605.1.15",
"cloudfront-is-desktop-viewer": "true",
"cloudfront-is-mobile-viewer": "false",
"accept": "*/*",
"x-forwarded-port": "443",
"cloudfront-is-smarttv-viewer": "false"
}
},
"prev": null,
"info": {
"selectionSetList": [
"address",
"id"
],
"selectionSetGraphQL": "{ address id}",
"parentTypeName": "Customer",
"fieldName": "address",
"variables": {}
},
"stash": {}
}
"""
let data = eventBody.data(using: .utf8)!
var event: AppSync.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(AppSync.Event.self, from: data))
switch event?.identity {
case .iam(let iamIdentity):
XCTAssertEqual(iamIdentity.accountId, "accountId1")
XCTAssertEqual(iamIdentity.cognitoIdentityPoolId, "cognitoIdentityPool2")
XCTAssertEqual(iamIdentity.cognitoIdentityId, "cognitoIdentity3")
XCTAssertEqual(iamIdentity.sourceIp, ["1.1.1.1"])
XCTAssertNil(iamIdentity.username)
XCTAssertEqual(iamIdentity.userArn, "arn123")
XCTAssertEqual(iamIdentity.cognitoIdentityAuthType, "authenticated")
XCTAssertEqual(iamIdentity.cognitoIdentityAuthProvider, "authprovider")
default:
XCTAssertTrue(false, "an iam identity was expected, but didn't find one.")
}
}
}
extension AppSync.Event.ArgumentValue: Equatable {
public static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.string(let lhsString), .string(let rhsString)):
return lhsString == rhsString
case (.dictionary(let lhsDictionary), .dictionary(let rhsDictionary)):
return lhsDictionary == rhsDictionary
default:
return false
}
}
}
@@ -1,137 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class CloudwatchTests: XCTestCase {
static func eventBody(type: String, details: String) -> String {
"""
{
"id": "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c",
"detail-type": "\(type)",
"source": "aws.events",
"account": "123456789012",
"time": "1970-01-01T00:00:00Z",
"region": "us-east-1",
"resources": [
"arn:aws:events:us-east-1:123456789012:rule/ExampleRule"
],
"detail": \(details)
}
"""
}
func testScheduledEventFromJSON() {
let eventBody = CloudwatchTests.eventBody(type: Cloudwatch.Scheduled.name, details: "{}")
let data = eventBody.data(using: .utf8)!
var maybeEvent: Cloudwatch.ScheduledEvent?
XCTAssertNoThrow(maybeEvent = try JSONDecoder().decode(Cloudwatch.ScheduledEvent.self, from: data))
guard let event = maybeEvent else {
return XCTFail("Expected to have an event")
}
XCTAssertEqual(event.id, "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c")
XCTAssertEqual(event.source, "aws.events")
XCTAssertEqual(event.accountId, "123456789012")
XCTAssertEqual(event.time, Date(timeIntervalSince1970: 0))
XCTAssertEqual(event.region, .us_east_1)
XCTAssertEqual(event.resources, ["arn:aws:events:us-east-1:123456789012:rule/ExampleRule"])
}
func testEC2InstanceStateChangeNotificationEventFromJSON() {
let eventBody = CloudwatchTests.eventBody(type: Cloudwatch.EC2.InstanceStateChangeNotification.name,
details: "{ \"instance-id\": \"0\", \"state\": \"stopping\" }")
let data = eventBody.data(using: .utf8)!
var maybeEvent: Cloudwatch.EC2.InstanceStateChangeNotificationEvent?
XCTAssertNoThrow(maybeEvent = try JSONDecoder().decode(Cloudwatch.EC2.InstanceStateChangeNotificationEvent.self, from: data))
guard let event = maybeEvent else {
return XCTFail("Expected to have an event")
}
XCTAssertEqual(event.id, "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c")
XCTAssertEqual(event.source, "aws.events")
XCTAssertEqual(event.accountId, "123456789012")
XCTAssertEqual(event.time, Date(timeIntervalSince1970: 0))
XCTAssertEqual(event.region, .us_east_1)
XCTAssertEqual(event.resources, ["arn:aws:events:us-east-1:123456789012:rule/ExampleRule"])
XCTAssertEqual(event.detail.instanceId, "0")
XCTAssertEqual(event.detail.state, .stopping)
}
func testEC2SpotInstanceInterruptionNoticeEventFromJSON() {
let eventBody = CloudwatchTests.eventBody(type: Cloudwatch.EC2.SpotInstanceInterruptionNotice.name,
details: "{ \"instance-id\": \"0\", \"instance-action\": \"terminate\" }")
let data = eventBody.data(using: .utf8)!
var maybeEvent: Cloudwatch.EC2.SpotInstanceInterruptionNoticeEvent?
XCTAssertNoThrow(maybeEvent = try JSONDecoder().decode(Cloudwatch.EC2.SpotInstanceInterruptionNoticeEvent.self, from: data))
guard let event = maybeEvent else {
return XCTFail("Expected to have an event")
}
XCTAssertEqual(event.id, "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c")
XCTAssertEqual(event.source, "aws.events")
XCTAssertEqual(event.accountId, "123456789012")
XCTAssertEqual(event.time, Date(timeIntervalSince1970: 0))
XCTAssertEqual(event.region, .us_east_1)
XCTAssertEqual(event.resources, ["arn:aws:events:us-east-1:123456789012:rule/ExampleRule"])
XCTAssertEqual(event.detail.instanceId, "0")
XCTAssertEqual(event.detail.action, .terminate)
}
func testCustomEventFromJSON() {
struct Custom: CloudwatchDetail {
public static let name = "Custom"
let name: String
}
let eventBody = CloudwatchTests.eventBody(type: Custom.name, details: "{ \"name\": \"foo\" }")
let data = eventBody.data(using: .utf8)!
var maybeEvent: Cloudwatch.Event<Custom>?
XCTAssertNoThrow(maybeEvent = try JSONDecoder().decode(Cloudwatch.Event<Custom>.self, from: data))
guard let event = maybeEvent else {
return XCTFail("Expected to have an event")
}
XCTAssertEqual(event.id, "cdc73f9d-aea9-11e3-9d5a-835b769c0d9c")
XCTAssertEqual(event.source, "aws.events")
XCTAssertEqual(event.accountId, "123456789012")
XCTAssertEqual(event.time, Date(timeIntervalSince1970: 0))
XCTAssertEqual(event.region, .us_east_1)
XCTAssertEqual(event.resources, ["arn:aws:events:us-east-1:123456789012:rule/ExampleRule"])
XCTAssertEqual(event.detail.name, "foo")
}
func testUnregistredType() {
let eventBody = CloudwatchTests.eventBody(type: UUID().uuidString, details: "{}")
let data = eventBody.data(using: .utf8)!
XCTAssertThrowsError(try JSONDecoder().decode(Cloudwatch.ScheduledEvent.self, from: data)) { error in
XCTAssert(error is Cloudwatch.DetailTypeMismatch, "expected DetailTypeMismatch but received \(error)")
}
}
func testTypeMismatch() {
let eventBody = CloudwatchTests.eventBody(type: Cloudwatch.EC2.InstanceStateChangeNotification.name,
details: "{ \"instance-id\": \"0\", \"state\": \"stopping\" }")
let data = eventBody.data(using: .utf8)!
XCTAssertThrowsError(try JSONDecoder().decode(Cloudwatch.ScheduledEvent.self, from: data)) { error in
XCTAssert(error is Cloudwatch.DetailTypeMismatch, "expected DetailTypeMismatch but received \(error)")
}
}
}
@@ -1,232 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class DynamoDBTests: XCTestCase {
static let streamEventBody = """
{
"Records": [
{
"eventID": "1",
"eventVersion": "1.0",
"dynamodb": {
"ApproximateCreationDateTime": 1.578648338E9,
"Keys": {
"Id": {
"N": "101"
}
},
"NewImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"StreamViewType": "NEW_AND_OLD_IMAGES",
"SequenceNumber": "111",
"SizeBytes": 26
},
"awsRegion": "eu-central-1",
"eventName": "INSERT",
"eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
"eventSource": "aws:dynamodb"
},
{
"eventID": "2",
"eventVersion": "1.0",
"dynamodb": {
"ApproximateCreationDateTime": 1.578648338E9,
"OldImage": {
"Message": {
"S": "New item!"
},
"Id": {
"N": "101"
}
},
"SequenceNumber": "222",
"Keys": {
"Id": {
"N": "101"
}
},
"SizeBytes": 59,
"NewImage": {
"Message": {
"S": "This item has changed"
},
"Id": {
"N": "101"
}
},
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"awsRegion": "eu-central-1",
"eventName": "MODIFY",
"eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
"eventSource": "aws:dynamodb"
},
{
"eventID": "3",
"eventVersion": "1.0",
"dynamodb": {
"ApproximateCreationDateTime":1.578648338E9,
"Keys": {
"Id": {
"N": "101"
}
},
"SizeBytes": 38,
"SequenceNumber": "333",
"OldImage": {
"Message": {
"S": "This item has changed"
},
"Id": {
"N": "101"
}
},
"StreamViewType": "NEW_AND_OLD_IMAGES"
},
"awsRegion": "eu-central-1",
"eventName": "REMOVE",
"eventSourceARN": "arn:aws:dynamodb:eu-central-1:account-id:table/ExampleTableWithStream/stream/2015-06-27T00:48:05.899",
"eventSource": "aws:dynamodb"
}
]
}
"""
func testEventFromJSON() {
let data = DynamoDBTests.streamEventBody.data(using: .utf8)!
var event: DynamoDB.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(DynamoDB.Event.self, from: data))
XCTAssertEqual(event?.records.count, 3)
}
// MARK: - Parse Attribute Value Tests -
func testAttributeValueBoolDecoding() {
let json = "{\"BOOL\": true}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .boolean(true))
}
func testAttributeValueBinaryDecoding() {
let json = "{\"B\": \"YmFzZTY0\"}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .binary([UInt8]("base64".utf8)))
}
func testAttributeValueBinarySetDecoding() {
let json = "{\"BS\": [\"YmFzZTY0\", \"YWJjMTIz\"]}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .binarySet([[UInt8]("base64".utf8), [UInt8]("abc123".utf8)]))
}
func testAttributeValueStringDecoding() {
let json = "{\"S\": \"huhu\"}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .string("huhu"))
}
func testAttributeValueStringSetDecoding() {
let json = "{\"SS\": [\"huhu\", \"haha\"]}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .stringSet(["huhu", "haha"]))
}
func testAttributeValueNullDecoding() {
let json = "{\"NULL\": true}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .null)
}
func testAttributeValueNumberDecoding() {
let json = "{\"N\": \"1.2345\"}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .number("1.2345"))
}
func testAttributeValueNumberSetDecoding() {
let json = "{\"NS\": [\"1.2345\", \"-19\"]}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .numberSet(["1.2345", "-19"]))
}
func testAttributeValueListDecoding() {
let json = "{\"L\": [{\"NS\": [\"1.2345\", \"-19\"]}, {\"S\": \"huhu\"}]}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .list([.numberSet(["1.2345", "-19"]), .string("huhu")]))
}
func testAttributeValueMapDecoding() {
let json = "{\"M\": {\"numbers\": {\"NS\": [\"1.2345\", \"-19\"]}, \"string\": {\"S\": \"huhu\"}}}"
var value: DynamoDB.AttributeValue?
XCTAssertNoThrow(value = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!))
XCTAssertEqual(value, .map([
"numbers": .numberSet(["1.2345", "-19"]),
"string": .string("huhu"),
]))
}
func testAttributeValueEmptyDecoding() {
let json = "{\"haha\": 1}"
XCTAssertThrowsError(_ = try JSONDecoder().decode(DynamoDB.AttributeValue.self, from: json.data(using: .utf8)!)) { error in
guard case DecodingError.dataCorrupted = error else {
XCTFail("Unexpected error: \(String(describing: error))")
return
}
}
}
func testAttributeValueEquatable() {
XCTAssertEqual(DynamoDB.AttributeValue.boolean(true), .boolean(true))
XCTAssertNotEqual(DynamoDB.AttributeValue.boolean(true), .boolean(false))
XCTAssertNotEqual(DynamoDB.AttributeValue.boolean(true), .string("haha"))
}
// MARK: - DynamoDB Decoder Tests -
func testDecoderSimple() {
let value: [String: DynamoDB.AttributeValue] = [
"foo": .string("bar"),
"xyz": .number("123"),
]
struct Test: Codable {
let foo: String
let xyz: UInt8
}
var test: Test?
XCTAssertNoThrow(test = try DynamoDB.Decoder().decode(Test.self, from: value))
XCTAssertEqual(test?.foo, "bar")
XCTAssertEqual(test?.xyz, 123)
}
}
-158
View File
@@ -1,158 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class S3Tests: XCTestCase {
static let eventBodyObjectCreated = """
{
"Records": [
{
"eventVersion":"2.1",
"eventSource":"aws:s3",
"awsRegion":"eu-central-1",
"eventTime":"2020-01-13T09:25:40.621Z",
"eventName":"ObjectCreated:Put",
"userIdentity":{
"principalId":"AWS:AAAAAAAJ2MQ4YFQZ7AULJ"
},
"requestParameters":{
"sourceIPAddress":"123.123.123.123"
},
"responseElements":{
"x-amz-request-id":"01AFA1430E18C358",
"x-amz-id-2":"JsbNw6sHGFwgzguQjbYcew//bfAeZITyTYLfjuu1U4QYqCq5CPlSyYLtvWQS+gw0RxcroItGwm8="
},
"s3":{
"s3SchemaVersion":"1.0",
"configurationId":"98b55bc4-3c0c-4007-b727-c6b77a259dde",
"bucket":{
"name":"eventsources",
"ownerIdentity":{
"principalId":"AAAAAAAAAAAAAA"
},
"arn":"arn:aws:s3:::eventsources"
},
"object":{
"key":"Hi.md",
"size":2880,
"eTag":"91a7f2c3ae81bcc6afef83979b463f0e",
"sequencer":"005E1C37948E783A6E"
}
}
}
]
}
"""
// A S3 ObjectRemoved:* event does not contain the object size
static let eventBodyObjectRemoved = """
{
"Records": [
{
"eventVersion":"2.1",
"eventSource":"aws:s3",
"awsRegion":"eu-central-1",
"eventTime":"2020-01-13T09:25:40.621Z",
"eventName":"ObjectRemoved:DeleteMarkerCreated",
"userIdentity":{
"principalId":"AWS:AAAAAAAJ2MQ4YFQZ7AULJ"
},
"requestParameters":{
"sourceIPAddress":"123.123.123.123"
},
"responseElements":{
"x-amz-request-id":"01AFA1430E18C358",
"x-amz-id-2":"JsbNw6sHGFwgzguQjbYcew//bfAeZITyTYLfjuu1U4QYqCq5CPlSyYLtvWQS+gw0RxcroItGwm8="
},
"s3":{
"s3SchemaVersion":"1.0",
"configurationId":"98b55bc4-3c0c-4007-b727-c6b77a259dde",
"bucket":{
"name":"eventsources",
"ownerIdentity":{
"principalId":"AAAAAAAAAAAAAA"
},
"arn":"arn:aws:s3:::eventsources"
},
"object":{
"key":"Hi.md",
"eTag":"91a7f2c3ae81bcc6afef83979b463f0e",
"sequencer":"005E1C37948E783A6E"
}
}
}
]
}
"""
func testObjectCreatedEvent() {
let data = S3Tests.eventBodyObjectCreated.data(using: .utf8)!
var event: S3.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(S3.Event.self, from: data))
guard let record = event?.records.first else {
XCTFail("Expected to have one record")
return
}
XCTAssertEqual(record.eventVersion, "2.1")
XCTAssertEqual(record.eventSource, "aws:s3")
XCTAssertEqual(record.awsRegion, .eu_central_1)
XCTAssertEqual(record.eventName, "ObjectCreated:Put")
XCTAssertEqual(record.eventTime, Date(timeIntervalSince1970: 1_578_907_540.621))
XCTAssertEqual(record.userIdentity, S3.UserIdentity(principalId: "AWS:AAAAAAAJ2MQ4YFQZ7AULJ"))
XCTAssertEqual(record.requestParameters, S3.RequestParameters(sourceIPAddress: "123.123.123.123"))
XCTAssertEqual(record.responseElements.count, 2)
XCTAssertEqual(record.s3.schemaVersion, "1.0")
XCTAssertEqual(record.s3.configurationId, "98b55bc4-3c0c-4007-b727-c6b77a259dde")
XCTAssertEqual(record.s3.bucket.name, "eventsources")
XCTAssertEqual(record.s3.bucket.ownerIdentity, S3.UserIdentity(principalId: "AAAAAAAAAAAAAA"))
XCTAssertEqual(record.s3.bucket.arn, "arn:aws:s3:::eventsources")
XCTAssertEqual(record.s3.object.key, "Hi.md")
XCTAssertEqual(record.s3.object.size, 2880)
XCTAssertEqual(record.s3.object.eTag, "91a7f2c3ae81bcc6afef83979b463f0e")
XCTAssertEqual(record.s3.object.sequencer, "005E1C37948E783A6E")
}
func testObjectRemovedEvent() {
let data = S3Tests.eventBodyObjectRemoved.data(using: .utf8)!
var event: S3.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(S3.Event.self, from: data))
guard let record = event?.records.first else {
XCTFail("Expected to have one record")
return
}
XCTAssertEqual(record.eventVersion, "2.1")
XCTAssertEqual(record.eventSource, "aws:s3")
XCTAssertEqual(record.awsRegion, .eu_central_1)
XCTAssertEqual(record.eventName, "ObjectRemoved:DeleteMarkerCreated")
XCTAssertEqual(record.eventTime, Date(timeIntervalSince1970: 1_578_907_540.621))
XCTAssertEqual(record.userIdentity, S3.UserIdentity(principalId: "AWS:AAAAAAAJ2MQ4YFQZ7AULJ"))
XCTAssertEqual(record.requestParameters, S3.RequestParameters(sourceIPAddress: "123.123.123.123"))
XCTAssertEqual(record.responseElements.count, 2)
XCTAssertEqual(record.s3.schemaVersion, "1.0")
XCTAssertEqual(record.s3.configurationId, "98b55bc4-3c0c-4007-b727-c6b77a259dde")
XCTAssertEqual(record.s3.bucket.name, "eventsources")
XCTAssertEqual(record.s3.bucket.ownerIdentity, S3.UserIdentity(principalId: "AAAAAAAAAAAAAA"))
XCTAssertEqual(record.s3.bucket.arn, "arn:aws:s3:::eventsources")
XCTAssertEqual(record.s3.object.key, "Hi.md")
XCTAssertNil(record.s3.object.size)
XCTAssertEqual(record.s3.object.eTag, "91a7f2c3ae81bcc6afef83979b463f0e")
XCTAssertEqual(record.s3.object.sequencer, "005E1C37948E783A6E")
}
}
-128
View File
@@ -1,128 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class SESTests: XCTestCase {
static let eventBody = """
{
"Records": [
{
"eventSource": "aws:ses",
"eventVersion": "1.0",
"ses": {
"mail": {
"commonHeaders": {
"date": "Wed, 7 Oct 2015 12:34:56 -0700",
"from": [
"Jane Doe <janedoe@example.com>"
],
"messageId": "<0123456789example.com>",
"returnPath": "janedoe@example.com",
"subject": "Test Subject",
"to": [
"johndoe@example.com"
]
},
"destination": [
"johndoe@example.com"
],
"headers": [
{
"name": "Return-Path",
"value": "<janedoe@example.com>"
},
{
"name": "Received",
"value": "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.eu-west-1.amazonaws.com with SMTP id o3vrnil0e2ic28trm7dfhrc2v0cnbeccl4nbp0g1 for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)"
}
],
"headersTruncated": true,
"messageId": "5h5auqp1oa1bg49b2q8f8tmli1oju8pcma2haao1",
"source": "janedoe@example.com",
"timestamp": "1970-01-01T00:00:00.000Z"
},
"receipt": {
"action": {
"functionArn": "arn:aws:lambda:eu-west-1:123456789012:function:Example",
"invocationType": "Event",
"type": "Lambda"
},
"dkimVerdict": {
"status": "PASS"
},
"processingTimeMillis": 574,
"recipients": [
"test@swift-server.com",
"test2@swift-server.com"
],
"spamVerdict": {
"status": "PASS"
},
"spfVerdict": {
"status": "PROCESSING_FAILED"
},
"timestamp": "1970-01-01T00:00:00.000Z",
"virusVerdict": {
"status": "FAIL"
}
}
}
}
]
}
"""
func testSimpleEventFromJSON() {
let data = Data(SESTests.eventBody.utf8)
var event: SES.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(SES.Event.self, from: data))
guard let record = event?.records.first else {
XCTFail("Expected to have one record")
return
}
XCTAssertEqual(record.eventSource, "aws:ses")
XCTAssertEqual(record.eventVersion, "1.0")
XCTAssertEqual(record.ses.mail.commonHeaders.date.description, "2015-10-07 19:34:56 +0000")
XCTAssertEqual(record.ses.mail.commonHeaders.from[0], "Jane Doe <janedoe@example.com>")
XCTAssertEqual(record.ses.mail.commonHeaders.messageId, "<0123456789example.com>")
XCTAssertEqual(record.ses.mail.commonHeaders.returnPath, "janedoe@example.com")
XCTAssertEqual(record.ses.mail.commonHeaders.subject, "Test Subject")
XCTAssertEqual(record.ses.mail.commonHeaders.to?[0], "johndoe@example.com")
XCTAssertEqual(record.ses.mail.destination[0], "johndoe@example.com")
XCTAssertEqual(record.ses.mail.headers[0].name, "Return-Path")
XCTAssertEqual(record.ses.mail.headers[0].value, "<janedoe@example.com>")
XCTAssertEqual(record.ses.mail.headers[1].name, "Received")
XCTAssertEqual(record.ses.mail.headers[1].value, "from mailer.example.com (mailer.example.com [203.0.113.1]) by inbound-smtp.eu-west-1.amazonaws.com with SMTP id o3vrnil0e2ic28trm7dfhrc2v0cnbeccl4nbp0g1 for johndoe@example.com; Wed, 07 Oct 2015 12:34:56 +0000 (UTC)")
XCTAssertEqual(record.ses.mail.headersTruncated, true)
XCTAssertEqual(record.ses.mail.messageId, "5h5auqp1oa1bg49b2q8f8tmli1oju8pcma2haao1")
XCTAssertEqual(record.ses.mail.source, "janedoe@example.com")
XCTAssertEqual(record.ses.mail.timestamp.description, "1970-01-01 00:00:00 +0000")
XCTAssertEqual(record.ses.receipt.action.functionArn, "arn:aws:lambda:eu-west-1:123456789012:function:Example")
XCTAssertEqual(record.ses.receipt.action.invocationType, "Event")
XCTAssertEqual(record.ses.receipt.action.type, "Lambda")
XCTAssertEqual(record.ses.receipt.dkimVerdict.status, .pass)
XCTAssertEqual(record.ses.receipt.processingTimeMillis, 574)
XCTAssertEqual(record.ses.receipt.recipients[0], "test@swift-server.com")
XCTAssertEqual(record.ses.receipt.recipients[1], "test2@swift-server.com")
XCTAssertEqual(record.ses.receipt.spamVerdict.status, .pass)
XCTAssertEqual(record.ses.receipt.spfVerdict.status, .processingFailed)
XCTAssertEqual(record.ses.receipt.timestamp.description, "1970-01-01 00:00:00 +0000")
XCTAssertEqual(record.ses.receipt.virusVerdict.status, .fail)
}
}
-82
View File
@@ -1,82 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class SNSTests: XCTestCase {
static let eventBody = """
{
"Records": [
{
"EventSource": "aws:sns",
"EventVersion": "1.0",
"EventSubscriptionArn": "arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5:6fabdb7f-b27e-456d-8e8a-14679db9e40c",
"Sns": {
"Type": "Notification",
"MessageId": "bdb6900e-1ae9-5b4b-b7fc-c681fde222e3",
"TopicArn": "arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5",
"Subject": null,
"Message": "{\\\"hello\\\": \\\"world\\\"}",
"Timestamp": "2020-01-08T14:18:51.203Z",
"SignatureVersion": "1",
"Signature": "LJMF/xmMH7A1gNy2unLA3hmzyf6Be+zS/Yeiiz9tZbu6OG8fwvWZeNOcEZardhSiIStc0TF7h9I+4Qz3omCntaEfayzTGmWN8itGkn2mfn/hMFmPbGM8gEUz3+jp1n6p+iqP3XTx92R0LBIFrU3ylOxSo8+SCOjA015M93wfZzwj0WPtynji9iAvvtf15d8JxPUu1T05BRitpFd5s6ZXDHtVQ4x/mUoLUN8lOVp+rs281/ZdYNUG/V5CwlyUDTOERdryTkBJ/GO1NNPa+6m04ywJFa5d+BC8mDcUcHhhXXjpTEbt8AHBmswK3nudHrVMRO/G4zmssxU2P7ii5+gCfA==",
"SigningCertUrl": "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-6aad65c2f9911b05cd53efda11f913f9.pem",
"UnsubscribeUrl": "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5:6fabdb7f-b27e-456d-8e8a-14679db9e40c",
"MessageAttributes": {
"binary":{
"Type": "Binary",
"Value": "YmFzZTY0"
},
"string":{
"Type": "String",
"Value": "abc123"
}
}
}
}
]
}
"""
func testSimpleEventFromJSON() {
let data = SNSTests.eventBody.data(using: .utf8)!
var event: SNS.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(SNS.Event.self, from: data))
guard let record = event?.records.first else {
XCTFail("Expected to have one record")
return
}
XCTAssertEqual(record.eventSource, "aws:sns")
XCTAssertEqual(record.eventVersion, "1.0")
XCTAssertEqual(record.eventSubscriptionArn, "arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5:6fabdb7f-b27e-456d-8e8a-14679db9e40c")
XCTAssertEqual(record.sns.type, "Notification")
XCTAssertEqual(record.sns.messageId, "bdb6900e-1ae9-5b4b-b7fc-c681fde222e3")
XCTAssertEqual(record.sns.topicArn, "arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5")
XCTAssertEqual(record.sns.message, "{\"hello\": \"world\"}")
XCTAssertEqual(record.sns.timestamp, Date(timeIntervalSince1970: 1_578_493_131.203))
XCTAssertEqual(record.sns.signatureVersion, "1")
XCTAssertEqual(record.sns.signature, "LJMF/xmMH7A1gNy2unLA3hmzyf6Be+zS/Yeiiz9tZbu6OG8fwvWZeNOcEZardhSiIStc0TF7h9I+4Qz3omCntaEfayzTGmWN8itGkn2mfn/hMFmPbGM8gEUz3+jp1n6p+iqP3XTx92R0LBIFrU3ylOxSo8+SCOjA015M93wfZzwj0WPtynji9iAvvtf15d8JxPUu1T05BRitpFd5s6ZXDHtVQ4x/mUoLUN8lOVp+rs281/ZdYNUG/V5CwlyUDTOERdryTkBJ/GO1NNPa+6m04ywJFa5d+BC8mDcUcHhhXXjpTEbt8AHBmswK3nudHrVMRO/G4zmssxU2P7ii5+gCfA==")
XCTAssertEqual(record.sns.signingCertURL, "https://sns.eu-central-1.amazonaws.com/SimpleNotificationService-6aad65c2f9911b05cd53efda11f913f9.pem")
XCTAssertEqual(record.sns.unsubscribeUrl, "https://sns.eu-central-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-central-1:079477498937:EventSources-SNSTopic-1NHENSE2MQKF5:6fabdb7f-b27e-456d-8e8a-14679db9e40c")
XCTAssertEqual(record.sns.messageAttributes?.count, 2)
XCTAssertEqual(record.sns.messageAttributes?["binary"], .binary([UInt8]("base64".utf8)))
XCTAssertEqual(record.sns.messageAttributes?["string"], .string("abc123"))
}
}
-87
View File
@@ -1,87 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class SQSTests: XCTestCase {
static let eventBody = """
{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "Hello from SQS!",
"attributes": {
"ApproximateReceiveCount": "1",
"SentTimestamp": "1523232000000",
"SenderId": "123456789012",
"ApproximateFirstReceiveTimestamp": "1523232000001"
},
"messageAttributes": {
"number":{
"stringValue":"123",
"stringListValues":[],
"binaryListValues":[],
"dataType":"Number"
},
"string":{
"stringValue":"abc123",
"stringListValues":[],
"binaryListValues":[],
"dataType":"String"
},
"binary":{
"dataType": "Binary",
"stringListValues":[],
"binaryListValues":[],
"binaryValue":"YmFzZTY0"
},
},
"md5OfBody": "7b270e59b47ff90a553787216d55d91d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:us-east-1:123456789012:MyQueue",
"awsRegion": "us-east-1"
}
]
}
"""
func testSimpleEventFromJSON() {
let data = SQSTests.eventBody.data(using: .utf8)!
var event: SQS.Event?
XCTAssertNoThrow(event = try JSONDecoder().decode(SQS.Event.self, from: data))
guard let message = event?.records.first else {
XCTFail("Expected to have one message in the event")
return
}
XCTAssertEqual(message.messageId, "19dd0b57-b21e-4ac1-bd88-01bbb068cb78")
XCTAssertEqual(message.receiptHandle, "MessageReceiptHandle")
XCTAssertEqual(message.body, "Hello from SQS!")
XCTAssertEqual(message.attributes.count, 4)
XCTAssertEqual(message.messageAttributes, [
"number": .number("123"),
"string": .string("abc123"),
"binary": .binary([UInt8]("base64".utf8)),
])
XCTAssertEqual(message.md5OfBody, "7b270e59b47ff90a553787216d55d91d")
XCTAssertEqual(message.eventSource, "aws:sqs")
XCTAssertEqual(message.eventSourceArn, "arn:aws:sqs:us-east-1:123456789012:MyQueue")
XCTAssertEqual(message.awsRegion, .us_east_1)
}
}
@@ -1,72 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class Base64Tests: XCTestCase {
// MARK: - Decoding -
func testDecodeEmptyString() throws {
var decoded: [UInt8]?
XCTAssertNoThrow(decoded = try "".base64decoded())
XCTAssertEqual(decoded?.count, 0)
}
func testBase64DecodingArrayOfNulls() throws {
let expected = Array(repeating: UInt8(0), count: 10)
var decoded: [UInt8]?
XCTAssertNoThrow(decoded = try "AAAAAAAAAAAAAA==".base64decoded())
XCTAssertEqual(decoded, expected)
}
func testBase64DecodingAllTheBytesSequentially() {
let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
let expected = Array(UInt8(0) ... UInt8(255))
var decoded: [UInt8]?
XCTAssertNoThrow(decoded = try base64.base64decoded())
XCTAssertEqual(decoded, expected)
}
func testBase64UrlDecodingAllTheBytesSequentially() {
let base64 = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w=="
let expected = Array(UInt8(0) ... UInt8(255))
var decoded: [UInt8]?
XCTAssertNoThrow(decoded = try base64.base64decoded(options: .base64UrlAlphabet))
XCTAssertEqual(decoded, expected)
}
func testBase64DecodingWithPoop() {
XCTAssertThrowsError(_ = try "💩".base64decoded()) { error in
XCTAssertEqual(error as? Base64.DecodingError, .invalidCharacter(240))
}
}
func testBase64DecodingWithInvalidLength() {
XCTAssertThrowsError(_ = try "AAAAA".base64decoded()) { error in
XCTAssertEqual(error as? Base64.DecodingError, .invalidLength)
}
}
func testNSStringToDecode() {
let test = "1234567"
let nsstring = test.data(using: .utf8)!.base64EncodedString()
XCTAssertNoThrow(try nsstring.base64decoded())
}
}
@@ -1,140 +0,0 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftAWSLambdaRuntime open source project
//
// Copyright (c) 2017-2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
@testable import AWSLambdaEvents
import XCTest
class DateWrapperTests: XCTestCase {
func testISO8601CodingWrapperSuccess() {
struct TestEvent: Decodable {
@ISO8601Coding
var date: Date
}
let json = #"{"date":"2020-03-26T16:53:05Z"}"#
var event: TestEvent?
XCTAssertNoThrow(event = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!))
XCTAssertEqual(event?.date, Date(timeIntervalSince1970: 1_585_241_585))
}
func testISO8601CodingWrapperFailure() {
struct TestEvent: Decodable {
@ISO8601Coding
var date: Date
}
let date = "2020-03-26T16:53:05" // missing Z at end
let json = #"{"date":"\#(date)"}"#
XCTAssertThrowsError(_ = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!)) { error in
guard case DecodingError.dataCorrupted(let context) = error else {
XCTFail("Unexpected error: \(error)"); return
}
XCTAssertEqual(context.codingPath.map(\.stringValue), ["date"])
XCTAssertEqual(context.debugDescription, "Expected date to be in ISO8601 date format, but `\(date)` is not in the correct format")
XCTAssertNil(context.underlyingError)
}
}
func testISO8601WithFractionalSecondsCodingWrapperSuccess() {
struct TestEvent: Decodable {
@ISO8601WithFractionalSecondsCoding
var date: Date
}
let json = #"{"date":"2020-03-26T16:53:05.123Z"}"#
var event: TestEvent?
XCTAssertNoThrow(event = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!))
XCTAssertEqual(event?.date, Date(timeIntervalSince1970: 1_585_241_585.123))
}
func testISO8601WithFractionalSecondsCodingWrapperFailure() {
struct TestEvent: Decodable {
@ISO8601WithFractionalSecondsCoding
var date: Date
}
let date = "2020-03-26T16:53:05Z" // missing fractional seconds
let json = #"{"date":"\#(date)"}"#
XCTAssertThrowsError(_ = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!)) { error in
guard case DecodingError.dataCorrupted(let context) = error else {
XCTFail("Unexpected error: \(error)"); return
}
XCTAssertEqual(context.codingPath.map(\.stringValue), ["date"])
XCTAssertEqual(context.debugDescription, "Expected date to be in ISO8601 date format with fractional seconds, but `\(date)` is not in the correct format")
XCTAssertNil(context.underlyingError)
}
}
func testRFC5322DateTimeCodingWrapperSuccess() {
struct TestEvent: Decodable {
@RFC5322DateTimeCoding
var date: Date
}
let json = #"{"date":"Thu, 5 Apr 2012 23:47:37 +0200"}"#
var event: TestEvent?
XCTAssertNoThrow(event = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!))
XCTAssertEqual(event?.date.description, "2012-04-05 21:47:37 +0000")
}
func testRFC5322DateTimeCodingWrapperWithExtraTimeZoneSuccess() {
struct TestEvent: Decodable {
@RFC5322DateTimeCoding
var date: Date
}
let json = #"{"date":"Fri, 26 Jun 2020 03:04:03 -0500 (CDT)"}"#
var event: TestEvent?
XCTAssertNoThrow(event = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!))
XCTAssertEqual(event?.date.description, "2020-06-26 08:04:03 +0000")
}
func testRFC5322DateTimeCodingWrapperWithAlphabeticTimeZoneSuccess() {
struct TestEvent: Decodable {
@RFC5322DateTimeCoding
var date: Date
}
let json = #"{"date":"Fri, 26 Jun 2020 03:04:03 CDT"}"#
var event: TestEvent?
XCTAssertNoThrow(event = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!))
XCTAssertEqual(event?.date.description, "2020-06-26 08:04:03 +0000")
}
func testRFC5322DateTimeCodingWrapperFailure() {
struct TestEvent: Decodable {
@RFC5322DateTimeCoding
var date: Date
}
let date = "Thu, 5 Apr 2012 23:47 +0200" // missing seconds
let json = #"{"date":"\#(date)"}"#
XCTAssertThrowsError(_ = try JSONDecoder().decode(TestEvent.self, from: json.data(using: .utf8)!)) { error in
guard case DecodingError.dataCorrupted(let context) = error else {
XCTFail("Unexpected error: \(error)"); return
}
XCTAssertEqual(context.codingPath.map(\.stringValue), ["date"])
XCTAssertEqual(context.debugDescription, "Expected date to be in RFC5322 date-time format with fractional seconds, but `\(date)` is not in the correct format")
XCTAssertNil(context.underlyingError)
}
}
}
+38 -22
View File
@@ -10,17 +10,6 @@ Combine this with Swift's developer friendliness, expressiveness, and emphasis o
Swift AWS Lambda Runtime was designed to make building Lambda functions in Swift simple and safe. The library is an implementation of the [AWS Lambda Runtime API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-custom.html) and uses an embedded asynchronous HTTP Client based on [SwiftNIO](http://github.com/apple/swift-nio) that is fine-tuned for performance in the AWS Runtime context. The library provides a multi-tier API that allows building a range of Lambda functions: From quick and simple closures to complex, performance-sensitive event handlers.
## Project status
This is the beginning of a community-driven open-source project actively seeking contributions.
While the core API is considered stable, the API may still evolve as we get closer to a `1.0` version.
There are several areas which need additional attention, including but not limited to:
* Further performance tuning
* Additional trigger events
* Additional documentation and best practices
* Additional examples
## Getting started
If you have never used AWS Lambda or Docker before, check out this [getting started guide](https://fabianfett.de/getting-started-with-swift-aws-lambda-runtime) which helps you with every step from zero to a running Lambda.
@@ -86,7 +75,33 @@ Next, create a `main.swift` and implement your Lambda.
}
```
Since most Lambda functions are triggered by events originating in the AWS platform like `SNS`, `SQS` or `APIGateway`, the package also includes a `AWSLambdaEvents` module that provides implementations for most common AWS event types further simplifying writing Lambda functions. For example, handling an `SQS` message:
Since most Lambda functions are triggered by events originating in the AWS platform like `SNS`, `SQS` or `APIGateway`, the [Swift AWS Lambda Events](http://github.com/swift-server/swift-aws-lambda-events) package includes a `AWSLambdaEvents` module that provides implementations for most common AWS event types further simplifying writing Lambda functions. For example, handling an `SQS` message:
First, add a dependency on the event packages:
```swift
// swift-tools-version:5.2
import PackageDescription
let package = Package(
name: "my-lambda",
products: [
.executable(name: "MyLambda", targets: ["MyLambda"]),
],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "0.1.0"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-events.git", from: "0.1.0"),
],
targets: [
.target(name: "MyLambda", dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
]),
]
)
```
```swift
// Import the modules
@@ -333,16 +348,7 @@ By default, the library also registers a Signal handler that traps `INT` and `TE
### Integration with AWS Platform Events
AWS Lambda functions can be invoked directly from the AWS Lambda console UI, AWS Lambda API, AWS SDKs, AWS CLI, and AWS toolkits. More commonly, they are invoked as a reaction to an events coming from the AWS platform. To make it easier to integrate with AWS platform events, the library includes an `AWSLambdaEvents` target which provides abstractions for many commonly used events. Additional events can be easily modeled when needed following the same patterns set by `AWSLambdaEvents`. Integration points with the AWS Platform include:
* [APIGateway Proxy](https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html)
* [S3 Events](https://docs.aws.amazon.com/lambda/latest/dg/with-s3.html)
* [SES Events](https://docs.aws.amazon.com/lambda/latest/dg/services-ses.html)
* [SNS Events](https://docs.aws.amazon.com/lambda/latest/dg/with-sns.html)
* [SQS Events](https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html)
* [CloudWatch Events](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents.html)
**Note**: Each one of the integration points mentioned above includes a set of `Codable` structs that mirror AWS' data model for these APIs.
AWS Lambda functions can be invoked directly from the AWS Lambda console UI, AWS Lambda API, AWS SDKs, AWS CLI, and AWS toolkits. More commonly, they are invoked as a reaction to an events coming from the AWS platform. To make it easier to integrate with AWS platform events, [Swift AWS Lambda Runtime Events](http://github.com/swift-server/swift-aws-lambda-events) library is available, designed to work together with this runtime library. [Swift AWS Lambda Runtime Events](http://github.com/swift-server/swift-aws-lambda-events) includes an `AWSLambdaEvents` target which provides abstractions for many commonly used events.
## Performance
@@ -359,3 +365,13 @@ Swift provides great Unicode support via [ICU](http://site.icu-project.org/home)
## Security
Please see [SECURITY.md](SECURITY.md) for details on the security process.
## Project status
This is a community-driven open-source project actively seeking contributions.
While the core API is considered stable, the API may still evolve as we get closer to a `1.0` version.
There are several areas which need additional attention, including but not limited to:
* Further performance tuning
* Additional documentation and best practices
* Additional examples