Browse Source

feat(alerts): Mute metric alerts (#50986)

Check the `RuleSnooze` table before firing metric alerts that email a
user or team so as to not fire the alert if it's been snoozed.

Closes #50579
Colleen O'Rourke 1 year ago
parent
commit
2762308531

+ 15 - 1
src/sentry/incidents/action_handlers.py

@@ -19,6 +19,7 @@ from sentry.incidents.models import (
     IncidentStatus,
     TriggerStatus,
 )
+from sentry.models import RuleSnooze
 from sentry.models.notificationsetting import NotificationSetting
 from sentry.models.options.user_option import UserOption
 from sentry.models.user import User
@@ -68,7 +69,17 @@ class EmailActionHandler(ActionHandler):
         if not target:
             return set()
 
+        if RuleSnooze.objects.filter(
+            alert_rule=self.incident.alert_rule, user_id__isnull=True
+        ).exists():
+            return set()
+
         if self.action.target_type == AlertRuleTriggerAction.TargetType.USER.value:
+            if RuleSnooze.objects.filter(
+                alert_rule=self.incident.alert_rule, user_id=target.id
+            ).exists():
+                return set()
+
             return {target.id}
 
         elif self.action.target_type == AlertRuleTriggerAction.TargetType.TEAM.value:
@@ -76,7 +87,10 @@ class EmailActionHandler(ActionHandler):
                 self.project,
                 {RpcUser(id=member.user_id) for member in target.member_set},
             )[ExternalProviders.EMAIL]
-            return {user.id for user in users}
+            snoozed_users = RuleSnooze.objects.filter(
+                alert_rule=self.incident.alert_rule, user_id__in=[user.id for user in users]
+            ).values_list("user_id", flat=True)
+            return {user.id for user in users if user.id not in snoozed_users}
 
         return set()
 

+ 43 - 0
tests/sentry/incidents/action_handlers/test_email.py

@@ -23,6 +23,7 @@ from sentry.incidents.models import (
     TriggerStatus,
 )
 from sentry.models import NotificationSetting, UserEmail, UserOption
+from sentry.models.rulesnooze import RuleSnooze
 from sentry.notifications.types import NotificationSettingOptionValues, NotificationSettingTypes
 from sentry.sentry_metrics import indexer
 from sentry.sentry_metrics.use_case_id_registry import UseCaseID
@@ -74,6 +75,24 @@ class EmailActionHandlerGetTargetsTest(TestCase):
         handler = EmailActionHandler(action, self.incident, self.project)
         assert handler.get_targets() == [(self.user.id, self.user.email)]
 
+    def test_rule_snoozed_by_user(self):
+        action = self.create_alert_rule_trigger_action(
+            target_type=AlertRuleTriggerAction.TargetType.USER,
+            target_identifier=str(self.user.id),
+        )
+        handler = EmailActionHandler(action, self.incident, self.project)
+        RuleSnooze.objects.create(alert_rule=self.incident.alert_rule, user_id=self.user.id)
+        assert handler.get_targets() == []
+
+    def test_user_rule_snoozed(self):
+        action = self.create_alert_rule_trigger_action(
+            target_type=AlertRuleTriggerAction.TargetType.USER,
+            target_identifier=str(self.user.id),
+        )
+        handler = EmailActionHandler(action, self.incident, self.project)
+        RuleSnooze.objects.create(alert_rule=self.incident.alert_rule)
+        assert handler.get_targets() == []
+
     def test_user_alerts_disabled(self):
         NotificationSetting.objects.update_settings(
             ExternalProviders.EMAIL,
@@ -102,6 +121,30 @@ class EmailActionHandlerGetTargetsTest(TestCase):
             (new_user.id, new_user.email),
         }
 
+    def test_rule_snoozed_by_one_user_in_team(self):
+        new_user = self.create_user()
+        self.create_team_membership(team=self.team, user=new_user)
+        action = self.create_alert_rule_trigger_action(
+            target_type=AlertRuleTriggerAction.TargetType.TEAM,
+            target_identifier=str(self.team.id),
+        )
+        handler = EmailActionHandler(action, self.incident, self.project)
+        RuleSnooze.objects.create(alert_rule=self.incident.alert_rule, user_id=new_user.id)
+        assert set(handler.get_targets()) == {
+            (self.user.id, self.user.email),
+        }
+
+    def test_team_rule_snoozed(self):
+        new_user = self.create_user()
+        self.create_team_membership(team=self.team, user=new_user)
+        action = self.create_alert_rule_trigger_action(
+            target_type=AlertRuleTriggerAction.TargetType.TEAM,
+            target_identifier=str(self.team.id),
+        )
+        handler = EmailActionHandler(action, self.incident, self.project)
+        RuleSnooze.objects.create(alert_rule=self.incident.alert_rule)
+        assert handler.get_targets() == []
+
     def test_team_alert_disabled(self):
         NotificationSetting.objects.update_settings(
             ExternalProviders.EMAIL,