-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#2307: send notification emails on changes to domain - [MS] #2827
base: main
Are you sure you want to change the base?
Conversation
🥳 Successfully deployed to developer sandbox ms. |
2 similar comments
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
if not hasattr(record, "server_time"): | ||
record.server_time = self.formatTime(record, self.datefmt) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was a minor fix to an edge case where this attribute doesn't exist, causing log formatting to revert back to its old "one log per \n" behavior. I can include it as a separate PR if we want, but thought it was small enough to sneak in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
@@ -118,6 +119,7 @@ def post(self, request, *args, **kwargs): | |||
if form.is_valid(): | |||
return self.form_valid(form) | |||
else: | |||
logger.debug(f"Form errors: {form.errors}") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not strictly necessary, but I found this super useful while debugging this PR so thought I might leave it in.
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
🥳 Successfully deployed to developer sandbox ms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work, I've noted a few changes that should be made though
@@ -61,11 +61,27 @@ def test_disable_email_flag(self): | |||
# Assert that an email wasn't sent | |||
self.assertFalse(self.mock_client.send_email.called) | |||
|
|||
@boto3_mocking.patching | |||
def test_email_with_cc(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(optional) Just in case, I'd also recommend test with the decorator @override_settings(IS_PRODUCTION=True)
and check the bcc_address as well as the cc_address The behaviour of that function changes when in production so it would catch any edge cases related to that
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): | ||
send_templated_email( | ||
"emails/update_to_approved_domain.txt", | ||
"emails/update_to_approved_domain_subject.txt", | ||
"doesnotexist@igorville.com", | ||
context={"domain": "test", "user": "test", "date": 1, "changes": "test"}, | ||
bcc_address=None, | ||
cc_addresses=["testy2@town.com", "mayor@igorville.gov"], | ||
) | ||
|
||
# check that an email was sent | ||
self.assertTrue(self.mock_client.send_email.called) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should also include a test here that checks on the content of cc_addresses.
We have a few tests that test on the bcc_addresses - should be mostly the same logic for it
@boto3_mocking.patching | ||
@less_console_noise_decorator | ||
def test_submission_confirmation(self): | ||
"""Submission confirmation email works.""" | ||
domain_request = completed_domain_request() | ||
domain_request = completed_domain_request(user=User.objects.create(username="test", email="testy@town.com")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Q) Why was this changed? Its not a bad thing though, just curious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default the completed_domain_request function creates a user with no email. Technically what was happening previously was that emails were being "sent" (the send_email function was being called) but without any to_address because when the domain request was submitted, it would grab an empty string from the user's email. That was a valid case previously (not sure if that was intentional) but now I added a check to make sure that there is some email address in to, bcc, or cc. As a result, these tests broke and to fix it I had to add a user object with an email.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely a bug! Good catch
self.security_contact, _ = PublicContact.objects.get_or_create( | ||
domain=self.domain, | ||
contact_type=PublicContact.ContactTypeChoices.SECURITY, | ||
email="security@igorville.gov", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you using this anywhere? If not can you delete it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, that was left over from a previous iteration of one of the tests! Good catch.
AllowedEmail(email="info@example.com"), | ||
AllowedEmail(email="doesnotexist@igorville.com"), | ||
] | ||
AllowedEmail.objects.bulk_create(allowed_emails) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice use of bulk_create
# forms of these types should not send notifications if they're part of a portfolio/Organization | ||
check_for_portfolio = { | ||
DomainOrgNameAddressForm, | ||
SeniorOfficialContactForm, | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Q) Why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(A) Because it was in the AC's :). Actually though, I think these cases will be handled separately in the org model work.
# don't notify for any other types of forms | ||
should_notify = False | ||
logger.info(f"Not notifying for {form.__class__}") | ||
if (should_notify and form.has_changed()) or force_send: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nitpick/optional) Personally, I think the should_notify variable in this context is moreso better described as can_notify
rather than should_notify
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually disagree, we technically can notify on changes with portfolios (as in, there's no technical barrier) but we have made a decision that we shouldn't. I like the attention to naming though! Good naming is important for whatever unfortunate soul has to maintain this code later on!
"changes": form_label_dict[form.__class__], | ||
} | ||
self.email_domain_managers( | ||
self.object, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
self.object is typically the model itself.
Later on its being used as domain_name
. Is self.object a string in this context?
Will log a warning if the email fails to send for any reason, but will not raise an error. | ||
""" | ||
try: | ||
domain = Domain.objects.get(name=domain_name) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Per above, I think self.object should already be the object assuming its not just a string.
I'd suggest grabbing that directly rather than a filter. If that returns none then you can log as normal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I agree. This works technically because (I think) when you treat a domain model as a string it uses the name, but it's clunky at best.
Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
🥳 Successfully deployed to developer sandbox ms. |
…/cisagov/manage.get.gov into ms/2307-send-notification-emails
🥳 Successfully deployed to developer sandbox ms. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works! One thing I noticed though is that I am receiving the email by being directly CC'd rather than it just being to my address, I assume that is not intentional?
Not blocking the review on it, but just be sure to resolve it given that design is in agreement (since they'll need to review this anyhow)
Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
@Matt-Spence Fair enough |
🥳 Successfully deployed to developer sandbox ms. |
Ticket
Resolves #2307
Changes
Context for reviewers
Setup
Code Review Verification Steps
Go to getgov-ms and add yourself as a domain manager to any domain. Make sure your email is in the whitelist, then go to that domain and make changes. Observe whether emails are sent, and their content.
Repeat for various permutations (multiple domain managers, some managers in/out of whitelist, domains in/out of portfolios, etc).
As the original developer, I have
Satisfied acceptance criteria and met development standards
Ensured code standards are met (Original Developer)
Validated user-facing changes (if applicable)
As a code reviewer, I have
Reviewed, tested, and left feedback about the changes
Ensured code standards are met (Code reviewer)
Validated user-facing changes as a developer
New pages have been added to .pa11yci file so that they will be tested with our automated accessibility testing
Checked keyboard navigability
Meets all designs and user flows provided by design/product
Tested general usability, landmarks, page header structure, and links with a screen reader (such as Voiceover or ANDI)
Tested with multiple browsers, the suggestion is to use ones that the developer didn't (check off which ones were used)
(Rarely needed) Tested as both an analyst and applicant user
Note: Multiple code reviewers can share the checklists above, a second reviewers should not make a duplicate checklist
As a designer reviewer, I have
Verified that the changes match the design intention
Validated user-facing changes as a designer
Checked keyboard navigability
Tested general usability, landmarks, page header structure, and links with a screen reader (such as Voiceover or ANDI)
Tested with multiple browsers (check off which ones were used)
(Rarely needed) Tested as both an analyst and applicant user
Screenshots