We covered so much in this chapter and the last step is to build the emailing functionality which will notify donors from a specific city whenever there’s a new blood donation event planned in their city.
As described in the chapter title, we will be using AWS Simple Email Service to send out emails because it is easy to use
from boto3
and offers 62.000 emails for free per month.
The solution roughly looks like this, and we are now at the middle step, which will batch up to 50 email addresses (a limitation of AWS SES) and send a message to them.
Even though using SES is relatively simple with boto3
, the service itself comes with certain limitations when you first
start using it. AWS calls this the “sandbox mode”.
From the docs:
In a sandbox environment, you can use all of the features offered by Amazon SES; however, certain sending limits and restrictions apply. When you’re ready to move out of the sandbox, submit a request for production access
In practice this means that:
FROM
email addressAll of these limitations are good enough for the purposes of this workshop. I have verified an identity in advance and it will be provided to you.
I was never a great developer so StackOverflow helped me on this one.
Create the chalicelib/utils.py
file and add in it
def chunk_list(lst, chunk_size):
for i in range(0, len(lst), chunk_size):
yield lst[i:i + chunk_size]
This will allow us to split a list lst
with many elements into even chunks of chunk_size
. This is how it works:
We can also write a test for this function. Your time to shine again.
Run pytest
to make sure it is included and passing:
And finally, batching those email addresses can be done in this way:
from chalicelib.utils import chunk_list
# ... SNIP ...
@app.on_dynamodb_record(stream_arn=stream_arn)
def handle_stream(event):
# ... SNIP ...
city_name = stream_data.get("city").get("S")
db_response = get_app_db().donors_by_city(city_name)
all_emails = [elem.get("email") for elem in db_response.return_value]
batched_emails = list(chunk_list(all_emails, 50))
app.log.debug(f"Gathered '{len(all_emails)}' from donors")
for email_batch in batched_emails:
# send email
Boto3 documentation for sending an email with SES is clear and explicit (it even lists certain limitations of AWS SES sandbox mode).
It shows us how to use the SES client to send emails and that is exactly what we will do. Below are the necessary code changes:
import boto3
# ... SNIP ...
ses_client = boto3.client("ses")
@app.on_dynamodb_record(stream_arn=stream_arn)
def handle_stream(event):
# ... SNIP ...
batched_emails = list(chunk_list(all_emails, 50))
app.log.debug(f"Gathered '{len(all_emails)}' from donors")
for email_batch in batched_emails:
ses_client.send_email(
Source="TO_BE_PROVIDED",
Destination={
"ToAddresses": email_batch
},
Message={
"Subject": {
"Data": f"New blood donation event in {city_name}",
"Charset": "UTF-8",
},
"Body": {
"Html": {
"Data": f"<p>Dear donor,</p><p>There is a new blood donation event happening in {city_name}.</p><p>Come join us!</p>",
"Charset": "UTF-8",
}
}
}
)
As always, we have to allow our function to send emails. This is done by appending the following statement to the
policy-handle-stream.json
file:
{
"Action": [
"ses:SendEmail"
],
"Resource": [
"arn:aws:ses:eu-central-1:932785857088:identity/TO_BE_PROVIDED"
],
"Effect": "Allow"
}
Deploy everything and add a new donation in Haarlem
chalice deploy
# ... SNIP ...
http -b POST $(chalice url)/donation/create city=Haarlem address="Other street" datetime="2022-04-06T12:00:00"
If you made it this far, congratulations 👍 It hasn’t been easy for sure.