Skip to content

Commit

Permalink
Functionality added to SES service (getmoto#3670)
Browse files Browse the repository at this point in the history
* correct exceptions when mising parameters

* test_render_template function

* update ses template function

* fix import

* except fixed

* tests and py2 fix
  • Loading branch information
pinzon authored Feb 11, 2021
1 parent 4a01360 commit c72670d
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 5 deletions.
23 changes: 23 additions & 0 deletions moto/ses/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,29 @@ def __init__(self, message):
)


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):
code = 400

Expand Down
81 changes: 80 additions & 1 deletion moto/ses/models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from __future__ import unicode_literals

import datetime
import json
import email
import datetime
from email.mime.base import MIMEBase
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.sns.models import sns_backends
Expand All @@ -11,6 +15,9 @@
ConfigurationSetDoesNotExist,
EventDestinationAlreadyExists,
TemplateNameAlreadyExists,
ValidationError,
InvalidParameterValue,
InvalidRenderingParameterException,
TemplateDoesNotExist,
RuleSetNameAlreadyExists,
RuleSetDoesNotExist,
Expand Down Expand Up @@ -288,8 +295,36 @@ def get_send_statistics(self):

def add_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 self.templates.get(template_name, None):
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

def get_template(self, template_name):
Expand All @@ -300,6 +335,50 @@ def get_template(self, template_name):
def list_templates(self):
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):
if self.receipt_rule_set.get(rule_set_name) is not None:
raise RuleSetNameAlreadyExists("Duplicate receipt rule set Name.")
Expand Down
47 changes: 43 additions & 4 deletions moto/ses/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,27 @@ def create_configuration_set_event_destination(self):
def create_template(self):
template_data = self._get_dict_param("Template")
template_info = {}
template_info["text_part"] = template_data["._text_part"]
template_info["html_part"] = template_data["._html_part"]
template_info["template_name"] = template_data["._name"]
template_info["subject_part"] = template_data["._subject_part"]
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.add_template(template_info=template_info)
template = self.response_template(CREATE_TEMPLATE)
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):
template_name = self._get_param("TemplateName")
template_data = ses_backend.get_template(template_name)
Expand All @@ -199,6 +211,12 @@ def list_templates(self):
template = self.response_template(LIST_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):
rule_set_name = self._get_param("RuleSetName")
ses_backend.create_receipt_rule_set(rule_set_name)
Expand Down Expand Up @@ -369,6 +387,13 @@ def create_receipt_rule(self):
</ResponseMetadata>
</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/">
<GetTemplateResult>
<Template>
Expand All @@ -383,6 +408,7 @@ def create_receipt_rule(self):
</ResponseMetadata>
</GetTemplateResponse>"""


LIST_TEMPLATES = """<ListTemplatesResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">
<ListTemplatesResult>
<TemplatesMetadata>
Expand All @@ -399,6 +425,19 @@ def create_receipt_rule(self):
</ResponseMetadata>
</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/">
<CreateReceiptRuleSetResult/>
<ResponseMetadata>
Expand Down
65 changes: 65 additions & 0 deletions tests/test_ses/test_ses_boto3.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
import json

import boto3
from botocore.exceptions import ClientError
Expand Down Expand Up @@ -484,3 +485,67 @@ def test_create_ses_template():

result = conn.list_templates()
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>"
)

0 comments on commit c72670d

Please sign in to comment.