Skip to content

Commit

Permalink
fix(parser): Add missing fields for SESEvent (aws-powertools#1027)
Browse files Browse the repository at this point in the history
Co-authored-by: Ran Isenberg <[email protected]>
  • Loading branch information
ran-isenberg and Ran Isenberg authored Apr 28, 2022
1 parent 11c55b9 commit 797a10a
Show file tree
Hide file tree
Showing 4 changed files with 206 additions and 8 deletions.
8 changes: 8 additions & 0 deletions aws_lambda_powertools/utilities/parser/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@
SesModel,
SesReceipt,
SesReceiptAction,
SesReceiptActionBase,
SesReceiptBounceAction,
SesReceiptS3Action,
SesReceiptVerdict,
SesReceiptWorkmailAction,
SesRecordModel,
)
from .sns import SnsModel, SnsNotificationModel, SnsRecordModel
Expand Down Expand Up @@ -84,6 +88,10 @@
"SesMailHeaders",
"SesReceipt",
"SesReceiptAction",
"SesReceiptActionBase",
"SesReceiptBounceAction",
"SesReceiptWorkmailAction",
"SesReceiptS3Action",
"SesReceiptVerdict",
"SnsModel",
"SnsNotificationModel",
Expand Down
34 changes: 31 additions & 3 deletions aws_lambda_powertools/utilities/parser/models/ses.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime
from typing import List, Optional
from typing import List, Optional, Union

from pydantic import BaseModel, Field
from pydantic.networks import EmailStr
Expand All @@ -12,21 +12,49 @@ class SesReceiptVerdict(BaseModel):
status: Literal["PASS", "FAIL", "GRAY", "PROCESSING_FAILED"]


class SesReceiptAction(BaseModel):
class SesReceiptActionBase(BaseModel):
topicArn: Optional[str]


class SesReceiptAction(SesReceiptActionBase):
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
recipients: List[EmailStr]
spamVerdict: SesReceiptVerdict
virusVerdict: SesReceiptVerdict
spfVerdict: SesReceiptVerdict
dkimVerdict: SesReceiptVerdict
dmarcVerdict: SesReceiptVerdict
action: SesReceiptAction
dmarcPolicy: Optional[Literal["quarantine", "reject", "none"]]
action: Union[SesReceiptAction, SesReceiptS3Action, SesReceiptBounceAction, SesReceiptWorkmailAction]


class SesMailHeaders(BaseModel):
Expand Down
114 changes: 114 additions & 0 deletions tests/events/sesEventS3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
{
"Records": [
{
"eventVersion": "1.0",
"ses": {
"receipt": {
"timestamp": "2015-09-11T20:32:33.936Z",
"processingTimeMillis": 406,
"recipients": [
"[email protected]"
],
"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": [
"[email protected]"
],
"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 [email protected]; 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": "[email protected]"
},
{
"name": "To",
"value": "[email protected]"
},
{
"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": "<[email protected]>"
},
{
"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": [
"[email protected]"
],
"date": "Fri, 11 Sep 2015 20:32:32 +0000",
"to": [
"[email protected]"
],
"messageId": "<[email protected]>",
"subject": "Example subject"
}
}
},
"eventSource": "aws:ses"
}
]
}
58 changes: 53 additions & 5 deletions tests/functional/parser/test_ses.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
from aws_lambda_powertools.utilities.parser import event_parser
from aws_lambda_powertools.utilities.parser.models import SesModel, SesRecordModel
from aws_lambda_powertools.utilities.parser.models import (
SesModel,
SesReceiptBounceAction,
SesReceiptWorkmailAction,
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):
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())
expected_address = "[email protected]"
records = event.Records
record: SesRecordModel = records[0]
Expand All @@ -29,6 +40,10 @@ def handle_ses(event: SesModel, _: LambdaContext):
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
Expand All @@ -38,12 +53,45 @@ def handle_ses(event: SesModel, _: LambdaContext):
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():
event_dict = load_event("sesEvent.json")
handle_ses(event_dict, LambdaContext())
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)

0 comments on commit 797a10a

Please sign in to comment.