mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-25 12:08:40 +02:00
Email business logic
This commit is contained in:
parent
8148b90996
commit
74f2034487
3 changed files with 64 additions and 72 deletions
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
1. Check the [Pipfile](../../../src/Pipfile) for pinned dependencies and manually adjust the version numbers
|
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 `docker-compose stop` to spin down the current containers and images so we can start afresh
|
||||||
2. Run
|
3. Run
|
||||||
|
|
||||||
cd src
|
cd src
|
||||||
docker-compose run app bash -c "pipenv lock && pipenv requirements > requirements.txt"
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
|
@ -71,17 +71,23 @@ class Command(BaseCommand):
|
||||||
# Secret is encrypted into getgov-credentials
|
# Secret is encrypted into getgov-credentials
|
||||||
# TODO: Update secret in getgov-credentials via cloud.gov and my own .env when ready
|
# 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))
|
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("encrypted_metadata is:", encrypted_metadata)
|
||||||
|
print("the type is: ", type(encrypted_metadata))
|
||||||
# Send the metadata file that is zipped
|
# 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
|
# TODO: Make new .txt files
|
||||||
# send_templated_email(encrypted_metadata, attachment=True)
|
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):
|
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
|
# Using ZIP_DEFLATED bc it's a more common compression method supported by most zip utilities and faster
|
||||||
# Could also use compression=pyzipper.ZIP_LZMA?
|
# 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:
|
with pyzipper.AESZipFile(output_file, 'w', compression=pyzipper.ZIP_DEFLATED, encryption=pyzipper.WZ_AES) as f_out:
|
||||||
f_out.setpassword(password)
|
f_out.setpassword(password)
|
||||||
f_out.writestr('encrypted_metadata.txt', input_file)
|
f_out.writestr('encrypted_metadata.txt', input_file)
|
||||||
|
|
|
@ -4,6 +4,10 @@ import boto3
|
||||||
import logging
|
import logging
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template.loader import get_template
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -15,7 +19,7 @@ class EmailSendingError(RuntimeError):
|
||||||
pass
|
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.
|
"""Send an email built from a template to one email address.
|
||||||
|
|
||||||
template_name and subject_template_name are relative to the same template
|
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:
|
except Exception as exc:
|
||||||
raise EmailSendingError("Could not access the SES client.") from 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:
|
try:
|
||||||
#if not attachment:
|
if file is None:
|
||||||
ses_client.send_email(
|
ses_client.send_email(
|
||||||
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
||||||
Destination={"ToAddresses": [to_address]},
|
Destination={"ToAddresses": [to_address]},
|
||||||
Content={
|
Content={
|
||||||
"Simple": {
|
"Simple": {
|
||||||
"Subject": {"Data": subject},
|
"Subject": {"Data": subject},
|
||||||
"Body": {"Text": {"Data": email_body}},
|
"Body": {"Text": {"Data": email_body}},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
)
|
||||||
)
|
if file is not None:
|
||||||
# else: # has attachment
|
# TODO: Update sender email when we figure out
|
||||||
# same as above but figure out how to attach a file
|
ses_client = boto3.client(
|
||||||
# via boto3 "boto3 SES file attachment"
|
"ses",
|
||||||
# we also want this to only send to the help email
|
region_name=settings.AWS_REGION,
|
||||||
|
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
|
||||||
# from email.mime.multipart import MIMEMultipart
|
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
|
||||||
# from email.mime.text import MIMEText
|
config=settings.BOTO_CONFIG,
|
||||||
# from email.mime.application import MIMEApplication
|
)
|
||||||
|
|
||||||
# sender_email = 'sender@example.com'
|
#TODO: Update sender to settings.DEFAULT_FROM_EMAIL
|
||||||
# recipient_email = 'help@get.gov'
|
response = send_email_with_attachment(settings.DEFAULT_FROM_EMAIL, to_address, subject, email_body, file, ses_client)
|
||||||
# subject = 'DOTGOV-Full Domain Metadata'
|
print("Response from send_email_with_attachment_is:", response)
|
||||||
# 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)
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise EmailSendingError("Could not send SES email.") from 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):
|
# Add the text part
|
||||||
# # Create a multipart/mixed parent container
|
text_part = MIMEText(body, 'plain')
|
||||||
# msg = MIMEMultipart('mixed')
|
msg.attach(text_part)
|
||||||
# msg['Subject'] = subject
|
|
||||||
# msg['From'] = sender_email
|
|
||||||
# msg['To'] = recipient_email
|
|
||||||
|
|
||||||
# # Add the text part
|
# Add the attachment part
|
||||||
# text_part = MIMEText(body, 'plain')
|
|
||||||
# msg.attach(text_part)
|
|
||||||
|
|
||||||
# # Add the attachment part
|
# set it into this "type"
|
||||||
# with open(attachment_path, 'rb') as attachment_file:
|
attachment_part = MIMEApplication(attachment_file)
|
||||||
# attachment_data = attachment_file.read()
|
# Adding attachment header + filename that the attachment will be called
|
||||||
# attachment_part = MIMEApplication(attachment_data)
|
attachment_part.add_header('Content-Disposition', f'attachment; filename="encrypted_metadata.zip"')
|
||||||
# attachment_part.add_header('Content-Disposition', f'attachment; filename="{attachment_path}"')
|
msg.attach(attachment_part)
|
||||||
# msg.attach(attachment_part)
|
|
||||||
|
|
||||||
# # Send the email
|
response = ses_client.send_raw_email(
|
||||||
# response = ses_client.send_raw_email(
|
Source=sender,
|
||||||
# Source=sender,
|
Destinations=[recipient],
|
||||||
# Destinations=[recipient],
|
RawMessage={"Data": msg.as_string()}
|
||||||
# RawMessage={'Data': msg.as_string()}
|
)
|
||||||
# )
|
return response
|
||||||
|
|
||||||
# ses_client.send_email(
|
|
||||||
# FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
|
|
||||||
# Destination={"ToAddresses": [to_address]},
|
|
||||||
# Content={
|
|
||||||
# "Simple": {
|
|
||||||
# "Subject": {"Data": subject},
|
|
||||||
# "Body": {"Text": {"Data": email_body}},
|
|
||||||
# },
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue