diff --git a/aws_lambda_powertools/utilities/data_classes/ses_event.py b/aws_lambda_powertools/utilities/data_classes/ses_event.py index 2ebc02e22a0..5e715c4eca8 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 @@ -139,15 +140,69 @@ def topic_arn(self) -> Optional[str]: @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 @@ -260,3 +315,16 @@ def mail(self) -> SESMail: @property def receipt(self) -> SESReceipt: return self.record.ses.receipt + + +class Disposition(Enum): + STOP_RULE = "STOP_RULE" + """No further actions in the current receipt rule will be processed, but further receipt rules can be processed.""" + STOP_RULE_SET = "STOP_RULE_SET" + """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]: + return {"disposition": disposition.value} 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/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 8a87075d16c..8c3cc51d55d 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 Disposition, SESReceiptAction, disposition_response from tests.functional.utils import load_event @@ -715,6 +716,64 @@ def test_ses_trigger_event(): 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_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( + { + "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_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)