0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-11 15:51:19 +00:00

Update senddeletionscheduled to also notify over configured channels

This commit is contained in:
Pēteris Caune 2023-08-21 15:38:11 +03:00
parent a7395115db
commit 455dc66ce2
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
2 changed files with 81 additions and 1 deletions

View file

@ -1,6 +1,7 @@
from __future__ import annotations
import time
from datetime import timedelta as td
from django.conf import settings
from django.contrib.auth.models import User
@ -8,6 +9,7 @@ from django.core.management.base import BaseCommand
from django.utils.timezone import now
from hc.accounts.models import Profile
from hc.api.models import Channel, Check
from hc.lib import emails
@ -22,6 +24,38 @@ class Command(BaseCommand):
q = q.exclude(last_login=None)
return q.order_by("email")
def send_channel_notifications(self, profile, skip_emails):
# Sending deletion notices to configured notification channels is
# a last ditch effort: only do this if 14 or fewer days are left.
delta = profile.deletion_scheduled_date - now()
if delta.days > 14:
return
formatted = profile.deletion_scheduled_date.strftime("%B %-d, %Y")
name = f"{settings.SITE_NAME} Account Deletion on {formatted}"
desc = (
f"The {settings.SITE_NAME} account registered to {profile.user.email} "
f"is scheduled for deletion on {formatted}. To keep the account, "
f"please contact {settings.SUPPORT_EMAIL} ASAP."
)
for channel in Channel.objects.filter(project__owner_id=profile.user_id):
if channel.kind == "email" and channel.email_value in skip_emails:
continue
dummy = Check(name=name, desc=desc, status="down", project=channel.project)
dummy.last_ping = now() - td(days=1)
dummy.n_pings = 1
self.stdout.write(f" * Sending notification to {channel.kind}")
error = channel.notify(dummy, is_test=True)
if error == "no-op":
# This channel may be configured to send "up" notifications only.
dummy.status = "up"
error = channel.notify(dummy, is_test=True)
if error:
self.stdout.write(f" Error sending notification: {error}")
def handle(self, *args, **options):
q = Profile.objects.order_by("id")
q = q.filter(deletion_scheduled_date__gt=now())
@ -42,6 +76,7 @@ class Command(BaseCommand):
"deletion_scheduled_date": profile.deletion_scheduled_date,
}
emails.deletion_scheduled(recipients, ctx)
self.send_channel_notifications(profile, skip_emails=recipients)
sent += 1
# Throttle so we don't send too many emails at once:

View file

@ -5,11 +5,12 @@ from datetime import timedelta as td
from unittest.mock import Mock
from django.core import mail
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.accounts.management.commands.senddeletionscheduled import Command
from hc.accounts.models import Member, Project
from hc.api.models import Check
from hc.api.models import Channel, Check
from hc.test import BaseTestCase
@ -18,7 +19,16 @@ def counts(result):
return [int(s) for s in re.findall(r"\d+", result)]
@override_settings(SITE_NAME="Mychecks")
class SendDeletionScheduledTestCase(BaseTestCase):
def setUp(self):
super().setUp()
self.channel = Channel(project=self.project, kind="email")
self.channel.value = "alerts@example.org"
self.channel.email_verified = True
self.channel.save()
def test_it_sends_notice(self):
self.profile.deletion_scheduled_date = now() + td(days=31)
self.profile.save()
@ -90,3 +100,38 @@ class SendDeletionScheduledTestCase(BaseTestCase):
cmd.handle()
# Bob should be listed as a recipient a single time, despite two memberships:
self.assertEqual(mail.outbox[0].to, ["alice@example.org", "bob@example.org"])
def test_it_notifies_channel(self):
self.profile.deletion_scheduled_date = now() + td(days=5)
self.profile.save()
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s
cmd.handle()
self.assertEqual(mail.outbox[0].subject, "Account Deletion Warning")
s = "DOWN | Mychecks Account Deletion"
self.assertTrue(mail.outbox[1].subject.startswith(s))
def test_it_does_not_notify_channels_if_more_than_14_days_left(self):
self.profile.deletion_scheduled_date = now() + td(days=15, minutes=1)
self.profile.save()
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s
cmd.handle()
self.assertEqual(len(mail.outbox), 1)
def test_it_skips_email_channels_of_team_members(self):
self.profile.deletion_scheduled_date = now() + td(days=5)
self.profile.save()
self.channel.value = "alice@example.org"
self.channel.save()
cmd = Command(stdout=Mock())
cmd.pause = Mock() # don't pause for 1s
cmd.handle()
self.assertEqual(len(mail.outbox), 1)