Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(parser): Add missing fields for SESEvent #1027

Merged
merged 1 commit into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)