From cba25734d506f6832f2ca009edaa0a8dbafef35a Mon Sep 17 00:00:00 2001 From: Matt <85322+mattmassicotte@users.noreply.github.com> Date: Fri, 6 Jan 2023 07:04:31 -0500 Subject: [PATCH 1/2] Initial cognito trigger support --- Sources/AWSLambdaEvents/Cognito.swift | 188 ++++++++++++++++++ Sources/AWSLambdaEvents/Docs.docc/index.md | 2 + Tests/AWSLambdaEventsTests/CognitoTests.swift | 92 +++++++++ readme.md | 1 + 4 files changed, 283 insertions(+) create mode 100644 Sources/AWSLambdaEvents/Cognito.swift create mode 100644 Tests/AWSLambdaEventsTests/CognitoTests.swift diff --git a/Sources/AWSLambdaEvents/Cognito.swift b/Sources/AWSLambdaEvents/Cognito.swift new file mode 100644 index 0000000..21b0b33 --- /dev/null +++ b/Sources/AWSLambdaEvents/Cognito.swift @@ -0,0 +1,188 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright (c) 2017-2022 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 +// +//===----------------------------------------------------------------------===// + +enum CognitoEventError: Error { + case unimplementedEvent(String) +} + +/// https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html +public enum CognitoEvent: Equatable { + public struct CallerContext: Codable, Hashable { + let awsSdkVersion: String + let clientId: String + } + + public struct Parameters: Codable, Equatable { + let version: String + let triggerSource: String + let region: AWSRegion + let userPoolId: String + let userName: String + let callerContext: CallerContext + } + + case preSignUpSignUp(Parameters, PreSignUp) + + public struct PreSignUp: Codable, Hashable { + /// One or more name-value pairs representing user attributes. The attribute names are the keys. + public let userAttributes: [String: String] + /// One or more name-value pairs containing the validation data in the request to register a user. + /// + /// The validation data is set and then passed from the client in the request to register a user. You can pass this data to your Lambda function by using the ClientMetadata parameter in the InitiateAuth and AdminInitiateAuth API actions. + public let validationData: [String: String]? + /// One or more key-value pairs that you can provide as custom input to the Lambda function that you specify for the pre sign-up trigger. + /// + /// You can pass this data to your Lambda function by using the ClientMetadata parameter in the following API actions: AdminCreateUser, AdminRespondToAuthChallenge, ForgotPassword, and SignUp. + public let clientMetadata: [String: String]? + } + + public var commonParameters: Parameters { + switch self { + case .preSignUpSignUp(let params, _): + return params + } + } +} + +extension CognitoEvent: Codable { + public enum CodingKeys: String, CodingKey { + case version + case triggerSource + case region + case userPoolId + case userName + case callerContext + case request + case response + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let version = try container.decode(String.self, forKey: .version) + let triggerSource = try container.decode(String.self, forKey: .triggerSource) + let region = try container.decode(AWSRegion.self, forKey: .region) + let userPoolId = try container.decode(String.self, forKey: .userPoolId) + let userName = try container.decode(String.self, forKey: .userName) + let callerContext = try container.decode(CallerContext.self, forKey: .callerContext) + + let params = CognitoEvent.Parameters(version: version, triggerSource: triggerSource, region: region, userPoolId: userPoolId, userName: userName, callerContext: callerContext) + + switch triggerSource { + case "PreSignUp_SignUp": + let value = try container.decode(CognitoEvent.PreSignUp.self, forKey: .request) + + self = .preSignUpSignUp(params, value) + default: + throw CognitoEventError.unimplementedEvent(triggerSource) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + let params = commonParameters + + try container.encode(params.version, forKey: .version) + try container.encode(params.triggerSource, forKey: .triggerSource) + try container.encode(params.region, forKey: .region) + try container.encode(params.userPoolId, forKey: .userPoolId) + try container.encode(params.userName, forKey: .userName) + try container.encode(params.callerContext, forKey: .callerContext) + + switch self { + case .preSignUpSignUp(_, let value): + try container.encode(value, forKey: .response) + } + } +} + +public enum CognitoEventResponse { + case preSignUpSignUp(CognitoEvent.Parameters, CognitoEvent.PreSignUp, PreSignUp) + + public struct PreSignUp: Codable, Hashable { + public let autoConfirmUser: Bool + public let autoVerifyPhone: Bool + public let autoVerifyEmail: Bool + + public init(autoConfirmUser: Bool, autoVerifyPhone: Bool, autoVerifyEmail: Bool) { + self.autoConfirmUser = autoConfirmUser + self.autoVerifyPhone = autoVerifyPhone + self.autoVerifyEmail = autoVerifyEmail + } + } + + public var commonParameters: CognitoEvent.Parameters { + switch self { + case .preSignUpSignUp(let params, _, _): + return params + } + } +} + + +extension CognitoEventResponse: Codable { + public enum CodingKeys: String, CodingKey { + case version + case triggerSource + case region + case userPoolId + case userName + case callerContext + case request + case response + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + let version = try container.decode(String.self, forKey: .version) + let triggerSource = try container.decode(String.self, forKey: .triggerSource) + let region = try container.decode(AWSRegion.self, forKey: .region) + let userPoolId = try container.decode(String.self, forKey: .userPoolId) + let userName = try container.decode(String.self, forKey: .userName) + let callerContext = try container.decode(CognitoEvent.CallerContext.self, forKey: .callerContext) + + let params = CognitoEvent.Parameters(version: version, triggerSource: triggerSource, region: region, userPoolId: userPoolId, userName: userName, callerContext: callerContext) + + switch triggerSource { + case "PreSignUp_SignUp": + let request = try container.decode(CognitoEvent.PreSignUp.self, forKey: .request) + let response = try container.decode(CognitoEventResponse.PreSignUp.self, forKey: .response) + + self = .preSignUpSignUp(params, request, response) + default: + throw CognitoEventError.unimplementedEvent(triggerSource) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + let params = commonParameters + + try container.encode(params.version, forKey: .version) + try container.encode(params.triggerSource, forKey: .triggerSource) + try container.encode(params.region, forKey: .region) + try container.encode(params.userPoolId, forKey: .userPoolId) + try container.encode(params.userName, forKey: .userName) + try container.encode(params.callerContext, forKey: .callerContext) + + switch self { + case .preSignUpSignUp(_, let request, let response): + try container.encode(request, forKey: .request) + try container.encode(response, forKey: .response) + } + } +} diff --git a/Sources/AWSLambdaEvents/Docs.docc/index.md b/Sources/AWSLambdaEvents/Docs.docc/index.md index 637fc55..afdacc3 100644 --- a/Sources/AWSLambdaEvents/Docs.docc/index.md +++ b/Sources/AWSLambdaEvents/Docs.docc/index.md @@ -18,6 +18,7 @@ AWS Lambda functions can be invoked directly from the AWS Lambda console UI, AWS * [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) +* [Cognito Lambda Triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html) **Note**: Each one of the integration points mentioned above includes a set of `Codable` structs that mirror AWS' data model for these APIs. @@ -38,3 +39,4 @@ Swift AWS Lambda Events is a supporting library for the [Swift AWS Lambda Runtim - ``SESEvent`` - ``SNSEvent`` - ``SQSEvent`` +- ``CognitoEvent`` diff --git a/Tests/AWSLambdaEventsTests/CognitoTests.swift b/Tests/AWSLambdaEventsTests/CognitoTests.swift new file mode 100644 index 0000000..32636f2 --- /dev/null +++ b/Tests/AWSLambdaEventsTests/CognitoTests.swift @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +final class CognitoTests: XCTestCase { + func testPreSignUpRequest() throws { + let json = """ +{ + "version": "1", + "triggerSource": "PreSignUp_SignUp", + "region": "us-east-1", + "userPoolId": "abc", + "userName": "blob", + "callerContext": { + "awsSdkVersion": "1", + "clientId": "abc", + }, + "request": { + "userAttributes": { + "string": "string" + }, + "validationData": { + "string": "string" + }, + "clientMetadata": { + "string": "string" + } + }, + + "response": {} +} +""" + let event = try JSONDecoder().decode(CognitoEvent.self, from: json.data(using: .utf8)!) + + guard case .preSignUpSignUp(let params, let request) = event else { + XCTFail() + return + } + + XCTAssertEqual(params.triggerSource, "PreSignUp_SignUp") + + let signUp = CognitoEvent.PreSignUp(userAttributes: ["string": "string"], + validationData: ["string": "string"], + clientMetadata: ["string": "string"]) + XCTAssertEqual(request, signUp) + } + + func testPreSignUpResponse() throws { + let params = CognitoEvent.Parameters(version: "1", + triggerSource: "PreSignUp_SignUp", + region: .us_east_1, + userPoolId: "abc", + userName: "blob", + callerContext: .init(awsSdkVersion: "1", clientId: "abc")) + let request = CognitoEvent.PreSignUp(userAttributes: ["string": "string"], + validationData: ["string": "string"], + clientMetadata: ["string": "string"]) + + let signUpResponse = CognitoEventResponse.PreSignUp(autoConfirmUser: true, + autoVerifyPhone: true, + autoVerifyEmail: true) + + let response = CognitoEventResponse.preSignUpSignUp(params, request, signUpResponse) + + let data = try JSONEncoder().encode(response) + + let decodedResponse = try JSONDecoder().decode(CognitoEventResponse.self, from: data) + + guard case .preSignUpSignUp(let decodedParams, let decodedRequest, let decodedResponse) = decodedResponse else { + XCTFail() + return + } + + XCTAssertEqual(decodedParams, params) + XCTAssertEqual(decodedRequest, request) + XCTAssertEqual(decodedResponse, signUpResponse) + } + +} diff --git a/readme.md b/readme.md index 4036342..c66c474 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,7 @@ AWS Lambda functions can be invoked directly from the AWS Lambda console UI, AWS * [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) +* [Cognito Lambda Triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools-working-with-aws-lambda-triggers.html) **Note**: Each one of the integration points mentioned above includes a set of `Codable` structs that mirror AWS' data model for these APIs. From 5f34f8d5306a9369c306dd47b06ad3d9e79afb0d Mon Sep 17 00:00:00 2001 From: Matt <85322+mattmassicotte@users.noreply.github.com> Date: Thu, 26 Jan 2023 08:40:06 -0500 Subject: [PATCH 2/2] Fix up formatting --- Sources/AWSLambdaEvents/Cognito.swift | 5 +- Tests/AWSLambdaEventsTests/CognitoTests.swift | 49 +++++++++---------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/Sources/AWSLambdaEvents/Cognito.swift b/Sources/AWSLambdaEvents/Cognito.swift index 21b0b33..6e07dbf 100644 --- a/Sources/AWSLambdaEvents/Cognito.swift +++ b/Sources/AWSLambdaEvents/Cognito.swift @@ -92,7 +92,7 @@ extension CognitoEvent: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - let params = commonParameters + let params = self.commonParameters try container.encode(params.version, forKey: .version) try container.encode(params.triggerSource, forKey: .triggerSource) @@ -131,7 +131,6 @@ public enum CognitoEventResponse { } } - extension CognitoEventResponse: Codable { public enum CodingKeys: String, CodingKey { case version @@ -170,7 +169,7 @@ extension CognitoEventResponse: Codable { public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) - let params = commonParameters + let params = self.commonParameters try container.encode(params.version, forKey: .version) try container.encode(params.triggerSource, forKey: .triggerSource) diff --git a/Tests/AWSLambdaEventsTests/CognitoTests.swift b/Tests/AWSLambdaEventsTests/CognitoTests.swift index 32636f2..deb3ab5 100644 --- a/Tests/AWSLambdaEventsTests/CognitoTests.swift +++ b/Tests/AWSLambdaEventsTests/CognitoTests.swift @@ -18,31 +18,31 @@ import XCTest final class CognitoTests: XCTestCase { func testPreSignUpRequest() throws { let json = """ -{ - "version": "1", - "triggerSource": "PreSignUp_SignUp", - "region": "us-east-1", - "userPoolId": "abc", - "userName": "blob", - "callerContext": { - "awsSdkVersion": "1", - "clientId": "abc", - }, - "request": { - "userAttributes": { - "string": "string" - }, - "validationData": { - "string": "string" - }, - "clientMetadata": { - "string": "string" - } - }, + { + "version": "1", + "triggerSource": "PreSignUp_SignUp", + "region": "us-east-1", + "userPoolId": "abc", + "userName": "blob", + "callerContext": { + "awsSdkVersion": "1", + "clientId": "abc", + }, + "request": { + "userAttributes": { + "string": "string" + }, + "validationData": { + "string": "string" + }, + "clientMetadata": { + "string": "string" + } + }, - "response": {} -} -""" + "response": {} + } + """ let event = try JSONDecoder().decode(CognitoEvent.self, from: json.data(using: .utf8)!) guard case .preSignUpSignUp(let params, let request) = event else { @@ -88,5 +88,4 @@ final class CognitoTests: XCTestCase { XCTAssertEqual(decodedRequest, request) XCTAssertEqual(decodedResponse, signUpResponse) } - }