Send invitation email when invite is created

This commit is contained in:
Neil Martinsen-Burrell 2023-03-27 15:20:08 -05:00
parent 1e77bd9bf6
commit 8338704315
No known key found for this signature in database
GPG key ID: 6A3C818CC10D0184
4 changed files with 64 additions and 7 deletions

View file

@ -469,7 +469,9 @@ class DomainApplication(TimeStampedModel):
nothing.
"""
if self.submitter is None or self.submitter.email is None:
logger.warning("Cannot send confirmation email, no submitter email address.")
logger.warning(
"Cannot send confirmation email, no submitter email address."
)
return
try:
send_templated_email(

View file

@ -0,0 +1,6 @@
You have been invited to manage a domain on get.gov, the registrar for
.gov domain names.
To accept your invitation, go to <{{ domain_url }}>.
You will need to log in with a Login.gov account using this email address.

View file

@ -1,4 +1,5 @@
from unittest import skip
from unittest.mock import MagicMock, ANY
from django.conf import settings
from django.test import Client, TestCase
@ -557,9 +558,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
# the post request should return a redirect to the contact
# question
self.assertEqual(election_result.status_code, 302)
self.assertEqual(
election_result["Location"], "/register/organization_contact/"
)
self.assertEqual(election_result["Location"], "/register/organization_contact/")
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
contact_page = election_result.follow()
self.assertNotContains(contact_page, "Federal agency")
@ -1139,8 +1138,13 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
success_page = success_result.follow()
self.assertContains(success_page, "mayor@igorville.gov")
@boto3_mocking.patching
def test_domain_invitation_created(self):
"""Add user on a nonexistent email creates an invitation."""
"""Add user on a nonexistent email creates an invitation.
Adding a non-existent user sends an email as a side-effect, so mock
out the boto3 SES email sending here.
"""
# make sure there is no user with this email
EMAIL = "mayor@igorville.gov"
User.objects.filter(email=EMAIL).delete()
@ -1159,10 +1163,36 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
self.assertContains(success_page, "Cancel") # link to cancel invitation
self.assertTrue(DomainInvitation.objects.filter(email=EMAIL).exists())
@boto3_mocking.patching
def test_domain_invitation_email_sent(self):
"""Inviting a non-existent user sends them an email."""
# make sure there is no user with this email
EMAIL = "mayor@igorville.gov"
User.objects.filter(email=EMAIL).delete()
mock_client = MagicMock()
mock_client_instance = mock_client.return_value
with boto3_mocking.clients.handler_for("sesv2", mock_client):
add_page = self.app.get(
reverse("domain-users-add", kwargs={"pk": self.domain.id})
)
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
add_page.form["email"] = EMAIL
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
add_page.form.submit()
# check the mock instance to see if `send_email` was called right
mock_client_instance.send_email.assert_called_once_with(
FromEmailAddress=settings.DEFAULT_FROM_EMAIL,
Destination={"ToAddresses": [EMAIL]},
Content=ANY,
)
def test_domain_invitation_cancel(self):
"""Posting to the delete view deletes an invitation."""
EMAIL = "mayor@igorville.gov"
invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=EMAIL)
invitation, _ = DomainInvitation.objects.get_or_create(
domain=self.domain, email=EMAIL
)
self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}))
with self.assertRaises(DomainInvitation.DoesNotExist):
DomainInvitation.objects.get(id=invitation.id)

View file

@ -11,6 +11,7 @@ from django.views.generic.edit import DeleteView, FormMixin
from registrar.models import Domain, DomainInvitation, User, UserDomainRole
from ..forms import DomainAddUserForm
from ..utility.email import send_templated_email, EmailSendingError
from .utility import DomainPermission
@ -56,6 +57,12 @@ class DomainAddUserView(DomainPermission, FormMixin, DetailView):
else:
return self.form_invalid(form)
def _domain_abs_url(self):
"""Get an absolute URL for this domain."""
return self.request.build_absolute_uri(
reverse("domain", kwargs={"pk": self.object.id})
)
def _make_invitation(self, email_address):
"""Make a Domain invitation for this email and redirect with a message."""
invitation, created = DomainInvitation.objects.get_or_create(
@ -68,7 +75,19 @@ class DomainAddUserView(DomainPermission, FormMixin, DetailView):
f"{email_address} has already been invited to this domain.",
)
else:
messages.success(self.request, f"Invited {email_address} to this domain.")
# created a new invitation in the database, so send an email
try:
send_templated_email(
"emails/domain_invitation.txt",
to_address=email_address,
context={"domain_url": self._domain_abs_url()},
)
except EmailSendingError:
messages.warning(self.request, "Could not send email invitation.")
else:
messages.success(
self.request, f"Invited {email_address} to this domain."
)
return redirect(self.get_success_url())
def form_valid(self, form):