Skip to content

Commit

Permalink
Handle missing runtime files and update for Python review
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidSouther committed Apr 1, 2024
1 parent f2e2186 commit 8fbacdd
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ public void prepareApplication() throws IOException {
// snippet-start:[sesv2.java2.newsletter.CreateEmailTemplate]
try {
// Create an email template named "weekly-coupons"
String newsletterHtml = Files.readString(Paths.get("resources/coupon_newsletter/coupon-newsletter.html"));
String newsletterText = Files.readString(Paths.get("resources/coupon_newsletter/coupon-newsletter.txt"));
String newsletterHtml = loadFile("resources/coupon_newsletter/coupon-newsletter.html");
String newsletterText = loadFile("resources/coupon_newsletter/coupon-newsletter.txt");

CreateEmailTemplateRequest templateRequest = CreateEmailTemplateRequest.builder()
.templateName(TEMPLATE_NAME)
Expand Down Expand Up @@ -164,6 +164,14 @@ public void prepareApplication() throws IOException {
// snippet-end:[sesv2.java2.newsletter.CreateEmailTemplate]
}

private String loadFile(String path) {
try {
return Files.readString(Paths.get(path));
} catch (IOException ioe) {
return "Missing " + path;
}
}

/**
* Helper method to create subscriber subaddresses.
*
Expand Down
25 changes: 15 additions & 10 deletions python/example_code/sesv2/newsletter.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ def load_file_content(file_path):
Returns:
str: The content of the file.
"""
with open(file_path, "r") as file:
content = file.read()
return content
try:
with open(file_path, "r") as file:
content = file.read()
return content
except Exception:
return f"Missing {file_path}"


def print_error(error):
Expand Down Expand Up @@ -209,7 +212,7 @@ def send_coupon_newsletter(self):
email_address = contact["EmailAddress"]
try:
# snippet-start:[python.example_code.sesv2.SendEmail.template]
send = self.ses_client.send_email(
self.ses_client.send_email(
FromEmailAddress=self.verified_email,
Destination={"ToAddresses": [email_address]},
Content={
Expand All @@ -222,7 +225,6 @@ def send_coupon_newsletter(self):
)
# snippet-end:[python.example_code.sesv2.SendEmail.template]
print(f"Newsletter sent to '{email_address}'.")
print("Debug: ", send)
if self.sleep:
# 1 email per second in sandbox mode, remove in production.
sleep(1.1)
Expand All @@ -234,12 +236,15 @@ def monitor_and_review(self):
Provides instructions for monitoring sending activity in the AWS console.
"""
print(
"To monitor your sending activity, please visit the SES Homepage in the AWS console:"
)
print("https://console.aws.amazon.com/ses/home#/account")
print(
"From there, you can view various dashboards and metrics related to your newsletter campaign."
"""
To monitor your sending activity, please visit the SES Homepage in the AWS console:
https://console.aws.amazon.com/ses/home#/account
From there, you can view various dashboards and metrics related to your newsletter campaign.
"""
)

input("Press enter to continue.")

def clean_up(self):
Expand Down
79 changes: 41 additions & 38 deletions python/example_code/sesv2/newsletter_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# SPDX-License-Identifier: Apache-2.0

import json
import unittest
import sys
from botocore.exceptions import ClientError
from io import StringIO
import pytest
from unittest.mock import patch

from python.example_code.sesv2.newsletter import (
from newsletter import (
SESv2Workflow,
get_subaddress_variants,
CONTACT_LIST_NAME,
Expand All @@ -18,9 +18,9 @@
# Run tests with `python -m unittest`


class TestSESv2WorkflowPrepareApplication(unittest.TestCase):
@patch("main.boto3.client")
def setUp(self, mock_client):
class TestSESv2WorkflowPrepareApplication:
@patch("boto3.client")
def setup_method(self, method, mock_client):
self.ses_client = mock_client()
self.workflow = SESv2Workflow(self.ses_client)

Expand All @@ -36,7 +36,7 @@ def test_prepare_application_success(self, mock_input):
sys.stdout = sys.__stdout__ # Reset standard output

expected_output = "Email identity 'verified@example.com' created successfully.\nContact list 'weekly-coupons-newsletter' created successfully.\nEmail template 'weekly-coupons' created successfully.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="verified@example.com")
def test_prepare_application_error_identity_already_exists(self, mock_input):
Expand All @@ -52,7 +52,7 @@ def test_prepare_application_error_identity_already_exists(self, mock_input):
sys.stdout = sys.__stdout__

expected_output = "Email identity 'verified@example.com' already exists.\nContact list 'weekly-coupons-newsletter' created successfully.\nEmail template 'weekly-coupons' created successfully.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="invalid@example.com")
def test_prepare_application_error_identity_not_found(self, mock_input):
Expand All @@ -64,12 +64,12 @@ def test_prepare_application_error_identity_not_found(self, mock_input):

captured_output = StringIO()
sys.stdout = captured_output
with self.assertRaises(ClientError):
with pytest.raises(ClientError):
self.workflow.prepare_application()
sys.stdout = sys.__stdout__

expected_output = ""
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="verified@example.com")
def test_prepare_application_error_identity_limit_exceeded(self, mock_input):
Expand All @@ -81,12 +81,12 @@ def test_prepare_application_error_identity_limit_exceeded(self, mock_input):

captured_output = StringIO()
sys.stdout = captured_output
with self.assertRaises(ClientError):
with pytest.raises(ClientError):
self.workflow.prepare_application()
sys.stdout = sys.__stdout__

expected_output = ""
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="verified@example.com")
def test_prepare_application_error_contact_list_limit_exceeded(self, mock_input):
Expand All @@ -98,21 +98,21 @@ def test_prepare_application_error_contact_list_limit_exceeded(self, mock_input)

captured_output = StringIO()
sys.stdout = captured_output
with self.assertRaises(ClientError):
with pytest.raises(ClientError):
self.workflow.prepare_application()
sys.stdout = sys.__stdout__

expected_output = (
"Email identity 'verified@example.com' created successfully.\n"
)
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output


class TestSESv2WorkflowGatherSubscribers(unittest.TestCase):
@patch("main.boto3.client")
def setUp(self, mock_client):
class TestSESv2WorkflowGatherSubscribers:
@patch("boto3.client")
def setup_method(self, method, mock_client):
self.ses_client = mock_client()
self.workflow = SESv2Workflow(self.ses_client, sleep=False)
self.workflow = SESv2Workflow(self.ses_client)
self.workflow.verified_email = "verified@example.com"

# Tests for gather_subscriber_email_addresses
Expand All @@ -136,7 +136,7 @@ def test_gather_subscriber_email_addresses_success(self, mock_input):
)
+ "\n"
)
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="user@example.com")
def test_gather_subscriber_email_addresses_error_contact_exists(self, mock_input):
Expand All @@ -157,7 +157,7 @@ def test_gather_subscriber_email_addresses_error_contact_exists(self, mock_input

email_variants = get_subaddress_variants("user@example.com", 3)
expected_output = f"Contact with email '{email_variants[0]}' created successfully.\nWelcome email sent to '{email_variants[0]}'.\nContact with email '{email_variants[1]}' created successfully.\nWelcome email sent to '{email_variants[1]}'.\nContact with email '{email_variants[2]}' already exists. Skipping...\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="user@example.com")
def test_gather_subscriber_email_addresses_error_send_email_failed(
Expand All @@ -175,25 +175,25 @@ def test_gather_subscriber_email_addresses_error_send_email_failed(

captured_output = StringIO()
sys.stdout = captured_output
with self.assertRaises(ClientError):
with pytest.raises(ClientError):
self.workflow.gather_subscriber_email_addresses()
sys.stdout = sys.__stdout__

email_variants = get_subaddress_variants("user@example.com", 3)
expected_output = f"Contact with email '{email_variants[0]}' created successfully.\nWelcome email sent to '{email_variants[0]}'.\nContact with email '{email_variants[1]}' created successfully.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output


class TestSESv2WorkflowSendCouponNewsletter(unittest.TestCase):
@patch("main.boto3.client")
def setUp(self, mock_client):
class TestSESv2WorkflowSendCouponNewsletter:
@patch("boto3.client")
def setup_method(self, method, mock_client):
self.ses_client = mock_client()
self.workflow = SESv2Workflow(self.ses_client, sleep=False)
self.workflow = SESv2Workflow(self.ses_client)
self.workflow.verified_email = "verified@example.com"

# Tests for send_coupon_newsletter
@patch(
"main.load_file_content",
"newsletter.load_file_content",
side_effect=[
"<html>Template Content</html>",
"Plain Text Template Content",
Expand All @@ -216,7 +216,7 @@ def test_send_coupon_newsletter_success(self, mock_load_file_content):
sys.stdout = sys.__stdout__

expected_output = "Newsletter sent to 'user+1@example.com'.\nNewsletter sent to 'user+2@example.com'.\nNewsletter sent to 'user+3@example.com'.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

def test_send_coupon_newsletter_error_contact_list_not_found(self):
self.ses_client.list_contacts.side_effect = ClientError(
Expand All @@ -230,9 +230,12 @@ def test_send_coupon_newsletter_error_contact_list_not_found(self):
sys.stdout = sys.__stdout__

expected_output = f"Contact list '{CONTACT_LIST_NAME}' does not exist.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("main.load_file_content", return_value=json.dumps(["Coupon 1", "Coupon 2"]))
@patch(
"newsletter.load_file_content",
return_value=json.dumps(["Coupon 1", "Coupon 2"]),
)
def test_send_coupon_newsletter_error_send_email_failed(
self, mock_load_file_content
):
Expand All @@ -258,14 +261,14 @@ def test_send_coupon_newsletter_error_send_email_failed(
sys.stdout = sys.__stdout__

expected_output = "Newsletter sent to 'user1@example.com'.\nError: An error occurred (MessageRejected) when calling the SendEmail operation: Unknown\nNewsletter sent to 'user3@example.com'.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output


class TestSESv2WorkflowCleanUp(unittest.TestCase):
@patch("main.boto3.client")
def setUp(self, mock_client):
class TestSESv2WorkflowCleanUp:
@patch("boto3.client")
def setup_method(self, method, mock_client):
self.ses_client = mock_client()
self.workflow = SESv2Workflow(self.ses_client, sleep=False)
self.workflow = SESv2Workflow(self.ses_client)
self.workflow.verified_email = "verified@example.com"

# Tests for clean_up
Expand All @@ -281,7 +284,7 @@ def test_clean_up_success(self, mock_input):
sys.stdout = sys.__stdout__

expected_output = f"Contact list '{CONTACT_LIST_NAME}' deleted successfully.\nEmail template '{TEMPLATE_NAME}' deleted successfully.\nSkipping email identity deletion.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="n")
def test_clean_up_error_contact_list_not_found(self, mock_input):
Expand All @@ -298,7 +301,7 @@ def test_clean_up_error_contact_list_not_found(self, mock_input):
sys.stdout = sys.__stdout__

expected_output = f"Contact list '{CONTACT_LIST_NAME}' does not exist.\nEmail template '{TEMPLATE_NAME}' deleted successfully.\nSkipping email identity deletion.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="n")
def test_clean_up_error_template_not_found(self, mock_input):
Expand All @@ -315,7 +318,7 @@ def test_clean_up_error_template_not_found(self, mock_input):
sys.stdout = sys.__stdout__

expected_output = f"Contact list '{CONTACT_LIST_NAME}' deleted successfully.\nEmail template '{TEMPLATE_NAME}' does not exist.\nSkipping email identity deletion.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output

@patch("builtins.input", return_value="y")
def test_clean_up_error_identity_not_found(self, mock_input):
Expand All @@ -332,4 +335,4 @@ def test_clean_up_error_identity_not_found(self, mock_input):
sys.stdout = sys.__stdout__

expected_output = f"Contact list '{CONTACT_LIST_NAME}' deleted successfully.\nEmail template '{TEMPLATE_NAME}' deleted successfully.\nEmail identity '{self.workflow.verified_email}' does not exist.\n"
self.assertEqual(captured_output.getvalue(), expected_output)
assert captured_output.getvalue() == expected_output
27 changes: 17 additions & 10 deletions rustv1/examples/ses/src/newsletter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,18 @@ impl<'a> SESWorkflow<'a> {
// snippet-end:[sesv2.rust.create-contact-list]

// snippet-start:[sesv2.rust.create-email-template]
let TEMPLATE_HTML: &str = include_str!("../resources/newsletter/coupon-newsletter.html");
let TEMPLATE_TEXT: &str = include_str!("../resources/newsletter/coupon-newsletter.txt");
let template_html =
std::fs::read_to_string("../resources/newsletter/coupon-newsletter.html")
.unwrap_or_else(|_| "Missing coupon-newsletter.html".to_string());
let template_text =
std::fs::read_to_string("../resources/newsletter/coupon-newsletter.txt")
.unwrap_or_else(|_| "Missing coupon-newsletter.txt".to_string());

// Create the email template
let template_content = EmailTemplateContent::builder()
.subject("Weekly Coupons Newsletter")
.html(TEMPLATE_HTML)
.text(TEMPLATE_TEXT)
.html(template_html)
.text(template_text)
.build();

match self
Expand Down Expand Up @@ -176,8 +180,10 @@ impl<'a> SESWorkflow<'a> {

// Send the welcome email
// snippet-start:[sesv2.rust.send-email.simple]
let WELCOME_HTML: &str = include_str!("../resources/newsletter/welcome.html");
let WELCOME_TXT: &str = include_str!("../resources/newsletter/welcome.txt");
let welcome_html = std::fs::read_to_string("../resources/newsletter/welcome.html")
.unwrap_or_else(|_| "Missing welcome.html".to_string());
let welcome_txt = std::fs::read_to_string("../resources/newsletter/welcome.txt")
.unwrap_or_else(|_| "Missing welcome.txt".to_string());
let email_content = EmailContent::builder()
.simple(
Message::builder()
Expand All @@ -188,8 +194,8 @@ impl<'a> SESWorkflow<'a> {
)
.body(
Body::builder()
.html(Content::builder().data(WELCOME_HTML).build()?)
.text(Content::builder().data(WELCOME_TXT).build()?)
.html(Content::builder().data(welcome_html).build()?)
.text(Content::builder().data(welcome_txt).build()?)
.build(),
)
.build(),
Expand Down Expand Up @@ -253,12 +259,13 @@ impl<'a> SESWorkflow<'a> {
let email = email.email_address.unwrap();

// snippet-start:[sesv2.rust.send-email.template]
let COUPONS: &str = include_str!("../resources/newsletter/sample_coupons.json");
let coupons = std::fs::read_to_string("../resources/newsletter/sample_coupons.json")
.unwrap_or_else(|_| r#"{"coupons":[]}"#.to_string());
let email_content = EmailContent::builder()
.template(
Template::builder()
.template_name(TEMPLATE_NAME)
.template_data(COUPONS)
.template_data(coupons)
.build(),
)
.build();
Expand Down

0 comments on commit 8fbacdd

Please sign in to comment.