From 155d114c80c82ec91513e6b022c93133d22bb528 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Wed, 28 May 2025 00:36:58 -0600 Subject: [PATCH 1/2] feat(parser): Added Cognito trigger schemas --- .../utilities/parser/models/__init__.py | 72 +++++ .../utilities/parser/models/cognito.py | 276 ++++++++++++++++++ .../parser/_pydantic/test_cognito_triggers.py | 46 +++ 3 files changed, 394 insertions(+) create mode 100644 aws_lambda_powertools/utilities/parser/models/cognito.py create mode 100644 tests/unit/parser/_pydantic/test_cognito_triggers.py diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 7ea8da2dc22..4257a802a1e 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -49,6 +49,43 @@ CloudWatchLogsLogEvent, CloudWatchLogsModel, ) +from .cognito import ( + CognitoCallerContextModel, + CognitoChallengeResultModel, + CognitoPreSignupRequestModel, + CognitoPreSignupResponseModel, + CognitoPreSignupTriggerModel, + CognitoPostConfirmationRequestModel, + CognitoPostConfirmationTriggerModel, + CognitoPreAuthenticationRequestModel, + CognitoPreAuthenticationTriggerModel, + CognitoPostAuthenticationRequestModel, + CognitoPostAuthenticationTriggerModel, + CognitoGroupConfigurationModel, + CognitoPreTokenGenRequestModel, + CognitoClaimsOverrideDetailsModel, + CognitoPreTokenGenResponseModel, + CognitoPreTokenGenerationTriggerModel, + CognitoMigrateUserRequestModel, + CognitoMigrateUserResponseModel, + CognitoMigrateUserTriggerModel, + CognitoCustomMessageRequestModel, + CognitoCustomMessageResponseModel, + CognitoCustomMessageTriggerModel, + CognitoCustomEmailSenderRequestModel, + CognitoCustomEmailSenderTriggerModel, + CognitoCustomSMSSenderRequestModel, + CognitoCustomSMSSenderTriggerModel, + CognitoDefineAuthChallengeRequestModel, + CognitoDefineAuthChallengeResponseModel, + CognitoDefineAuthChallengeTriggerModel, + CognitoCreateAuthChallengeRequestModel, + CognitoCreateAuthChallengeResponseModel, + CognitoCreateAuthChallengeTriggerModel, + CognitoVerifyAuthChallengeRequestModel, + CognitoVerifyAuthChallengeResponseModel, + CognitoVerifyAuthChallengeTriggerModel, +) from .dynamodb import ( DynamoDBStreamChangedRecordModel, DynamoDBStreamModel, @@ -137,6 +174,41 @@ "CloudWatchLogsDecode", "CloudWatchLogsLogEvent", "CloudWatchLogsModel", + "CognitoCallerContextModel", + "CognitoChallengeResultModel", + "CognitoPreSignupRequestModel", + "CognitoPreSignupResponseModel", + "CognitoPreSignupTriggerModel", + "CognitoPostConfirmationRequestModel", + "CognitoPostConfirmationTriggerModel", + "CognitoPreAuthenticationRequestModel", + "CognitoPreAuthenticationTriggerModel", + "CognitoPostAuthenticationRequestModel", + "CognitoPostAuthenticationTriggerModel", + "CognitoGroupConfigurationModel", + "CognitoPreTokenGenRequestModel", + "CognitoClaimsOverrideDetailsModel", + "CognitoPreTokenGenResponseModel", + "CognitoPreTokenGenerationTriggerModel", + "CognitoMigrateUserRequestModel", + "CognitoMigrateUserResponseModel", + "CognitoMigrateUserTriggerModel", + "CognitoCustomMessageRequestModel", + "CognitoCustomMessageResponseModel", + "CognitoCustomMessageTriggerModel", + "CognitoCustomEmailSenderRequestModel", + "CognitoCustomEmailSenderTriggerModel", + "CognitoCustomSMSSenderRequestModel", + "CognitoCustomSMSSenderTriggerModel", + "CognitoDefineAuthChallengeRequestModel", + "CognitoDefineAuthChallengeResponseModel", + "CognitoDefineAuthChallengeTriggerModel", + "CognitoCreateAuthChallengeRequestModel", + "CognitoCreateAuthChallengeResponseModel", + "CognitoCreateAuthChallengeTriggerModel", + "CognitoVerifyAuthChallengeRequestModel", + "CognitoVerifyAuthChallengeResponseModel", + "CognitoVerifyAuthChallengeTriggerModel", "AlbModel", "AlbRequestContext", "AlbRequestContextData", diff --git a/aws_lambda_powertools/utilities/parser/models/cognito.py b/aws_lambda_powertools/utilities/parser/models/cognito.py new file mode 100644 index 00000000000..d49457adc94 --- /dev/null +++ b/aws_lambda_powertools/utilities/parser/models/cognito.py @@ -0,0 +1,276 @@ +from __future__ import annotations +from typing import Any, Dict, List, Optional +from pydantic import BaseModel + + +class CognitoCallerContextModel(BaseModel): + awsSdkVersion: str + clientId: str + + +class CognitoChallengeResultModel(BaseModel): + challengeName: str + challengeResult: bool + challengeMetadata: Optional[str] + + +class CognitoPreSignupRequestModel(BaseModel): + userAttributes: Dict[str, str] + validationData: Optional[Dict[str, str]] = None + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoPreSignupResponseModel(BaseModel): + autoConfirmUser: Optional[bool] = None + autoVerifyPhone: Optional[bool] = None + autoVerifyEmail: Optional[bool] = None + + +class CognitoPreSignupTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoPreSignupRequestModel + response: CognitoPreSignupResponseModel + + +class CognitoPostConfirmationRequestModel(BaseModel): + userAttributes: Dict[str, str] + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoPostConfirmationTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoPostConfirmationRequestModel + response: Dict[str, Any] = {} + + +class CognitoPreAuthenticationRequestModel(BaseModel): + userAttributes: Dict[str, str] + validationData: Optional[Dict[str, str]] = None + userNotFound: Optional[bool] = None + + +class CognitoPreAuthenticationTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoPreAuthenticationRequestModel + response: Dict[str, Any] = {} + + +class CognitoPostAuthenticationRequestModel(BaseModel): + userAttributes: Dict[str, str] + newDeviceUsed: bool + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoPostAuthenticationTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoPostAuthenticationRequestModel + response: Dict[str, Any] = {} + + +class CognitoGroupConfigurationModel(BaseModel): + groupsToOverride: List[str] + iamRolesToOverride: List[str] + preferredRole: Optional[str] = None + + +class CognitoPreTokenGenRequestModel(BaseModel): + userAttributes: Dict[str, str] + groupConfiguration: CognitoGroupConfigurationModel + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoClaimsOverrideDetailsModel(BaseModel): + claimsToAddOrOverride: Optional[Dict[str, str]] = None + claimsToSuppress: Optional[List[str]] = None + groupOverrideDetails: Optional[CognitoGroupConfigurationModel] = None + + +class CognitoPreTokenGenResponseModel(BaseModel): + claimsOverrideDetails: Optional[CognitoClaimsOverrideDetailsModel] = None + + +class CognitoPreTokenGenerationTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoPreTokenGenRequestModel + response: CognitoPreTokenGenResponseModel + + +class CognitoMigrateUserRequestModel(BaseModel): + password: str + validationData: Optional[Dict[str, str]] = None + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoMigrateUserResponseModel(BaseModel): + userAttributes: Dict[str, str] + finalUserStatus: Optional[str] = None + messageAction: Optional[str] = None + desiredDeliveryMediums: Optional[List[str]] = None + forceAliasCreation: Optional[bool] = None + enableSMSMFA: Optional[bool] = None + + +class CognitoMigrateUserTriggerModel(BaseModel): + userName: str + version: Optional[str] = None + region: Optional[str] = None + userPoolId: Optional[str] = None + callerContext: Optional[CognitoCallerContextModel] = None + triggerSource: Optional[str] = None + request: CognitoMigrateUserRequestModel + response: CognitoMigrateUserResponseModel + + +class CognitoCustomMessageRequestModel(BaseModel): + userAttributes: Dict[str, str] + codeParameter: Optional[str] = None + usernameParameter: Optional[str] = None + clientMetadata: Optional[Dict[str, str]] = None + + +class CognitoCustomMessageResponseModel(BaseModel): + smsMessage: Optional[str] = None + emailMessage: Optional[str] = None + emailSubject: Optional[str] = None + + +class CognitoCustomMessageTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoCustomMessageRequestModel + response: CognitoCustomMessageResponseModel + + +class CognitoCustomEmailSenderRequestModel(BaseModel): + type: str + code: str + clientMetadata: Optional[Dict[str, str]] = None + userAttributes: Dict[str, str] + + +class CognitoCustomEmailSenderTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoCustomEmailSenderRequestModel + + +class CognitoCustomSMSSenderRequestModel(BaseModel): + type: str + code: str + clientMetadata: Optional[Dict[str, str]] = None + userAttributes: Dict[str, str] + + +class CognitoCustomSMSSenderTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoCustomSMSSenderRequestModel + + +class CognitoDefineAuthChallengeRequestModel(BaseModel): + userAttributes: Dict[str, str] + session: List[CognitoChallengeResultModel] + clientMetadata: Optional[Dict[str, str]] = None + userNotFound: Optional[bool] = None + + +class CognitoDefineAuthChallengeResponseModel(BaseModel): + challengeName: str + issueTokens: bool + failAuthentication: bool + + +class CognitoDefineAuthChallengeTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoDefineAuthChallengeRequestModel + response: CognitoDefineAuthChallengeResponseModel + + +class CognitoCreateAuthChallengeRequestModel(BaseModel): + userAttributes: Dict[str, str] + challengeName: str + session: List[CognitoChallengeResultModel] + clientMetadata: Optional[Dict[str, str]] = None + userNotFound: Optional[bool] = None + + +class CognitoCreateAuthChallengeResponseModel(BaseModel): + publicChallengeParameters: Dict[str, str] + privateChallengeParameters: Dict[str, str] + challengeMetadata: Optional[str] = None + + +class CognitoCreateAuthChallengeTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoCreateAuthChallengeRequestModel + response: CognitoCreateAuthChallengeResponseModel + + +class CognitoVerifyAuthChallengeRequestModel(BaseModel): + userAttributes: Dict[str, str] + privateChallengeParameters: Dict[str, str] + challengeAnswer: str + clientMetadata: Optional[Dict[str, str]] = None + userNotFound: Optional[bool] = None + + +class CognitoVerifyAuthChallengeResponseModel(BaseModel): + answerCorrect: bool + + +class CognitoVerifyAuthChallengeTriggerModel(BaseModel): + version: str + region: str + userPoolId: str + userName: str + callerContext: CognitoCallerContextModel + triggerSource: str + request: CognitoVerifyAuthChallengeRequestModel + response: CognitoVerifyAuthChallengeResponseModel \ No newline at end of file diff --git a/tests/unit/parser/_pydantic/test_cognito_triggers.py b/tests/unit/parser/_pydantic/test_cognito_triggers.py new file mode 100644 index 00000000000..39c88f66a80 --- /dev/null +++ b/tests/unit/parser/_pydantic/test_cognito_triggers.py @@ -0,0 +1,46 @@ +import pytest +from aws_lambda_powertools.utilities.parser import parse, ValidationError +from aws_lambda_powertools.utilities.parser.models import ( + CognitoPreSignupTriggerModel, + CognitoPostConfirmationTriggerModel, + CognitoPreAuthenticationTriggerModel, + CognitoPostAuthenticationTriggerModel, + CognitoPreTokenGenerationTriggerModel, + CognitoMigrateUserTriggerModel, + CognitoCustomMessageTriggerModel, + CognitoCustomEmailSenderTriggerModel, + CognitoCustomSMSSenderTriggerModel, + CognitoDefineAuthChallengeTriggerModel, + CognitoCreateAuthChallengeTriggerModel, + CognitoVerifyAuthChallengeTriggerModel, +) +from tests.functional.utils import load_event + +@pytest.mark.parametrize( + "filename,model", + [ + # use the existing `tests/events/*.json` names: + ("cognitoPreSignUpEvent.json", CognitoPreSignupTriggerModel), + ("cognitoPostConfirmationEvent.json", CognitoPostConfirmationTriggerModel), + ("cognitoPreAuthenticationEvent.json", CognitoPreAuthenticationTriggerModel), + ("cognitoPostAuthenticationEvent.json", CognitoPostAuthenticationTriggerModel), + ("cognitoPreTokenGenerationEvent.json", CognitoPreTokenGenerationTriggerModel), + ("cognitoUserMigrationEvent.json", CognitoMigrateUserTriggerModel), + ("cognitoCustomMessageEvent.json", CognitoCustomMessageTriggerModel), + ("cognitoCustomEmailSenderEvent.json", CognitoCustomEmailSenderTriggerModel), + ("cognitoCustomSMSSenderEvent.json", CognitoCustomSMSSenderTriggerModel), + ("cognitoDefineAuthChallengeEvent.json", CognitoDefineAuthChallengeTriggerModel), + ("cognitoCreateAuthChallengeEvent.json", CognitoCreateAuthChallengeTriggerModel), + ("cognitoVerifyAuthChallengeResponseEvent.json", CognitoVerifyAuthChallengeTriggerModel), + ], +) +def test_cognito_trigger_models_parse_success(filename, model): + event = load_event(filename) + parsed = parse(event=event, model=model) + # if parsing succeeds, we get an instance + assert isinstance(parsed, model) + + +def test_cognito_trigger_models_invalid_raises(): + with pytest.raises(ValidationError): + parse(event={"foo": "bar"}, model=CognitoPreSignupTriggerModel) From 03e520bbd077a38fb6767fb9ac9001a3c8343bc8 Mon Sep 17 00:00:00 2001 From: Vatsal Goel <144617902+VatsalGoel3@users.noreply.github.com> Date: Thu, 29 May 2025 00:47:21 -0600 Subject: [PATCH 2/2] Updated docs --- docs/utilities/parser.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/utilities/parser.md b/docs/utilities/parser.md index 4cdf0d452f2..e76c72d4b60 100644 --- a/docs/utilities/parser.md +++ b/docs/utilities/parser.md @@ -117,6 +117,18 @@ The example above uses `SqsModel`. Other built-in models can be found below. | **CloudFormationCustomResourceUpdateModel** | Lambda Event Source payload for AWS CloudFormation `UPDATE` operation | | **CloudFormationCustomResourceDeleteModel** | Lambda Event Source payload for AWS CloudFormation `DELETE` operation | | **CloudwatchLogsModel** | Lambda Event Source payload for Amazon CloudWatch Logs | +| **CognitoPreSignupTriggerModel** | Lambda User Pool Pre-Sign-Up trigger event | +| **CognitoPostConfirmationTriggerModel** | Lambda User Pool Post Confirmation trigger event | +| **CognitoPreAuthenticationTriggerModel** | Lambda User Pool Pre Authentication trigger event | +| **CognitoPostAuthenticationTriggerModel** | Lambda User Pool Post Authentication trigger event | +| **CognitoPreTokenGenerationTriggerModel** | Lambda User Pool Pre Token Generation trigger event | +| **CognitoMigrateUserTriggerModel** | Lambda User Pool Migrate User trigger event | +| **CognitoCustomMessageTriggerModel** | Lambda User Pool Custom Message trigger event | +| **CognitoCustomEmailSenderTriggerModel** | Lambda User Pool Custom Email Sender trigger event | +| **CognitoCustomSMSSenderTriggerModel** | Lambda User Pool Custom SMS Sender trigger event | +| **CognitoDefineAuthChallengeTriggerModel** | Lambda User Pool Define Auth Challenge trigger event | +| **CognitoCreateAuthChallengeTriggerModel** | Lambda User Pool Create Auth Challenge trigger event | +| **CognitoVerifyAuthChallengeTriggerModel** | Lambda User Pool Verify Auth Challenge trigger event | | **DynamoDBStreamModel** | Lambda Event Source payload for Amazon DynamoDB Streams | | **EventBridgeModel** | Lambda Event Source payload for Amazon EventBridge | | **IoTCoreThingEvent** | Lambda Event Source payload for IoT Core Thing created, updated, or deleted. |