Skip to content

Commit 0e77982

Browse files
authored
feat(rulesnooze): Don't notify if rule is snoozed (#47197)
This pr adds the removal of notifications for rules where a RuleSnooze exists. This handles both cases of individual snoozing and everyone having the alert snoozed. Closes #46842
1 parent faebb64 commit 0e77982

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

src/sentry/notifications/notifications/rules.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def get_participants(self) -> Mapping[ExternalProviders, Iterable[RpcActor]]:
7373
event=self.event,
7474
notification_type=self.notification_setting_type,
7575
fallthrough_choice=self.fallthrough_choice,
76+
rules=self.rules,
7677
)
7778

7879
def get_subject(self, context: Mapping[str, Any] | None = None) -> str:

src/sentry/notifications/utils/participants.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from collections import defaultdict
55
from typing import TYPE_CHECKING, Any, Iterable, List, Mapping, MutableMapping, Sequence, Tuple
66

7+
from django.db.models import Q
8+
79
from sentry import features
810
from sentry.models import (
911
ActorTuple,
@@ -16,6 +18,8 @@
1618
Project,
1719
ProjectOwnership,
1820
Release,
21+
Rule,
22+
RuleSnooze,
1923
Team,
2024
User,
2125
)
@@ -384,10 +388,25 @@ def get_send_to(
384388
event: Event | None = None,
385389
notification_type: NotificationSettingTypes = NotificationSettingTypes.ISSUE_ALERTS,
386390
fallthrough_choice: FallthroughChoiceType | None = None,
391+
rules: Iterable[Rule] | None = None,
387392
) -> Mapping[ExternalProviders, set[RpcActor]]:
388393
recipients = determine_eligible_recipients(
389394
project, target_type, target_identifier, event, fallthrough_choice
390395
)
396+
397+
if rules:
398+
rule_snoozes = RuleSnooze.objects.filter(Q(rule__in=rules))
399+
muted_user_ids = []
400+
for rule_snooze in rule_snoozes:
401+
if rule_snooze.user_id is None:
402+
return {}
403+
else:
404+
muted_user_ids.append(rule_snooze.user_id)
405+
406+
if muted_user_ids:
407+
recipients = filter(
408+
lambda x: x.actor_type != ActorType.USER or x.id not in muted_user_ids, recipients
409+
)
391410
return get_recipients_by_provider(project, recipients, notification_type)
392411

393412

tests/sentry/mail/test_adapter.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
ProjectOwnership,
3232
Repository,
3333
Rule,
34+
RuleSnooze,
3435
User,
3536
UserEmail,
3637
UserOption,
@@ -48,7 +49,7 @@
4849
from sentry.plugins.base import Notification
4950
from sentry.services.hybrid_cloud.actor import RpcActor
5051
from sentry.testutils import TestCase
51-
from sentry.testutils.helpers import override_options
52+
from sentry.testutils.helpers import override_options, with_feature
5253
from sentry.testutils.helpers.datetime import before_now, iso_format
5354
from sentry.testutils.silo import region_silo_test
5455
from sentry.types.activity import ActivityType
@@ -183,6 +184,82 @@ def test_simple_notification(self):
183184
assert msg.subject == "[Sentry] BAR-1 - Hello world"
184185
assert "my rule" in msg.alternatives[0][0]
185186

187+
@with_feature("organizations:mute-alerts")
188+
def test_simple_snooze(self):
189+
"""Test that notification for alert snoozed by user is not send to that user."""
190+
event = self.store_event(
191+
data={"message": "Hello world", "level": "error"}, project_id=self.project.id
192+
)
193+
194+
rule = self.create_project_rule(project=self.project)
195+
RuleSnooze.objects.create(user_id=self.user.id, owner_id=self.user.id, rule=rule)
196+
ProjectOwnership.objects.create(project_id=self.project.id, fallthrough=True)
197+
198+
notification = Notification(event=event, rule=rule)
199+
200+
with self.options({"system.url-prefix": "http://example.com"}), self.tasks():
201+
self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS)
202+
203+
assert len(mail.outbox) == 0
204+
205+
@with_feature("organizations:mute-alerts")
206+
def test_snooze_for_all(self):
207+
"""Test that notification for alert snoozed for everyone is not send to user."""
208+
event = self.store_event(
209+
data={"message": "Hello world", "level": "error"}, project_id=self.project.id
210+
)
211+
212+
rule = self.create_project_rule(project=self.project)
213+
RuleSnooze.objects.create(owner_id=self.user.id, rule=rule)
214+
ProjectOwnership.objects.create(project_id=self.project.id, fallthrough=True)
215+
216+
notification = Notification(event=event, rule=rule)
217+
218+
with self.options({"system.url-prefix": "http://example.com"}), self.tasks():
219+
self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS)
220+
221+
assert len(mail.outbox) == 0
222+
223+
@with_feature("organizations:mute-alerts")
224+
def test_someone_else_snoozes_themself(self):
225+
"""Test that notification for alert snoozed by user2 for themself is sent to user"""
226+
event = self.store_event(
227+
data={"message": "Hello world", "level": "error"}, project_id=self.project.id
228+
)
229+
230+
rule = self.create_project_rule(project=self.project)
231+
user2 = self.create_user(email="[email protected]")
232+
RuleSnooze.objects.create(user_id=user2.id, owner_id=user2.id, rule=rule)
233+
ProjectOwnership.objects.create(project_id=self.project.id, fallthrough=True)
234+
235+
notification = Notification(event=event, rule=rule)
236+
237+
with self.options({"system.url-prefix": "http://example.com"}), self.tasks():
238+
self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS)
239+
240+
assert len(mail.outbox) == 1
241+
msg = mail.outbox[0]
242+
assert msg.subject == "[Sentry] BAR-1 - Hello world"
243+
244+
@with_feature("organizations:mute-alerts")
245+
def test_someone_else_snoozes_everyone(self):
246+
"""Test that notification for alert snoozed by user2 for everyone is not sent to user"""
247+
event = self.store_event(
248+
data={"message": "Hello world", "level": "error"}, project_id=self.project.id
249+
)
250+
251+
rule = self.create_project_rule(project=self.project)
252+
user2 = self.create_user(email="[email protected]")
253+
RuleSnooze.objects.create(owner_id=user2.id, rule=rule)
254+
ProjectOwnership.objects.create(project_id=self.project.id, fallthrough=True)
255+
256+
notification = Notification(event=event, rule=rule)
257+
258+
with self.options({"system.url-prefix": "http://example.com"}), self.tasks():
259+
self.adapter.notify(notification, ActionTargetType.ISSUE_OWNERS)
260+
261+
assert len(mail.outbox) == 0
262+
186263
def test_simple_notification_generic(self):
187264
"""Test that an issue that is neither error nor performance type renders a generic email template"""
188265
event = self.store_event(

0 commit comments

Comments
 (0)