Run black on moto & test directories.
This commit is contained in:
parent
c820395dbf
commit
96e5b1993d
507 changed files with 52541 additions and 47814 deletions
|
|
@ -6,5 +6,4 @@ class MessageRejectedError(RESTError):
|
|||
code = 400
|
||||
|
||||
def __init__(self, message):
|
||||
super(MessageRejectedError, self).__init__(
|
||||
"MessageRejected", message)
|
||||
super(MessageRejectedError, self).__init__("MessageRejected", message)
|
||||
|
|
|
|||
|
|
@ -11,32 +11,20 @@ COMMON_MAIL = {
|
|||
"sourceArn": "arn:aws:ses:us-west-2:888888888888:identity/example.com",
|
||||
"sourceIp": "127.0.3.0",
|
||||
"sendingAccountId": "123456789012",
|
||||
"destination": [
|
||||
"recipient@example.com"
|
||||
],
|
||||
"destination": ["recipient@example.com"],
|
||||
"headersTruncated": False,
|
||||
"headers": [
|
||||
{
|
||||
"name": "From",
|
||||
"value": "\"Sender Name\" <sender@example.com>"
|
||||
},
|
||||
{
|
||||
"name": "To",
|
||||
"value": "\"Recipient Name\" <recipient@example.com>"
|
||||
}
|
||||
{"name": "From", "value": '"Sender Name" <sender@example.com>'},
|
||||
{"name": "To", "value": '"Recipient Name" <recipient@example.com>'},
|
||||
],
|
||||
"commonHeaders": {
|
||||
"from": [
|
||||
"Sender Name <sender@example.com>"
|
||||
],
|
||||
"from": ["Sender Name <sender@example.com>"],
|
||||
"date": "Mon, 08 Oct 2018 14:05:45 +0000",
|
||||
"to": [
|
||||
"Recipient Name <recipient@example.com>"
|
||||
],
|
||||
"to": ["Recipient Name <recipient@example.com>"],
|
||||
"messageId": " custom-message-ID",
|
||||
"subject": "Message sent using Amazon SES"
|
||||
}
|
||||
}
|
||||
"subject": "Message sent using Amazon SES",
|
||||
},
|
||||
},
|
||||
}
|
||||
BOUNCE = {
|
||||
"bounceType": "Permanent",
|
||||
|
|
@ -46,30 +34,26 @@ BOUNCE = {
|
|||
"status": "5.0.0",
|
||||
"action": "failed",
|
||||
"diagnosticCode": "smtp; 550 user unknown",
|
||||
"emailAddress": "recipient1@example.com"
|
||||
"emailAddress": "recipient1@example.com",
|
||||
},
|
||||
{
|
||||
"status": "4.0.0",
|
||||
"action": "delayed",
|
||||
"emailAddress": "recipient2@example.com"
|
||||
}
|
||||
"emailAddress": "recipient2@example.com",
|
||||
},
|
||||
],
|
||||
"reportingMTA": "example.com",
|
||||
"timestamp": "2012-05-25T14:59:38.605Z",
|
||||
"feedbackId": "000001378603176d-5a4b5ad9-6f30-4198-a8c3-b1eb0c270a1d-000000",
|
||||
"remoteMtaIp": "127.0.2.0"
|
||||
"remoteMtaIp": "127.0.2.0",
|
||||
}
|
||||
COMPLAINT = {
|
||||
"userAgent": "AnyCompany Feedback Loop (V0.01)",
|
||||
"complainedRecipients": [
|
||||
{
|
||||
"emailAddress": "recipient1@example.com"
|
||||
}
|
||||
],
|
||||
"complainedRecipients": [{"emailAddress": "recipient1@example.com"}],
|
||||
"complaintFeedbackType": "abuse",
|
||||
"arrivalDate": "2009-12-03T04:24:21.000-05:00",
|
||||
"timestamp": "2012-05-25T14:59:38.623Z",
|
||||
"feedbackId": "000001378603177f-18c07c78-fa81-4a58-9dd1-fedc3cb8f49a-000000"
|
||||
"feedbackId": "000001378603177f-18c07c78-fa81-4a58-9dd1-fedc3cb8f49a-000000",
|
||||
}
|
||||
DELIVERY = {
|
||||
"timestamp": "2014-05-28T22:41:01.184Z",
|
||||
|
|
@ -77,5 +61,5 @@ DELIVERY = {
|
|||
"recipients": ["success@simulator.amazonses.com"],
|
||||
"smtpResponse": "250 ok: Message 64111812 accepted",
|
||||
"reportingMTA": "a8-70.smtp-out.amazonses.com",
|
||||
"remoteMtaIp": "127.0.2.0"
|
||||
"remoteMtaIp": "127.0.2.0",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ class SESFeedback(BaseModel):
|
|||
|
||||
|
||||
class Message(BaseModel):
|
||||
|
||||
def __init__(self, message_id, source, subject, body, destinations):
|
||||
self.id = message_id
|
||||
self.source = source
|
||||
|
|
@ -50,13 +49,7 @@ class Message(BaseModel):
|
|||
|
||||
|
||||
class TemplateMessage(BaseModel):
|
||||
|
||||
def __init__(self,
|
||||
message_id,
|
||||
source,
|
||||
template,
|
||||
template_data,
|
||||
destinations):
|
||||
def __init__(self, message_id, source, template, template_data, destinations):
|
||||
self.id = message_id
|
||||
self.source = source
|
||||
self.template = template
|
||||
|
|
@ -65,7 +58,6 @@ class TemplateMessage(BaseModel):
|
|||
|
||||
|
||||
class RawMessage(BaseModel):
|
||||
|
||||
def __init__(self, message_id, source, destinations, raw_data):
|
||||
self.id = message_id
|
||||
self.source = source
|
||||
|
|
@ -74,7 +66,6 @@ class RawMessage(BaseModel):
|
|||
|
||||
|
||||
class SESQuota(BaseModel):
|
||||
|
||||
def __init__(self, sent):
|
||||
self.sent = sent
|
||||
|
||||
|
|
@ -84,7 +75,6 @@ class SESQuota(BaseModel):
|
|||
|
||||
|
||||
class SESBackend(BaseBackend):
|
||||
|
||||
def __init__(self):
|
||||
self.addresses = []
|
||||
self.email_addresses = []
|
||||
|
|
@ -97,7 +87,7 @@ class SESBackend(BaseBackend):
|
|||
_, address = parseaddr(source)
|
||||
if address in self.addresses:
|
||||
return True
|
||||
user, host = address.split('@', 1)
|
||||
user, host = address.split("@", 1)
|
||||
return host in self.domains
|
||||
|
||||
def verify_email_identity(self, address):
|
||||
|
|
@ -116,7 +106,7 @@ class SESBackend(BaseBackend):
|
|||
return self.email_addresses
|
||||
|
||||
def delete_identity(self, identity):
|
||||
if '@' in identity:
|
||||
if "@" in identity:
|
||||
self.addresses.remove(identity)
|
||||
else:
|
||||
self.domains.remove(identity)
|
||||
|
|
@ -124,11 +114,9 @@ class SESBackend(BaseBackend):
|
|||
def send_email(self, source, subject, body, destinations, region):
|
||||
recipient_count = sum(map(len, destinations.values()))
|
||||
if recipient_count > RECIPIENT_LIMIT:
|
||||
raise MessageRejectedError('Too many recipients.')
|
||||
raise MessageRejectedError("Too many recipients.")
|
||||
if not self._is_verified_address(source):
|
||||
raise MessageRejectedError(
|
||||
"Email address not verified %s" % source
|
||||
)
|
||||
raise MessageRejectedError("Email address not verified %s" % source)
|
||||
|
||||
self.__process_sns_feedback__(source, destinations, region)
|
||||
|
||||
|
|
@ -138,23 +126,21 @@ class SESBackend(BaseBackend):
|
|||
self.sent_message_count += recipient_count
|
||||
return message
|
||||
|
||||
def send_templated_email(self, source, template, template_data, destinations, region):
|
||||
def send_templated_email(
|
||||
self, source, template, template_data, destinations, region
|
||||
):
|
||||
recipient_count = sum(map(len, destinations.values()))
|
||||
if recipient_count > RECIPIENT_LIMIT:
|
||||
raise MessageRejectedError('Too many recipients.')
|
||||
raise MessageRejectedError("Too many recipients.")
|
||||
if not self._is_verified_address(source):
|
||||
raise MessageRejectedError(
|
||||
"Email address not verified %s" % source
|
||||
)
|
||||
raise MessageRejectedError("Email address not verified %s" % source)
|
||||
|
||||
self.__process_sns_feedback__(source, destinations, region)
|
||||
|
||||
message_id = get_random_message_id()
|
||||
message = TemplateMessage(message_id,
|
||||
source,
|
||||
template,
|
||||
template_data,
|
||||
destinations)
|
||||
message = TemplateMessage(
|
||||
message_id, source, template, template_data, destinations
|
||||
)
|
||||
self.sent_messages.append(message)
|
||||
self.sent_message_count += recipient_count
|
||||
return message
|
||||
|
|
@ -162,10 +148,11 @@ class SESBackend(BaseBackend):
|
|||
def __type_of_message__(self, destinations):
|
||||
"""Checks the destination for any special address that could indicate delivery,
|
||||
complaint or bounce like in SES simualtor"""
|
||||
alladdress = destinations.get(
|
||||
"ToAddresses", []) + destinations.get(
|
||||
"CcAddresses", []) + destinations.get(
|
||||
"BccAddresses", [])
|
||||
alladdress = (
|
||||
destinations.get("ToAddresses", [])
|
||||
+ destinations.get("CcAddresses", [])
|
||||
+ destinations.get("BccAddresses", [])
|
||||
)
|
||||
for addr in alladdress:
|
||||
if SESFeedback.SUCCESS_ADDR in addr:
|
||||
return SESFeedback.DELIVERY
|
||||
|
|
@ -198,30 +185,29 @@ class SESBackend(BaseBackend):
|
|||
_, source_email_address = parseaddr(source)
|
||||
if source_email_address not in self.addresses:
|
||||
raise MessageRejectedError(
|
||||
"Did not have authority to send from email %s" % source_email_address
|
||||
"Did not have authority to send from email %s"
|
||||
% source_email_address
|
||||
)
|
||||
|
||||
recipient_count = len(destinations)
|
||||
message = email.message_from_string(raw_data)
|
||||
if source is None:
|
||||
if message['from'] is None:
|
||||
raise MessageRejectedError(
|
||||
"Source not specified"
|
||||
)
|
||||
if message["from"] is None:
|
||||
raise MessageRejectedError("Source not specified")
|
||||
|
||||
_, source_email_address = parseaddr(message['from'])
|
||||
_, source_email_address = parseaddr(message["from"])
|
||||
if source_email_address not in self.addresses:
|
||||
raise MessageRejectedError(
|
||||
"Did not have authority to send from email %s" % source_email_address
|
||||
"Did not have authority to send from email %s"
|
||||
% source_email_address
|
||||
)
|
||||
|
||||
for header in 'TO', 'CC', 'BCC':
|
||||
for header in "TO", "CC", "BCC":
|
||||
recipient_count += sum(
|
||||
d.strip() and 1 or 0
|
||||
for d in message.get(header, '').split(',')
|
||||
d.strip() and 1 or 0 for d in message.get(header, "").split(",")
|
||||
)
|
||||
if recipient_count > RECIPIENT_LIMIT:
|
||||
raise MessageRejectedError('Too many recipients.')
|
||||
raise MessageRejectedError("Too many recipients.")
|
||||
|
||||
self.__process_sns_feedback__(source, destinations, region)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ from .models import ses_backend
|
|||
|
||||
|
||||
class EmailResponse(BaseResponse):
|
||||
|
||||
def verify_email_identity(self):
|
||||
address = self.querystring.get('EmailAddress')[0]
|
||||
address = self.querystring.get("EmailAddress")[0]
|
||||
ses_backend.verify_email_identity(address)
|
||||
template = self.response_template(VERIFY_EMAIL_IDENTITY)
|
||||
return template.render()
|
||||
|
||||
def verify_email_address(self):
|
||||
address = self.querystring.get('EmailAddress')[0]
|
||||
address = self.querystring.get("EmailAddress")[0]
|
||||
ses_backend.verify_email_address(address)
|
||||
template = self.response_template(VERIFY_EMAIL_ADDRESS)
|
||||
return template.render()
|
||||
|
|
@ -32,94 +31,88 @@ class EmailResponse(BaseResponse):
|
|||
return template.render(email_addresses=email_addresses)
|
||||
|
||||
def verify_domain_dkim(self):
|
||||
domain = self.querystring.get('Domain')[0]
|
||||
domain = self.querystring.get("Domain")[0]
|
||||
ses_backend.verify_domain(domain)
|
||||
template = self.response_template(VERIFY_DOMAIN_DKIM_RESPONSE)
|
||||
return template.render()
|
||||
|
||||
def verify_domain_identity(self):
|
||||
domain = self.querystring.get('Domain')[0]
|
||||
domain = self.querystring.get("Domain")[0]
|
||||
ses_backend.verify_domain(domain)
|
||||
template = self.response_template(VERIFY_DOMAIN_IDENTITY_RESPONSE)
|
||||
return template.render()
|
||||
|
||||
def delete_identity(self):
|
||||
domain = self.querystring.get('Identity')[0]
|
||||
domain = self.querystring.get("Identity")[0]
|
||||
ses_backend.delete_identity(domain)
|
||||
template = self.response_template(DELETE_IDENTITY_RESPONSE)
|
||||
return template.render()
|
||||
|
||||
def send_email(self):
|
||||
bodydatakey = 'Message.Body.Text.Data'
|
||||
if 'Message.Body.Html.Data' in self.querystring:
|
||||
bodydatakey = 'Message.Body.Html.Data'
|
||||
bodydatakey = "Message.Body.Text.Data"
|
||||
if "Message.Body.Html.Data" in self.querystring:
|
||||
bodydatakey = "Message.Body.Html.Data"
|
||||
body = self.querystring.get(bodydatakey)[0]
|
||||
source = self.querystring.get('Source')[0]
|
||||
subject = self.querystring.get('Message.Subject.Data')[0]
|
||||
destinations = {
|
||||
'ToAddresses': [],
|
||||
'CcAddresses': [],
|
||||
'BccAddresses': [],
|
||||
}
|
||||
source = self.querystring.get("Source")[0]
|
||||
subject = self.querystring.get("Message.Subject.Data")[0]
|
||||
destinations = {"ToAddresses": [], "CcAddresses": [], "BccAddresses": []}
|
||||
for dest_type in destinations:
|
||||
# consume up to 51 to allow exception
|
||||
for i in six.moves.range(1, 52):
|
||||
field = 'Destination.%s.member.%s' % (dest_type, i)
|
||||
field = "Destination.%s.member.%s" % (dest_type, i)
|
||||
address = self.querystring.get(field)
|
||||
if address is None:
|
||||
break
|
||||
destinations[dest_type].append(address[0])
|
||||
|
||||
message = ses_backend.send_email(source, subject, body, destinations, self.region)
|
||||
message = ses_backend.send_email(
|
||||
source, subject, body, destinations, self.region
|
||||
)
|
||||
template = self.response_template(SEND_EMAIL_RESPONSE)
|
||||
return template.render(message=message)
|
||||
|
||||
def send_templated_email(self):
|
||||
source = self.querystring.get('Source')[0]
|
||||
template = self.querystring.get('Template')
|
||||
template_data = self.querystring.get('TemplateData')
|
||||
source = self.querystring.get("Source")[0]
|
||||
template = self.querystring.get("Template")
|
||||
template_data = self.querystring.get("TemplateData")
|
||||
|
||||
destinations = {
|
||||
'ToAddresses': [],
|
||||
'CcAddresses': [],
|
||||
'BccAddresses': [],
|
||||
}
|
||||
destinations = {"ToAddresses": [], "CcAddresses": [], "BccAddresses": []}
|
||||
for dest_type in destinations:
|
||||
# consume up to 51 to allow exception
|
||||
for i in six.moves.range(1, 52):
|
||||
field = 'Destination.%s.member.%s' % (dest_type, i)
|
||||
field = "Destination.%s.member.%s" % (dest_type, i)
|
||||
address = self.querystring.get(field)
|
||||
if address is None:
|
||||
break
|
||||
destinations[dest_type].append(address[0])
|
||||
|
||||
message = ses_backend.send_templated_email(source,
|
||||
template,
|
||||
template_data,
|
||||
destinations,
|
||||
self.region)
|
||||
message = ses_backend.send_templated_email(
|
||||
source, template, template_data, destinations, self.region
|
||||
)
|
||||
template = self.response_template(SEND_TEMPLATED_EMAIL_RESPONSE)
|
||||
return template.render(message=message)
|
||||
|
||||
def send_raw_email(self):
|
||||
source = self.querystring.get('Source')
|
||||
source = self.querystring.get("Source")
|
||||
if source is not None:
|
||||
source, = source
|
||||
(source,) = source
|
||||
|
||||
raw_data = self.querystring.get('RawMessage.Data')[0]
|
||||
raw_data = self.querystring.get("RawMessage.Data")[0]
|
||||
raw_data = base64.b64decode(raw_data)
|
||||
if six.PY3:
|
||||
raw_data = raw_data.decode('utf-8')
|
||||
raw_data = raw_data.decode("utf-8")
|
||||
destinations = []
|
||||
# consume up to 51 to allow exception
|
||||
for i in six.moves.range(1, 52):
|
||||
field = 'Destinations.member.%s' % i
|
||||
field = "Destinations.member.%s" % i
|
||||
address = self.querystring.get(field)
|
||||
if address is None:
|
||||
break
|
||||
destinations.append(address[0])
|
||||
|
||||
message = ses_backend.send_raw_email(source, destinations, raw_data, self.region)
|
||||
message = ses_backend.send_raw_email(
|
||||
source, destinations, raw_data, self.region
|
||||
)
|
||||
template = self.response_template(SEND_RAW_EMAIL_RESPONSE)
|
||||
return template.render(message=message)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,6 @@
|
|||
from __future__ import unicode_literals
|
||||
from .responses import EmailResponse
|
||||
|
||||
url_bases = [
|
||||
"https?://email.(.+).amazonaws.com",
|
||||
"https?://ses.(.+).amazonaws.com",
|
||||
]
|
||||
url_bases = ["https?://email.(.+).amazonaws.com", "https?://ses.(.+).amazonaws.com"]
|
||||
|
||||
url_paths = {
|
||||
'{0}/$': EmailResponse.dispatch,
|
||||
}
|
||||
url_paths = {"{0}/$": EmailResponse.dispatch}
|
||||
|
|
|
|||
|
|
@ -4,16 +4,16 @@ import string
|
|||
|
||||
|
||||
def random_hex(length):
|
||||
return ''.join(random.choice(string.ascii_lowercase) for x in range(length))
|
||||
return "".join(random.choice(string.ascii_lowercase) for x in range(length))
|
||||
|
||||
|
||||
def get_random_message_id():
|
||||
return "{0}-{1}-{2}-{3}-{4}-{5}-{6}".format(
|
||||
random_hex(16),
|
||||
random_hex(8),
|
||||
random_hex(4),
|
||||
random_hex(4),
|
||||
random_hex(4),
|
||||
random_hex(12),
|
||||
random_hex(6),
|
||||
random_hex(16),
|
||||
random_hex(8),
|
||||
random_hex(4),
|
||||
random_hex(4),
|
||||
random_hex(4),
|
||||
random_hex(12),
|
||||
random_hex(6),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue