Functionality added to SES service (#3670)

* correct exceptions when mising parameters

* test_render_template function

* update ses template function

* fix import

* except fixed

* tests and py2 fix
This commit is contained in:
Cristopher Pinzón 2021-02-11 13:31:17 -05:00 committed by GitHub
parent 4a01360d88
commit c72670d536
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 211 additions and 5 deletions

View File

@ -36,6 +36,29 @@ class TemplateNameAlreadyExists(RESTError):
) )
class ValidationError(RESTError):
code = 400
def __init__(self, message):
super(ValidationError, self).__init__("ValidationError", message)
class InvalidParameterValue(RESTError):
code = 400
def __init__(self, message):
super(InvalidParameterValue, self).__init__("InvalidParameterValue", message)
class InvalidRenderingParameterException:
code = 400
def __init__(self, message):
super(InvalidRenderingParameterException, self).__init__(
"InvalidRenderingParameterException", message
)
class TemplateDoesNotExist(RESTError): class TemplateDoesNotExist(RESTError):
code = 400 code = 400

View File

@ -1,8 +1,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import datetime import json
import email import email
import datetime
from email.mime.base import MIMEBase
from email.utils import parseaddr from email.utils import parseaddr
from email.mime.multipart import MIMEMultipart
from email.encoders import encode_7or8bit
from moto.core import BaseBackend, BaseModel from moto.core import BaseBackend, BaseModel
from moto.sns.models import sns_backends from moto.sns.models import sns_backends
@ -11,6 +15,9 @@ from .exceptions import (
ConfigurationSetDoesNotExist, ConfigurationSetDoesNotExist,
EventDestinationAlreadyExists, EventDestinationAlreadyExists,
TemplateNameAlreadyExists, TemplateNameAlreadyExists,
ValidationError,
InvalidParameterValue,
InvalidRenderingParameterException,
TemplateDoesNotExist, TemplateDoesNotExist,
RuleSetNameAlreadyExists, RuleSetNameAlreadyExists,
RuleSetDoesNotExist, RuleSetDoesNotExist,
@ -288,8 +295,36 @@ class SESBackend(BaseBackend):
def add_template(self, template_info): def add_template(self, template_info):
template_name = template_info["template_name"] template_name = template_info["template_name"]
if not template_name:
raise ValidationError(
"1 validation error detected: "
"Value null at 'template.templateName'"
"failed to satisfy constraint: Member must not be null"
)
if self.templates.get(template_name, None): if self.templates.get(template_name, None):
raise TemplateNameAlreadyExists("Duplicate Template Name.") raise TemplateNameAlreadyExists("Duplicate Template Name.")
template_subject = template_info["subject_part"]
if not template_subject:
raise InvalidParameterValue("The subject must be specified.")
self.templates[template_name] = template_info
def update_template(self, template_info):
template_name = template_info["template_name"]
if not template_name:
raise ValidationError(
"1 validation error detected: "
"Value null at 'template.templateName'"
"failed to satisfy constraint: Member must not be null"
)
if not self.templates.get(template_name, None):
raise TemplateDoesNotExist("Invalid Template Name.")
template_subject = template_info["subject_part"]
if not template_subject:
raise InvalidParameterValue("The subject must be specified.")
self.templates[template_name] = template_info self.templates[template_name] = template_info
def get_template(self, template_name): def get_template(self, template_name):
@ -300,6 +335,50 @@ class SESBackend(BaseBackend):
def list_templates(self): def list_templates(self):
return list(self.templates.values()) return list(self.templates.values())
def render_template(self, render_data):
template_name = render_data.get("name", "")
template = self.templates.get(template_name, None)
if not template:
raise TemplateDoesNotExist("Invalid Template Name.")
template_data = render_data.get("data")
try:
template_data = json.loads(template_data)
except ValueError:
raise InvalidRenderingParameterException(
"Template rendering data is invalid"
)
subject_part = template["subject_part"]
text_part = template["text_part"]
html_part = template["html_part"]
for key, value in template_data.items():
subject_part = str.replace(str(subject_part), "{{%s}}" % key, value)
text_part = str.replace(str(text_part), "{{%s}}" % key, value)
html_part = str.replace(str(html_part), "{{%s}}" % key, value)
email = MIMEMultipart("alternative")
mime_text = MIMEBase("text", "plain;charset=UTF-8")
mime_text.set_payload(text_part.encode("utf-8"))
encode_7or8bit(mime_text)
email.attach(mime_text)
mime_html = MIMEBase("text", "html;charset=UTF-8")
mime_html.set_payload(html_part.encode("utf-8"))
encode_7or8bit(mime_html)
email.attach(mime_html)
now = datetime.datetime.now().isoformat()
rendered_template = "Date: %s\r\nSubject: %s\r\n%s" % (
now,
subject_part,
email.as_string(),
)
return rendered_template
def create_receipt_rule_set(self, rule_set_name): def create_receipt_rule_set(self, rule_set_name):
if self.receipt_rule_set.get(rule_set_name) is not None: if self.receipt_rule_set.get(rule_set_name) is not None:
raise RuleSetNameAlreadyExists("Duplicate receipt rule set Name.") raise RuleSetNameAlreadyExists("Duplicate receipt rule set Name.")

View File

@ -179,15 +179,27 @@ class EmailResponse(BaseResponse):
def create_template(self): def create_template(self):
template_data = self._get_dict_param("Template") template_data = self._get_dict_param("Template")
template_info = {} template_info = {}
template_info["text_part"] = template_data["._text_part"] template_info["text_part"] = template_data.get("._text_part", "")
template_info["html_part"] = template_data["._html_part"] template_info["html_part"] = template_data.get("._html_part", "")
template_info["template_name"] = template_data["._name"] template_info["template_name"] = template_data.get("._name", "")
template_info["subject_part"] = template_data["._subject_part"] template_info["subject_part"] = template_data.get("._subject_part", "")
template_info["Timestamp"] = datetime.utcnow() template_info["Timestamp"] = datetime.utcnow()
ses_backend.add_template(template_info=template_info) ses_backend.add_template(template_info=template_info)
template = self.response_template(CREATE_TEMPLATE) template = self.response_template(CREATE_TEMPLATE)
return template.render() return template.render()
def update_template(self):
template_data = self._get_dict_param("Template")
template_info = {}
template_info["text_part"] = template_data.get("._text_part", "")
template_info["html_part"] = template_data.get("._html_part", "")
template_info["template_name"] = template_data.get("._name", "")
template_info["subject_part"] = template_data.get("._subject_part", "")
template_info["Timestamp"] = datetime.utcnow()
ses_backend.update_template(template_info=template_info)
template = self.response_template(UPDATE_TEMPLATE)
return template.render()
def get_template(self): def get_template(self):
template_name = self._get_param("TemplateName") template_name = self._get_param("TemplateName")
template_data = ses_backend.get_template(template_name) template_data = ses_backend.get_template(template_name)
@ -199,6 +211,12 @@ class EmailResponse(BaseResponse):
template = self.response_template(LIST_TEMPLATES) template = self.response_template(LIST_TEMPLATES)
return template.render(templates=email_templates) return template.render(templates=email_templates)
def test_render_template(self):
render_info = self._get_dict_param("Template")
rendered_template = ses_backend.render_template(render_info)
template = self.response_template(RENDER_TEMPLATE)
return template.render(template=rendered_template)
def create_receipt_rule_set(self): def create_receipt_rule_set(self):
rule_set_name = self._get_param("RuleSetName") rule_set_name = self._get_param("RuleSetName")
ses_backend.create_receipt_rule_set(rule_set_name) ses_backend.create_receipt_rule_set(rule_set_name)
@ -369,6 +387,13 @@ CREATE_TEMPLATE = """<CreateTemplateResponse xmlns="http://ses.amazonaws.com/doc
</ResponseMetadata> </ResponseMetadata>
</CreateTemplateResponse>""" </CreateTemplateResponse>"""
UPDATE_TEMPLATE = """<UpdateTemplateResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<UpdateTemplateResult/>
<ResponseMetadata>
<RequestId>47e0ef1a-9bf2-11e1-9279-0100e8cf12ba</RequestId>
</ResponseMetadata>
</UpdateTemplateResponse>"""
GET_TEMPLATE = """<GetTemplateResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/"> GET_TEMPLATE = """<GetTemplateResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<GetTemplateResult> <GetTemplateResult>
<Template> <Template>
@ -383,6 +408,7 @@ GET_TEMPLATE = """<GetTemplateResponse xmlns="http://ses.amazonaws.com/doc/2010-
</ResponseMetadata> </ResponseMetadata>
</GetTemplateResponse>""" </GetTemplateResponse>"""
LIST_TEMPLATES = """<ListTemplatesResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/"> LIST_TEMPLATES = """<ListTemplatesResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<ListTemplatesResult> <ListTemplatesResult>
<TemplatesMetadata> <TemplatesMetadata>
@ -399,6 +425,19 @@ LIST_TEMPLATES = """<ListTemplatesResponse xmlns="http://ses.amazonaws.com/doc/2
</ResponseMetadata> </ResponseMetadata>
</ListTemplatesResponse>""" </ListTemplatesResponse>"""
RENDER_TEMPLATE = """
<TestRenderTemplateResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<TestRenderTemplateResult>
<RenderedTemplate>
{{template | e}}
</RenderedTemplate>
</TestRenderTemplateResult>
<ResponseMetadata>
<RequestId>47e0ef1a-9bf2-11e1-9279-0100e8cf12ba</RequestId>
</ResponseMetadata>
</TestRenderTemplateResponse>
"""
CREATE_RECEIPT_RULE_SET = """<CreateReceiptRuleSetResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/"> CREATE_RECEIPT_RULE_SET = """<CreateReceiptRuleSetResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<CreateReceiptRuleSetResult/> <CreateReceiptRuleSetResult/>
<ResponseMetadata> <ResponseMetadata>

View File

@ -1,4 +1,5 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import json
import boto3 import boto3
from botocore.exceptions import ClientError from botocore.exceptions import ClientError
@ -484,3 +485,67 @@ def test_create_ses_template():
result = conn.list_templates() result = conn.list_templates()
result["TemplatesMetadata"][0]["Name"].should.equal("MyTemplate") result["TemplatesMetadata"][0]["Name"].should.equal("MyTemplate")
@mock_ses
def test_render_template():
conn = boto3.client("ses", region_name="us-east-1")
kwargs = dict(
TemplateName="MyTestTemplate",
TemplateData=json.dumps({"name": "John", "favoriteanimal": "Lion"}),
)
with pytest.raises(ClientError) as ex:
conn.test_render_template(**kwargs)
ex.value.response["Error"]["Code"].should.equal("TemplateDoesNotExist")
conn.create_template(
Template={
"TemplateName": "MyTestTemplate",
"SubjectPart": "Greetings, {{name}}!",
"TextPart": "Dear {{name}},"
"\r\nYour favorite animal is {{favoriteanimal}}.",
"HtmlPart": "<h1>Hello {{name}},"
"</h1><p>Your favorite animal is {{favoriteanimal}}.</p>",
}
)
result = conn.test_render_template(**kwargs)
result["RenderedTemplate"].should.contain("Subject: Greetings, John!")
result["RenderedTemplate"].should.contain("Dear John,")
result["RenderedTemplate"].should.contain("<h1>Hello John,</h1>")
result["RenderedTemplate"].should.contain("Your favorite animal is Lion")
@mock_ses
def test_update_ses_template():
conn = boto3.client("ses", region_name="us-east-1")
template = {
"TemplateName": "MyTemplateToUpdate",
"SubjectPart": "Greetings, {{name}}!",
"TextPart": "Dear {{name}}," "\r\nYour favorite animal is {{favoriteanimal}}.",
"HtmlPart": "<h1>Hello {{name}},"
"</h1><p>Your favorite animal is {{favoriteanimal}}.</p>",
}
with pytest.raises(ClientError) as ex:
conn.update_template(Template=template)
ex.value.response["Error"]["Code"].should.equal("TemplateDoesNotExist")
conn.create_template(Template=template)
template["SubjectPart"] = "Hi, {{name}}!"
template["TextPart"] = "Dear {{name}},\r\n Your favorite color is {{color}}"
template[
"HtmlPart"
] = "<h1>Hello {{name}},</h1><p>Your favorite color is {{color}}</p>"
conn.update_template(Template=template)
result = conn.get_template(TemplateName=template["TemplateName"])
result["Template"]["SubjectPart"].should.equal("Hi, {{name}}!")
result["Template"]["TextPart"].should.equal(
"Dear {{name}},\n Your favorite color is {{color}}"
)
result["Template"]["HtmlPart"].should.equal(
"<h1>Hello {{name}},</h1><p>Your favorite color is {{color}}</p>"
)