Email business logic

This commit is contained in:
Rebecca Hsieh 2024-02-29 22:17:48 -08:00
parent 8148b90996
commit 74f2034487
No known key found for this signature in database
3 changed files with 64 additions and 72 deletions

View file

@ -3,7 +3,7 @@
1. Check the [Pipfile](../../../src/Pipfile) for pinned dependencies and manually adjust the version numbers
2. Run `docker-compose stop` to spin down the current containers and images so we can start afresh
2. Run
3. Run
cd src
docker-compose run app bash -c "pipenv lock && pipenv requirements > requirements.txt"
@ -13,9 +13,9 @@
It is necessary to use `bash -c` because `run pipenv requirements` will not recognize that it is running non-interactively and will include garbage formatting characters.
The requirements.txt is used by Cloud.gov. It is needed to work around a bug in the CloudFoundry buildpack version of Pipenv that breaks on installing from a git repository.
3. Change geventconnpool back to what it was originally within the Pipfile.lock and requirements.txt.
4. Change geventconnpool back to what it was originally within the Pipfile.lock and requirements.txt.
This is done by either saving what it was originally or opening a PR and using that as a reference to undo changes to any mention of geventconnpool.
Geventconnpool, when set as a requirement without the reference portion, is defaulting to get a commit from 2014 which then breaks the code, as we want the newest version from them.
4. Run `docker-compose build` to build a new image for local development with the updated dependencies.
5. Run `docker-compose build` to build a new image for local development with the updated dependencies.
The reason for de-coupling the `build` and `lock` steps is to increase consistency between builds--a run of `build` will always get exactly the dependencies listed in `Pipfile.lock`, nothing more, nothing less.

View file

@ -71,17 +71,23 @@ class Command(BaseCommand):
# Secret is encrypted into getgov-credentials
# TODO: Update secret in getgov-credentials via cloud.gov and my own .env when ready
# encrypted_metadata is the encrypted output
# Encrypt the metadata
# TODO: UPDATE SECRET_ENCRYPT_METADATA pw getgov-credentials on stable
encrypted_metadata = self._encrypt_metadata(s3_client.get_file(file_name), encrypted_metadata_output, str.encode(settings.SECRET_ENCRYPT_METADATA))
print("encrypted_metadata is:", encrypted_metadata)
print("the type is: ", type(encrypted_metadata))
# Send the metadata file that is zipped
# Q: Would we set the vars I set in email.py here to pass in to the helper function or best way to invoke
# send_templated_email(encrypted_metadata, attachment=True)
# TODO: Make new .txt files
send_templated_email(
"emails/metadata_body.txt",
"emails/metadata_subject.txt",
to_address="rebecca.hsieh@truss.works", # TODO: Update to settings.DEFAULT_FROM_EMAIL once tested
file=encrypted_metadata,
)
def _encrypt_metadata(self, input_file, output_file, password):
# Using ZIP_DEFLATED bc it's a more common compression method supported by most zip utilities
# Could also use compression=pyzipper.ZIP_LZMA?
# Using ZIP_DEFLATED bc it's a more common compression method supported by most zip utilities and faster
# We could also use compression=pyzipper.ZIP_LZMA if we are looking for smaller file size
with pyzipper.AESZipFile(output_file, 'w', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as f_out:
f_out.setpassword(password)
f_out.writestr('encrypted_metadata.txt', input_file)

View file

@ -4,6 +4,10 @@ import boto3
import logging
from django.conf import settings
from django.template.loader import get_template
from email.mime.base import MIMEBase
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
logger = logging.getLogger(__name__)
@ -15,7 +19,7 @@ class EmailSendingError(RuntimeError):
pass
def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}):
def send_templated_email(template_name: str, subject_template_name: str, to_address: str, context={}, file: str=None):
"""Send an email built from a template to one email address.
template_name and subject_template_name are relative to the same template
@ -40,74 +44,56 @@ def send_templated_email(template_name: str, subject_template_name: str, to_addr
except Exception as exc:
raise EmailSendingError("Could not access the SES client.") from exc
# Are we okay with passing in "attachment" var in as boolean parameter
# If so, TODO: add attachment boolean to other functions
try:
#if not attachment:
ses_client.send_email(
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
Destination={"ToAddresses": [to_address]},
Content={
"Simple": {
"Subject": {"Data": subject},
"Body": {"Text": {"Data": email_body}},
if file is None:
ses_client.send_email(
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
Destination={"ToAddresses": [to_address]},
Content={
"Simple": {
"Subject": {"Data": subject},
"Body": {"Text": {"Data": email_body}},
},
},
},
)
# else: # has attachment
# same as above but figure out how to attach a file
# via boto3 "boto3 SES file attachment"
# we also want this to only send to the help email
# from email.mime.multipart import MIMEMultipart
# from email.mime.text import MIMEText
# from email.mime.application import MIMEApplication
)
if file is not None:
# TODO: Update sender email when we figure out
ses_client = boto3.client(
"ses",
region_name=settings.AWS_REGION,
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
config=settings.BOTO_CONFIG,
)
# sender_email = 'sender@example.com'
# recipient_email = 'help@get.gov'
# subject = 'DOTGOV-Full Domain Metadata'
# body = 'Domain metadata email, should have an attachment included change here later.'
# attachment_path = 'path/to/attachment/file.pdf'
# aws_region = 'sesv2'
# response = send_email_with_attachment(sender_email, recipient_email, subject, body, attachment_path, aws_region)
# print(response)
#TODO: Update sender to settings.DEFAULT_FROM_EMAIL
response = send_email_with_attachment(settings.DEFAULT_FROM_EMAIL, to_address, subject, email_body, file, ses_client)
print("Response from send_email_with_attachment_is:", response)
except Exception as exc:
raise EmailSendingError("Could not send SES email.") from exc
def send_email_with_attachment(sender, recipient, subject, body, attachment_file, ses_client):
# Create a multipart/mixed parent container
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = recipient
# def send_email_with_attachment(sender, recipient, subject, body, attachment_path, aws_region):
# # Create a multipart/mixed parent container
# msg = MIMEMultipart('mixed')
# msg['Subject'] = subject
# msg['From'] = sender_email
# msg['To'] = recipient_email
# Add the text part
text_part = MIMEText(body, 'plain')
msg.attach(text_part)
# # Add the text part
# text_part = MIMEText(body, 'plain')
# msg.attach(text_part)
# Add the attachment part
# # Add the attachment part
# with open(attachment_path, 'rb') as attachment_file:
# attachment_data = attachment_file.read()
# attachment_part = MIMEApplication(attachment_data)
# attachment_part.add_header('Content-Disposition', f'attachment; filename="{attachment_path}"')
# msg.attach(attachment_part)
# set it into this "type"
attachment_part = MIMEApplication(attachment_file)
# Adding attachment header + filename that the attachment will be called
attachment_part.add_header('Content-Disposition', f'attachment; filename="encrypted_metadata.zip"')
msg.attach(attachment_part)
# # Send the email
# response = ses_client.send_raw_email(
# Source=sender,
# Destinations=[recipient],
# RawMessage={'Data': msg.as_string()}
# )
# ses_client.send_email(
# FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
# Destination={"ToAddresses": [to_address]},
# Content={
# "Simple": {
# "Subject": {"Data": subject},
# "Body": {"Text": {"Data": email_body}},
# },
# },
# )
response = ses_client.send_raw_email(
Source=sender,
Destinations=[recipient],
RawMessage={"Data": msg.as_string()}
)
return response