From ebbff892787e74d0e3f59c84801387757816136f Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sat, 12 Feb 2022 22:05:37 -0800 Subject: [PATCH 1/6] fix(data_classes): Add missing fields for SESEvent Change: - Add missing fields from SESMailCommonHeaders - Fix date type to be a str - Add missing docs for SESReceiptStatus status field - Add missing topicArn - Add missing fields for Bounce, S3 and WorkMail actions close #1025 --- .../utilities/data_classes/ses_event.py | 103 +++++++++++++++- tests/events/sesEventS3.json | 114 ++++++++++++++++++ tests/functional/test_data_classes.py | 52 ++++++++ 3 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 tests/events/sesEventS3.json diff --git a/aws_lambda_powertools/utilities/data_classes/ses_event.py b/aws_lambda_powertools/utilities/data_classes/ses_event.py index c270a85ddf7..7a312e2eb6d 100644 --- a/aws_lambda_powertools/utilities/data_classes/ses_event.py +++ b/aws_lambda_powertools/utilities/data_classes/ses_event.py @@ -1,4 +1,4 @@ -from typing import Iterator, List +from typing import Iterator, List, Optional from aws_lambda_powertools.utilities.data_classes.common import DictWrapper @@ -26,7 +26,7 @@ def get_from(self) -> List[str]: return self["from"] @property - def date(self) -> List[str]: + def date(self) -> str: """The date and time when Amazon SES received the message.""" return self["date"] @@ -45,6 +45,26 @@ def subject(self) -> str: """The value of the Subject header for the email.""" return str(self["subject"]) + @property + def cc(self) -> Optional[List[str]]: + """The values in the CC header of the email.""" + return self.get("cc") + + @property + def bcc(self) -> Optional[List[str]]: + """The values in the BCC header of the email.""" + return self.get("bcc") + + @property + def sender(self) -> Optional[List[str]]: + """The values in the Sender header of the email.""" + return self.get("sender") + + @property + def reply_to(self) -> Optional[List[str]]: + """The values in the replyTo header of the email.""" + return self.get("replyTo") + class SESMail(DictWrapper): @property @@ -94,6 +114,10 @@ def common_headers(self) -> SESMailCommonHeaders: class SESReceiptStatus(DictWrapper): @property def status(self) -> str: + """Receipt status + + Possible values: 'PASS', 'FAIL', 'GRAY', 'PROCESSING_FAILED', 'DISABLED' + """ return str(self["status"]) @@ -107,18 +131,78 @@ def get_type(self) -> str: # Note: this name conflicts with existing python builtins return self["type"] + @property + def topic_arn(self) -> Optional[str]: + """String that contains the Amazon Resource Name (ARN) of the Amazon SNS topic to which the + notification was published.""" + return self.get("topicArn") + @property def function_arn(self) -> str: """String that contains the ARN of the Lambda function that was triggered. + Present only for the Lambda action type.""" return self["functionArn"] @property def invocation_type(self) -> str: """String that contains the invocation type of the Lambda function. Possible values are RequestResponse - and Event. Present only for the Lambda action type.""" + and Event. + + Present only for the Lambda action type.""" return self["invocationType"] + @property + def bucket_name(self) -> str: + """String that contains the name of the Amazon S3 bucket to which the message was published. + + Present only for the S3 action type.""" + return str(self["bucketName"]) + + @property + def object_key(self) -> str: + """String that contains a name that uniquely identifies the email in the Amazon S3 bucket. + This is the same as the messageId in the mail object. + + Present only for the S3 action type.""" + return str(self["objectKey"]) + + @property + def smtp_reply_code(self) -> str: + """String that contains the SMTP reply code, as defined by RFC 5321. + + Present only for the bounce action type.""" + return str(self["smtpReplyCode"]) + + @property + def status_code(self) -> str: + """String that contains the SMTP enhanced status code, as defined by RFC 3463. + + Present only for the bounce action type.""" + return self["statusCode"] + + @property + def message(self) -> str: + """String that contains the human-readable text to include in the bounce message. + + Present only for the bounce action type.""" + return str(self["message"]) + + @property + def sender(self) -> str: + """String that contains the email address of the sender of the email that bounced. + This is the address from which the bounce message was sent. + + Present only for the bounce action type.""" + return str(self["sender"]) + + @property + def organization_arn(self) -> str: + """String that contains the ARN of the Amazon WorkMail organization. + + Present only for the WorkMail action type.""" + return str(self["organizationArn"]) + class SESReceipt(DictWrapper): @property @@ -154,12 +238,25 @@ def spf_verdict(self) -> SESReceiptStatus: """Object that indicates whether the Sender Policy Framework (SPF) check passed.""" return SESReceiptStatus(self["spfVerdict"]) + @property + def dkim_verdict(self) -> SESReceiptStatus: + """Object that indicates whether the DomainKeys Identified Mail (DKIM) check passed""" + return SESReceiptStatus(self["dkimVerdict"]) + @property def dmarc_verdict(self) -> SESReceiptStatus: """Object that indicates whether the Domain-based Message Authentication, Reporting & Conformance (DMARC) check passed.""" return SESReceiptStatus(self["dmarcVerdict"]) + @property + def dmarc_policy(self) -> Optional[str]: + """Indicates the Domain-based Message Authentication, Reporting & Conformance (DMARC) settings for + the sending domain. This field only appears if the message fails DMARC authentication. + + Possible values for this field are: none, quarantine, reject""" + return self.get("dmarcPolicy") + @property def action(self) -> SESReceiptAction: """Object that encapsulates information about the action that was executed.""" diff --git a/tests/events/sesEventS3.json b/tests/events/sesEventS3.json new file mode 100644 index 00000000000..16827d866d1 --- /dev/null +++ b/tests/events/sesEventS3.json @@ -0,0 +1,114 @@ +{ + "Records": [ + { + "eventVersion": "1.0", + "ses": { + "receipt": { + "timestamp": "2015-09-11T20:32:33.936Z", + "processingTimeMillis": 406, + "recipients": [ + "recipient@example.com" + ], + "spamVerdict": { + "status": "PASS" + }, + "virusVerdict": { + "status": "PASS" + }, + "spfVerdict": { + "status": "PASS" + }, + "dkimVerdict": { + "status": "PASS" + }, + "dmarcVerdict": { + "status": "PASS" + }, + "dmarcPolicy": "reject", + "action": { + "type": "S3", + "topicArn": "arn:aws:sns:us-east-1:012345678912:example-topic", + "bucketName": "my-S3-bucket", + "objectKey": "email" + } + }, + "mail": { + "timestamp": "2015-09-11T20:32:33.936Z", + "source": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", + "messageId": "d6iitobk75ur44p8kdnnp7g2n800", + "destination": [ + "recipient@example.com" + ], + "headersTruncated": false, + "headers": [ + { + "name": "Return-Path", + "value": "<0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com>" + }, + { + "name": "Received", + "value": "from a9-183.smtp-out.amazonses.com (a9-183.smtp-out.amazonses.com [54.240.9.183]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id d6iitobk75ur44p8kdnnp7g2n800 for recipient@example.com; Fri, 11 Sep 2015 20:32:33 +0000 (UTC)" + }, + { + "name": "DKIM-Signature", + "value": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug; d=amazonses.com; t=1442003552; h=From:To:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Date:Message-ID:Feedback-ID; bh=DWr3IOmYWoXCA9ARqGC/UaODfghffiwFNRIb2Mckyt4=; b=p4ukUDSFqhqiub+zPR0DW1kp7oJZakrzupr6LBe6sUuvqpBkig56UzUwc29rFbJF hlX3Ov7DeYVNoN38stqwsF8ivcajXpQsXRC1cW9z8x875J041rClAjV7EGbLmudVpPX 4hHst1XPyX5wmgdHIhmUuh8oZKpVqGi6bHGzzf7g=" + }, + { + "name": "From", + "value": "sender@example.com" + }, + { + "name": "To", + "value": "recipient@example.com" + }, + { + "name": "Subject", + "value": "Example subject" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Content-Type", + "value": "text/plain; charset=UTF-8" + }, + { + "name": "Content-Transfer-Encoding", + "value": "7bit" + }, + { + "name": "Date", + "value": "Fri, 11 Sep 2015 20:32:32 +0000" + }, + { + "name": "Message-ID", + "value": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>" + }, + { + "name": "X-SES-Outgoing", + "value": "2015.09.11-54.240.9.183" + }, + { + "name": "Feedback-ID", + "value": "1.us-east-1.Krv2FKpFdWV+KUYw3Qd6wcpPJ4Sv/pOPpEPSHn2u2o4=:AmazonSES" + } + ], + "commonHeaders": { + "returnPath": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", + "from": [ + "sender@example.com" + ], + "date": "Fri, 11 Sep 2015 20:32:32 +0000", + "to": [ + "recipient@example.com" + ], + "messageId": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>", + "subject": "Example subject" + } + } + }, + "eventSource": "aws:ses" + } + ] +} diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index d0d936ec2f8..ffdb5db5a08 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -76,6 +76,7 @@ ) from aws_lambda_powertools.utilities.data_classes.event_source import event_source from aws_lambda_powertools.utilities.data_classes.s3_object_event import S3ObjectLambdaEvent +from aws_lambda_powertools.utilities.data_classes.ses_event import SESReceiptAction from tests.functional.utils import load_event @@ -691,6 +692,10 @@ def test_ses_trigger_event(): assert common_headers.to == [expected_address] assert common_headers.message_id == "<0123456789example.com>" assert common_headers.subject == "Test Subject" + assert common_headers.cc is None + assert common_headers.bcc is None + assert common_headers.sender is None + assert common_headers.reply_to is None receipt = record.ses.receipt assert receipt.timestamp == "1970-01-01T00:00:00.000Z" assert receipt.processing_time_millis == 574 @@ -699,15 +704,62 @@ def test_ses_trigger_event(): assert receipt.virus_verdict.status == "PASS" assert receipt.spf_verdict.status == "PASS" assert receipt.dmarc_verdict.status == "PASS" + assert receipt.dkim_verdict.status == "PASS" + assert receipt.dmarc_policy is None action = receipt.action assert action.get_type == action.raw_event["type"] assert action.function_arn == action.raw_event["functionArn"] assert action.invocation_type == action.raw_event["invocationType"] + assert action.topic_arn is None assert event.record.raw_event == event["Records"][0] assert event.mail.raw_event == event["Records"][0]["ses"]["mail"] assert event.receipt.raw_event == event["Records"][0]["ses"]["receipt"] +def test_ses_trigger_event_s3(): + event = SESEvent(load_event("sesEventS3.json")) + records = list(event.records) + record = records[0] + receipt = record.ses.receipt + assert receipt.dmarc_policy == "reject" + action = record.ses.receipt.action + assert action.get_type == "S3" + assert action.topic_arn == "arn:aws:sns:us-east-1:012345678912:example-topic" + assert action.bucket_name == "my-S3-bucket" + assert action.object_key == "email" + + +def test_ses_trigger_event_bounce(): + action = SESReceiptAction( + { + "type": "Bounce", + "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", + "smtpReplyCode": "5.1.1", + "message": "message", + "sender": "sender", + "statusCode": "550", + } + ) + assert action.get_type == action["type"] + assert action.topic_arn == action["topicArn"] + assert action.smtp_reply_code == action["smtpReplyCode"] + assert action.message == action["message"] + assert action.sender == action["sender"] + assert action.status_code == action["statusCode"] + + +def test_ses_trigger_event_work_mail(): + action = SESReceiptAction( + { + "type": "WorkMail", + "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", + "organizationArn": "arn", + } + ) + assert action.get_type == "WorkMail" + assert action.organization_arn == action["organizationArn"] + + def test_sns_trigger_event(): event = SNSEvent(load_event("snsEvent.json")) records = list(event.records) From e1539bd6516d302b6457b13b988f8e2d4c92e323 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Sun, 13 Feb 2022 19:29:05 -0800 Subject: [PATCH 2/6] tests: Add SES event for SNS --- tests/events/sesEventSNS.json | 112 ++++++++++++++++++++++++++ tests/functional/test_data_classes.py | 9 +++ 2 files changed, 121 insertions(+) create mode 100644 tests/events/sesEventSNS.json diff --git a/tests/events/sesEventSNS.json b/tests/events/sesEventSNS.json new file mode 100644 index 00000000000..2b6086b6a98 --- /dev/null +++ b/tests/events/sesEventSNS.json @@ -0,0 +1,112 @@ +{ + "Records": [ + { + "eventVersion": "1.0", + "ses": { + "receipt": { + "timestamp": "2015-09-11T20:32:33.936Z", + "processingTimeMillis": 222, + "recipients": [ + "recipient@example.com" + ], + "spamVerdict": { + "status": "PASS" + }, + "virusVerdict": { + "status": "PASS" + }, + "spfVerdict": { + "status": "PASS" + }, + "dkimVerdict": { + "status": "PASS" + }, + "dmarcVerdict": { + "status": "PASS" + }, + "dmarcPolicy": "reject", + "action": { + "type": "SNS", + "topicArn": "arn:aws:sns:us-east-1:012345678912:example-topic" + } + }, + "mail": { + "timestamp": "2015-09-11T20:32:33.936Z", + "source": "61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com", + "messageId": "d6iitobk75ur44p8kdnnp7g2n800", + "destination": [ + "recipient@example.com" + ], + "headersTruncated": false, + "headers": [ + { + "name": "Return-Path", + "value": "<0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com>" + }, + { + "name": "Received", + "value": "from a9-183.smtp-out.amazonses.com (a9-183.smtp-out.amazonses.com [54.240.9.183]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id d6iitobk75ur44p8kdnnp7g2n800 for recipient@example.com; Fri, 11 Sep 2015 20:32:33 +0000 (UTC)" + }, + { + "name": "DKIM-Signature", + "value": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug; d=amazonses.com; t=1442003552; h=From:To:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Date:Message-ID:Feedback-ID; bh=DWr3IOmYWoXCA9ARqGC/UaODfghffiwFNRIb2Mckyt4=; b=p4ukUDSFqhqiub+zPR0DW1kp7oJZakrzupr6LBe6sUuvqpBkig56UzUwc29rFbJF hlX3Ov7DeYVNoN38stqwsF8ivcajXpQsXRC1cW9z8x875J041rClAjV7EGbLmudVpPX 4hHst1XPyX5wmgdHIhmUuh8oZKpVqGi6bHGzzf7g=" + }, + { + "name": "From", + "value": "sender@example.com" + }, + { + "name": "To", + "value": "recipient@example.com" + }, + { + "name": "Subject", + "value": "Example subject" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Content-Type", + "value": "text/plain; charset=UTF-8" + }, + { + "name": "Content-Transfer-Encoding", + "value": "7bit" + }, + { + "name": "Date", + "value": "Fri, 11 Sep 2015 20:32:32 +0000" + }, + { + "name": "Message-ID", + "value": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>" + }, + { + "name": "X-SES-Outgoing", + "value": "2015.09.11-54.240.9.183" + }, + { + "name": "Feedback-ID", + "value": "1.us-east-1.Krv2FKpFdWV+KUYw3Qd6wcpPJ4Sv/pOPpEPSHn2u2o4=:AmazonSES" + } + ], + "commonHeaders": { + "returnPath": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", + "from": [ + "sender@example.com" + ], + "date": "Fri, 11 Sep 2015 20:32:32 +0000", + "to": [ + "recipient@example.com" + ], + "messageId": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>", + "subject": "Example subject" + } + } + }, + "eventSource": "aws:ses" + } + ] + } diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index ffdb5db5a08..fa57e759db8 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -729,6 +729,15 @@ def test_ses_trigger_event_s3(): assert action.object_key == "email" +def test_ses_trigger_event_sns(): + event = SESEvent(load_event("sesEventSNS.json")) + records = list(event.records) + record = records[0] + action = record.ses.receipt.action + assert action.get_type == "SNS" + assert action.topic_arn == "arn:aws:sns:us-east-1:012345678912:example-topic" + + def test_ses_trigger_event_bounce(): action = SESReceiptAction( { From c049fd5f7023e4176bdefd039ef164f6946ac527 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 18 Feb 2022 15:09:38 -0800 Subject: [PATCH 3/6] feat(data-classes): Add ses response builder function --- .../utilities/data_classes/ses_event.py | 17 ++++++++++++++++- tests/functional/test_data_classes.py | 7 ++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/ses_event.py b/aws_lambda_powertools/utilities/data_classes/ses_event.py index 7a312e2eb6d..42d29866b94 100644 --- a/aws_lambda_powertools/utilities/data_classes/ses_event.py +++ b/aws_lambda_powertools/utilities/data_classes/ses_event.py @@ -1,4 +1,5 @@ -from typing import Iterator, List, Optional +from enum import Enum +from typing import Dict, Iterator, List, Optional from aws_lambda_powertools.utilities.data_classes.common import DictWrapper @@ -316,3 +317,17 @@ def mail(self) -> SESMail: @property def receipt(self) -> SESReceipt: return self.record.ses.receipt + + +class Disposition(Enum): + """No further actions in the current receipt rule will be processed, but further receipt rules can be processed.""" + + STOP_RULE = "STOP_RULE" + """No further actions or receipt rules will be processed.""" + STOP_RULE_SET = "STOP_RULE_SET" + """This means that further actions and receipt rules can be processed.""" + CONTINUE = "CONTINUE" + + +def disposition_response(disposition: Disposition) -> Dict[str, str]: + return {"disposition": disposition.value} diff --git a/tests/functional/test_data_classes.py b/tests/functional/test_data_classes.py index fa57e759db8..964150419da 100644 --- a/tests/functional/test_data_classes.py +++ b/tests/functional/test_data_classes.py @@ -76,7 +76,7 @@ ) from aws_lambda_powertools.utilities.data_classes.event_source import event_source from aws_lambda_powertools.utilities.data_classes.s3_object_event import S3ObjectLambdaEvent -from aws_lambda_powertools.utilities.data_classes.ses_event import SESReceiptAction +from aws_lambda_powertools.utilities.data_classes.ses_event import Disposition, SESReceiptAction, disposition_response from tests.functional.utils import load_event @@ -769,6 +769,11 @@ def test_ses_trigger_event_work_mail(): assert action.organization_arn == action["organizationArn"] +def test_ses_disposition_response(): + response = disposition_response(Disposition.STOP_RULE_SET) + assert response == {"disposition": "STOP_RULE_SET"} + + def test_sns_trigger_event(): event = SNSEvent(load_event("snsEvent.json")) records = list(event.records) From fd9020c5abefa1c499854ed482b8761215b004ff Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 18 Feb 2022 21:03:17 -0800 Subject: [PATCH 4/6] docs: fix docstrings for enum --- aws_lambda_powertools/utilities/data_classes/ses_event.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/aws_lambda_powertools/utilities/data_classes/ses_event.py b/aws_lambda_powertools/utilities/data_classes/ses_event.py index 42d29866b94..3a66e741cd0 100644 --- a/aws_lambda_powertools/utilities/data_classes/ses_event.py +++ b/aws_lambda_powertools/utilities/data_classes/ses_event.py @@ -320,13 +320,12 @@ def receipt(self) -> SESReceipt: class Disposition(Enum): - """No further actions in the current receipt rule will be processed, but further receipt rules can be processed.""" - STOP_RULE = "STOP_RULE" - """No further actions or receipt rules will be processed.""" + """No further actions in the current receipt rule will be processed, but further receipt rules can be processed.""" STOP_RULE_SET = "STOP_RULE_SET" - """This means that further actions and receipt rules can be processed.""" + """No further actions or receipt rules will be processed.""" CONTINUE = "CONTINUE" + """This means that further actions and receipt rules can be processed.""" def disposition_response(disposition: Disposition) -> Dict[str, str]: From bfa03b652bd6360d406ab068e96dd292adbfcba4 Mon Sep 17 00:00:00 2001 From: Michael Brewer Date: Fri, 18 Feb 2022 21:33:07 -0800 Subject: [PATCH 5/6] From 31fe45ec28d7ac9579d509351be214cabecd9c3e Mon Sep 17 00:00:00 2001 From: Simon Thulbourn Date: Thu, 28 Apr 2022 17:34:36 +0100 Subject: [PATCH 6/6] Revert "fix(parser): Add missing fields for SESEvent (#1027)" (#1190) This reverts commit 797a10afac80544e2d69bcb7d624909436f2b12a. --- .../utilities/parser/models/__init__.py | 8 -- .../utilities/parser/models/ses.py | 34 +----- tests/events/sesEventS3.json | 114 ------------------ tests/functional/parser/test_ses.py | 58 +-------- 4 files changed, 8 insertions(+), 206 deletions(-) delete mode 100644 tests/events/sesEventS3.json diff --git a/aws_lambda_powertools/utilities/parser/models/__init__.py b/aws_lambda_powertools/utilities/parser/models/__init__.py index 34c8e6ce6a1..e3fb50a2d5d 100644 --- a/aws_lambda_powertools/utilities/parser/models/__init__.py +++ b/aws_lambda_powertools/utilities/parser/models/__init__.py @@ -37,11 +37,7 @@ SesModel, SesReceipt, SesReceiptAction, - SesReceiptActionBase, - SesReceiptBounceAction, - SesReceiptS3Action, SesReceiptVerdict, - SesReceiptWorkmailAction, SesRecordModel, ) from .sns import SnsModel, SnsNotificationModel, SnsRecordModel @@ -88,10 +84,6 @@ "SesMailHeaders", "SesReceipt", "SesReceiptAction", - "SesReceiptActionBase", - "SesReceiptBounceAction", - "SesReceiptWorkmailAction", - "SesReceiptS3Action", "SesReceiptVerdict", "SnsModel", "SnsNotificationModel", diff --git a/aws_lambda_powertools/utilities/parser/models/ses.py b/aws_lambda_powertools/utilities/parser/models/ses.py index 7cd655ea28c..70fd2e83978 100644 --- a/aws_lambda_powertools/utilities/parser/models/ses.py +++ b/aws_lambda_powertools/utilities/parser/models/ses.py @@ -1,5 +1,5 @@ from datetime import datetime -from typing import List, Optional, Union +from typing import List, Optional from pydantic import BaseModel, Field from pydantic.networks import EmailStr @@ -12,38 +12,12 @@ class SesReceiptVerdict(BaseModel): status: Literal["PASS", "FAIL", "GRAY", "PROCESSING_FAILED"] -class SesReceiptActionBase(BaseModel): - topicArn: Optional[str] - - -class SesReceiptAction(SesReceiptActionBase): +class SesReceiptAction(BaseModel): type: Literal["Lambda"] # noqa A003,VNE003 invocationType: Literal["Event"] functionArn: str -class SesReceiptS3Action(SesReceiptActionBase): - type: Literal["S3"] # noqa A003,VNE003 - topicArn: str - bucketName: str - objectKey: str - - -class SesReceiptBounceAction(SesReceiptActionBase): - type: Literal["Bounce"] # noqa A003,VNE003 - topicArn: str - smtpReplyCode: str - message: str - sender: str - statusCode: str - - -class SesReceiptWorkmailAction(SesReceiptActionBase): - type: Literal["WorkMail"] # noqa A003,VNE003 - topicArn: str - organizationArn: str - - class SesReceipt(BaseModel): timestamp: datetime processingTimeMillis: PositiveInt @@ -51,10 +25,8 @@ class SesReceipt(BaseModel): spamVerdict: SesReceiptVerdict virusVerdict: SesReceiptVerdict spfVerdict: SesReceiptVerdict - dkimVerdict: SesReceiptVerdict dmarcVerdict: SesReceiptVerdict - dmarcPolicy: Optional[Literal["quarantine", "reject", "none"]] - action: Union[SesReceiptAction, SesReceiptS3Action, SesReceiptBounceAction, SesReceiptWorkmailAction] + action: SesReceiptAction class SesMailHeaders(BaseModel): diff --git a/tests/events/sesEventS3.json b/tests/events/sesEventS3.json deleted file mode 100644 index dbea2d42ce1..00000000000 --- a/tests/events/sesEventS3.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "Records": [ - { - "eventVersion": "1.0", - "ses": { - "receipt": { - "timestamp": "2015-09-11T20:32:33.936Z", - "processingTimeMillis": 406, - "recipients": [ - "recipient@example.com" - ], - "spamVerdict": { - "status": "PASS" - }, - "virusVerdict": { - "status": "PASS" - }, - "spfVerdict": { - "status": "PASS" - }, - "dkimVerdict": { - "status": "PASS" - }, - "dmarcVerdict": { - "status": "PASS" - }, - "dmarcPolicy": "reject", - "action": { - "type": "S3", - "topicArn": "arn:aws:sns:us-east-1:012345678912:example-topic", - "bucketName": "my-S3-bucket", - "objectKey": "email" - } - }, - "mail": { - "timestamp": "2015-09-11T20:32:33.936Z", - "source": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", - "messageId": "d6iitobk75ur44p8kdnnp7g2n800", - "destination": [ - "recipient@example.com" - ], - "headersTruncated": false, - "headers": [ - { - "name": "Return-Path", - "value": "<0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com>" - }, - { - "name": "Received", - "value": "from a9-183.smtp-out.amazonses.com (a9-183.smtp-out.amazonses.com [54.240.9.183]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id d6iitobk75ur44p8kdnnp7g2n800 for recipient@example.com; Fri, 11 Sep 2015 20:32:33 +0000 (UTC)" - }, - { - "name": "DKIM-Signature", - "value": "v=1; a=rsa-sha256; q=dns/txt; c=relaxed/simple; s=ug7nbtf4gccmlpwj322ax3p6ow6yfsug; d=amazonses.com; t=1442003552; h=From:To:Subject:MIME-Version:Content-Type:Content-Transfer-Encoding:Date:Message-ID:Feedback-ID; bh=DWr3IOmYWoXCA9ARqGC/UaODfghffiwFNRIb2Mckyt4=; b=p4ukUDSFqhqiub+zPR0DW1kp7oJZakrzupr6LBe6sUuvqpBkig56UzUwc29rFbJF hlX3Ov7DeYVNoN38stqwsF8ivcajXpQsXRC1cW9z8x875J041rClAjV7EGbLmudVpPX 4hHst1XPyX5wmgdHIhmUuh8oZKpVqGi6bHGzzf7g=" - }, - { - "name": "From", - "value": "sender@example.com" - }, - { - "name": "To", - "value": "recipient@example.com" - }, - { - "name": "Subject", - "value": "Example subject" - }, - { - "name": "MIME-Version", - "value": "1.0" - }, - { - "name": "Content-Type", - "value": "text/plain; charset=UTF-8" - }, - { - "name": "Content-Transfer-Encoding", - "value": "7bit" - }, - { - "name": "Date", - "value": "Fri, 11 Sep 2015 20:32:32 +0000" - }, - { - "name": "Message-ID", - "value": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>" - }, - { - "name": "X-SES-Outgoing", - "value": "2015.09.11-54.240.9.183" - }, - { - "name": "Feedback-ID", - "value": "1.us-east-1.Krv2FKpFdWV+KUYw3Qd6wcpPJ4Sv/pOPpEPSHn2u2o4=:AmazonSES" - } - ], - "commonHeaders": { - "returnPath": "0000014fbe1c09cf-7cb9f704-7531-4e53-89a1-5fa9744f5eb6-000000@amazonses.com", - "from": [ - "sender@example.com" - ], - "date": "Fri, 11 Sep 2015 20:32:32 +0000", - "to": [ - "recipient@example.com" - ], - "messageId": "<61967230-7A45-4A9D-BEC9-87CBCF2211C9@example.com>", - "subject": "Example subject" - } - } - }, - "eventSource": "aws:ses" - } - ] -} diff --git a/tests/functional/parser/test_ses.py b/tests/functional/parser/test_ses.py index 34a44253514..d434e2350f8 100644 --- a/tests/functional/parser/test_ses.py +++ b/tests/functional/parser/test_ses.py @@ -1,22 +1,11 @@ from aws_lambda_powertools.utilities.parser import event_parser -from aws_lambda_powertools.utilities.parser.models import ( - SesModel, - SesReceiptBounceAction, - SesReceiptWorkmailAction, - SesRecordModel, -) +from aws_lambda_powertools.utilities.parser.models import SesModel, SesRecordModel from aws_lambda_powertools.utilities.typing import LambdaContext from tests.functional.utils import load_event @event_parser(model=SesModel) -def handle_ses(event: SesModel, _: LambdaContext) -> SesModel: - return event - - -def test_ses_trigger_lambda_event(): - event_dict = load_event("sesEvent.json") - event = handle_ses(event_dict, LambdaContext()) +def handle_ses(event: SesModel, _: LambdaContext): expected_address = "johndoe@example.com" records = event.Records record: SesRecordModel = records[0] @@ -40,10 +29,6 @@ def test_ses_trigger_lambda_event(): assert common_headers.to == [expected_address] assert common_headers.messageId == "<0123456789example.com>" assert common_headers.subject == "Test Subject" - assert common_headers.cc is None - assert common_headers.bcc is None - assert common_headers.sender is None - assert common_headers.reply_to is None receipt = record.ses.receipt convert_time = int(round(receipt.timestamp.timestamp() * 1000)) assert convert_time == 0 @@ -53,45 +38,12 @@ def test_ses_trigger_lambda_event(): assert receipt.virusVerdict.status == "PASS" assert receipt.spfVerdict.status == "PASS" assert receipt.dmarcVerdict.status == "PASS" - assert receipt.dmarcVerdict.status == "PASS" - assert receipt.dmarcPolicy is None action = receipt.action assert action.type == "Lambda" assert action.functionArn == "arn:aws:lambda:us-west-2:012345678912:function:Example" assert action.invocationType == "Event" - assert action.topicArn is None - -def test_ses_trigger_event_s3(): - event_dict = load_event("sesEventS3.json") - event = handle_ses(event_dict, LambdaContext()) - records = list(event.Records) - record = records[0] - receipt = record.ses.receipt - assert receipt.dmarcPolicy == "reject" - action = record.ses.receipt.action - assert action.type == "S3" - assert action.topicArn == "arn:aws:sns:us-east-1:012345678912:example-topic" - assert action.bucketName == "my-S3-bucket" - assert action.objectKey == "email" - - -def test_ses_trigger_event_bounce(): - event_dict = { - "type": "Bounce", - "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", - "smtpReplyCode": "5.1.1", - "message": "message", - "sender": "sender", - "statusCode": "550", - } - SesReceiptBounceAction(**event_dict) - -def test_ses_trigger_event_work_mail(): - event_dict = { - "type": "WorkMail", - "topicArn": "arn:aws:sns:us-east-1:123456789012:topic:my-topic", - "organizationArn": "arn", - } - SesReceiptWorkmailAction(**event_dict) +def test_ses_trigger_event(): + event_dict = load_event("sesEvent.json") + handle_ses(event_dict, LambdaContext())