diff --git a/src/sentry/rules/actions/notify_event_service.py b/src/sentry/rules/actions/notify_event_service.py index 31fe09bf88bc9a..1f30adc75f0bc3 100644 --- a/src/sentry/rules/actions/notify_event_service.py +++ b/src/sentry/rules/actions/notify_event_service.py @@ -103,18 +103,27 @@ def find_alert_rule_action_ui_component(app_platform_event: AppPlatformEvent) -> Loop through the triggers for the alert rule event. For each trigger, check if an action is an alert rule UI Component """ - triggers = ( - getattr(app_platform_event, "data", {}) - .get("metric_alert", {}) - .get("alert_rule", {}) - .get("triggers", []) - ) + data = getattr(app_platform_event, "data", {}) or {} + + metric_alert = data.get("metric_alert") or {} + if not isinstance(metric_alert, dict): + metric_alert = {} + + alert_rule = metric_alert.get("alert_rule") or {} + if not isinstance(alert_rule, dict): + alert_rule = {} + + triggers = alert_rule.get("triggers") or [] + if not isinstance(triggers, list): + triggers = [] actions = [ action for trigger in triggers - for action in trigger.get("actions", {}) - if (action.get("type") == "sentry_app" and action.get("settings") is not None) + if isinstance(trigger, dict) + for action in trigger.get("actions") or [] + if isinstance(action, dict) + if action.get("type") == "sentry_app" and action.get("settings") is not None ] return bool(len(actions)) diff --git a/tests/sentry/rules/actions/test_notify_event_service.py b/tests/sentry/rules/actions/test_notify_event_service.py index ac43f7bc7fb8ad..46bbba53f09a2c 100644 --- a/tests/sentry/rules/actions/test_notify_event_service.py +++ b/tests/sentry/rules/actions/test_notify_event_service.py @@ -1,11 +1,15 @@ +from types import SimpleNamespace from unittest.mock import MagicMock, patch from django.utils import timezone -from sentry.rules.actions.notify_event_service import NotifyEventServiceAction +from sentry.rules.actions.notify_event_service import ( + NotifyEventServiceAction, + find_alert_rule_action_ui_component, +) from sentry.sentry_apps.tasks.sentry_apps import notify_sentry_app from sentry.silo.base import SiloMode -from sentry.testutils.cases import RuleTestCase +from sentry.testutils.cases import RuleTestCase, TestCase from sentry.testutils.silo import assume_test_silo_mode from sentry.testutils.skips import requires_snuba @@ -90,3 +94,32 @@ def test_sentry_app_installed(self) -> None: results = rule.get_services() assert len(results) == 0 + + +class FindAlertRuleActionUiComponentTest(TestCase): + def test_handles_null_metric_alert_payload(self) -> None: + event = SimpleNamespace(data={"metric_alert": None}) + + assert not find_alert_rule_action_ui_component(event) + + def test_detects_sentry_app_action(self) -> None: + event = SimpleNamespace( + data={ + "metric_alert": { + "alert_rule": { + "triggers": [ + { + "actions": [ + { + "type": "sentry_app", + "settings": {"foo": "bar"}, + } + ] + } + ] + } + } + } + ) + + assert find_alert_rule_action_ui_component(event)