diff --git a/moto/ses/exceptions.py b/moto/ses/exceptions.py index c15473188..7a4ef1b03 100644 --- a/moto/ses/exceptions.py +++ b/moto/ses/exceptions.py @@ -25,3 +25,19 @@ class EventDestinationAlreadyExists(RESTError): super(EventDestinationAlreadyExists, self).__init__( "EventDestinationAlreadyExists", message ) + + +class TemplateNameAlreadyExists(RESTError): + code = 400 + + def __init__(self, message): + super(TemplateNameAlreadyExists, self).__init__( + "TemplateNameAlreadyExists", message + ) + + +class TemplateDoesNotExist(RESTError): + code = 400 + + def __init__(self, message): + super(TemplateDoesNotExist, self).__init__("TemplateDoesNotExist", message) diff --git a/moto/ses/models.py b/moto/ses/models.py index d141e25ae..6c3eb219a 100644 --- a/moto/ses/models.py +++ b/moto/ses/models.py @@ -10,6 +10,8 @@ from .exceptions import ( MessageRejectedError, ConfigurationSetDoesNotExist, EventDestinationAlreadyExists, + TemplateNameAlreadyExists, + TemplateDoesNotExist, ) from .utils import get_random_message_id from .feedback import COMMON_MAIL, BOUNCE, COMPLAINT, DELIVERY @@ -91,6 +93,7 @@ class SESBackend(BaseBackend): self.config_set = {} self.config_set_event_destination = {} self.event_destinations = {} + self.templates = {} def _is_verified_address(self, source): _, address = parseaddr(source) @@ -277,5 +280,19 @@ class SESBackend(BaseBackend): statistics["Timestamp"] = datetime.datetime.utcnow() return statistics + def add_template(self, template_info): + template_name = template_info["template_name"] + if self.templates.get(template_name, None): + raise TemplateNameAlreadyExists("Duplicate Template Name.") + self.templates[template_name] = template_info + + def get_template(self, template_name): + if not self.templates.get(template_name, None): + raise TemplateDoesNotExist("Invalid Template Name.") + return self.templates[template_name] + + def list_templates(self): + return list(self.templates.values()) + ses_backend = SESBackend() diff --git a/moto/ses/responses.py b/moto/ses/responses.py index 8c9dc8f75..f0780e98a 100644 --- a/moto/ses/responses.py +++ b/moto/ses/responses.py @@ -5,6 +5,7 @@ import six from moto.core.responses import BaseResponse from .models import ses_backend +from datetime import datetime class EmailResponse(BaseResponse): @@ -175,6 +176,29 @@ class EmailResponse(BaseResponse): template = self.response_template(CREATE_CONFIGURATION_SET_EVENT_DESTINATION) return template.render() + 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["Timestamp"] = datetime.utcnow() + ses_backend.add_template(template_info=template_info) + template = self.response_template(CREATE_TEMPLATE) + return template.render() + + def get_template(self): + template_name = self._get_param("TemplateName") + template_data = ses_backend.get_template(template_name) + template = self.response_template(GET_TEMPLATE) + return template.render(template_data=template_data) + + def list_templates(self): + email_templates = ses_backend.list_templates() + template = self.response_template(LIST_TEMPLATES) + return template.render(templates=email_templates) + VERIFY_EMAIL_IDENTITY = """ @@ -324,3 +348,40 @@ CREATE_CONFIGURATION_SET_EVENT_DESTINATION = """67e0ef1a-9bf2-11e1-9279-0100e8cf109a """ + +CREATE_TEMPLATE = """ + + + 47e0ef1a-9bf2-11e1-9279-0100e8cf12ba + +""" + +GET_TEMPLATE = """ + + + + + 47e0ef1a-9bf2-11e1-9279-0100e8cf12ba + +""" + +LIST_TEMPLATES = """ + + + {% for template in templates %} + + {{ template["template_name"] }} + {{ template["Timestamp"] }} + + {% endfor %} + + + + 47e0ef1a-9bf2-11e1-9279-0100e8cf12ba + +""" diff --git a/tests/test_ses/test_ses_boto3.py b/tests/test_ses/test_ses_boto3.py index 0e6bb9bea..a94612077 100644 --- a/tests/test_ses/test_ses_boto3.py +++ b/tests/test_ses/test_ses_boto3.py @@ -277,3 +277,46 @@ def test_create_configuration_set(): ) ex.exception.response["Error"]["Code"].should.equal("EventDestinationAlreadyExists") + + +@mock_ses +def test_create_ses_template(): + conn = boto3.client("ses", region_name="us-east-1") + + conn.create_template( + Template={ + "TemplateName": "MyTemplate", + "SubjectPart": "Greetings, {{name}}!", + "TextPart": "Dear {{name}}," + "\r\nYour favorite animal is {{favoriteanimal}}.", + "HtmlPart": "

Hello {{name}}," + "

Your favorite animal is {{favoriteanimal}}.

", + } + ) + with assert_raises(ClientError) as ex: + conn.create_template( + Template={ + "TemplateName": "MyTemplate", + "SubjectPart": "Greetings, {{name}}!", + "TextPart": "Dear {{name}}," + "\r\nYour favorite animal is {{favoriteanimal}}.", + "HtmlPart": "

Hello {{name}}," + "

Your favorite animal is {{favoriteanimal}}.

", + } + ) + + ex.exception.response["Error"]["Code"].should.equal("TemplateNameAlreadyExists") + + # get a template which is already added + result = conn.get_template(TemplateName="MyTemplate") + result["Template"]["TemplateName"].should.equal("MyTemplate") + result["Template"]["SubjectPart"].should.equal("Greetings, {{name}}!") + + # get a template which is not present + with assert_raises(ClientError) as ex: + conn.get_template(TemplateName="MyFakeTemplate") + + ex.exception.response["Error"]["Code"].should.equal("TemplateDoesNotExist") + + result = conn.list_templates() + result["TemplatesMetadata"][0]["Name"].should.equal("MyTemplate")