diff --git a/src/sentry/incidents/endpoints/serializers/alert_rule_trigger_action.py b/src/sentry/incidents/endpoints/serializers/alert_rule_trigger_action.py index 6b0c30dbbb07..f64d8080247b 100644 --- a/src/sentry/incidents/endpoints/serializers/alert_rule_trigger_action.py +++ b/src/sentry/incidents/endpoints/serializers/alert_rule_trigger_action.py @@ -1,47 +1,64 @@ +from __future__ import annotations + import logging +from collections.abc import Mapping +from typing import Any + +from django.contrib.auth.models import AnonymousUser from sentry.api.serializers import Serializer, register from sentry.incidents.models.alert_rule import AlertRuleTriggerAction +from sentry.models.organizationmember import OrganizationMember +from sentry.models.team import Team +from sentry.users.models.user import User +from sentry.users.services.user.model import RpcUser from sentry.workflow_engine.utils.legacy_metric_tracking import report_used_legacy_models logger = logging.getLogger(__name__) def human_desc( - action_type, - target_type, - target_identifier, - target, - target_display=None, - action_target=None, - priority=None, -): - # Returns a human readable description to display in the UI + action_type: int, + target_identifier: str | None, + target: OrganizationMember | Team | str | None, + target_display: str | None = None, + priority: str | None = None, +) -> str | None: + """Return a human-readable description of a metric alert action for display in the UI. + + Args: + action_type: An ``ActionService`` enum value (e.g. EMAIL, SLACK, PAGERDUTY). + priority: On-call severity/priority string when applicable. + PagerDuty: "default", "critical", "warning", "error", "info". + Opsgenie: "P1"–"P5". + """ if priority: priority += " level" + slack_desc = f"Send a Slack notification to {target_display}" action_type_to_string = { - AlertRuleTriggerAction.Type.PAGERDUTY.value: f"Send a {priority} PagerDuty notification to {target_display}", - AlertRuleTriggerAction.Type.SLACK.value: f"Send a Slack notification to {target_display}", + AlertRuleTriggerAction.Type.PAGERDUTY.value: ( + f"Send a {priority} PagerDuty notification to {target_display}" + if priority + else f"Send a PagerDuty notification to {target_display}" + ), + AlertRuleTriggerAction.Type.SLACK.value: slack_desc, + AlertRuleTriggerAction.Type.SLACK_STAGING.value: slack_desc, AlertRuleTriggerAction.Type.MSTEAMS.value: f"Send a Microsoft Teams notification to {target_display}", AlertRuleTriggerAction.Type.SENTRY_APP.value: f"Send a notification via {target_display}", } if action_type == AlertRuleTriggerAction.Type.EMAIL.value: - if action_target: - if target_type == AlertRuleTriggerAction.TargetType.USER.value: - email = target.get_email() if target else "[removed]" - return "Send a notification to " + email - elif target_type == AlertRuleTriggerAction.TargetType.TEAM.value: - slug = "#" + target.slug if target else "[removed]" - return "Send an email to members of " + slug - else: - logger.info("email.action.description.no_action_target") - return "Send an email to [removed]" + if isinstance(target, OrganizationMember): + return "Send a notification to " + target.get_email() + elif isinstance(target, Team): + return "Send an email to members of #" + target.slug + logger.info("email.action.description.no_action_target") + return "Send an email to [removed]" elif action_type == AlertRuleTriggerAction.Type.OPSGENIE.value: if priority: return f"Send a {priority} Opsgenie notification to {target_display}" - return "Send an Opsgenie notification to {target_display}" + return f"Send an Opsgenie notification to {target_display}" elif action_type == AlertRuleTriggerAction.Type.DISCORD.value: if not target_display: logger.info( @@ -50,7 +67,7 @@ def human_desc( ) return f"Send a Discord notification to {target_display}" else: - return action_type_to_string[action_type] + return action_type_to_string.get(action_type) def get_identifier_from_action(action_type, target_identifier, target_display=None): @@ -74,15 +91,23 @@ def get_input_channel_id(action_type, target_identifier=None): @register(AlertRuleTriggerAction) -class AlertRuleTriggerActionSerializer(Serializer): - def serialize(self, obj, attrs, user, **kwargs): +class AlertRuleTriggerActionSerializer(Serializer[dict[str, Any]]): + def serialize( + self, + obj: AlertRuleTriggerAction, + attrs: Mapping[str, Any], + user: User | RpcUser | AnonymousUser, + **kwargs: Any, + ) -> dict[str, Any]: # Mark that we're using legacy AlertRuleTriggerAction models report_used_legacy_models() from sentry.incidents.serializers import ACTION_TARGET_TYPE_TO_STRING - priority = ( - obj.sentry_app_config.get("priority") if isinstance(obj.sentry_app_config, dict) else "" + priority: str | None = ( + obj.sentry_app_config.get("priority") + if isinstance(obj.sentry_app_config, dict) + else None ) result = { "id": str(obj.id), @@ -102,18 +127,12 @@ def serialize(self, obj, attrs, user, **kwargs): "dateCreated": obj.date_added, "desc": human_desc( obj.type, - obj.target_type, obj.target_identifier, obj.target, obj.target_display, - obj.target, priority, ), - "priority": ( - obj.sentry_app_config.get("priority", None) - if isinstance(obj.sentry_app_config, dict) - else None - ), + "priority": priority, } # Check if action is a Sentry App that has Alert Rule UI Component settings diff --git a/src/sentry/incidents/endpoints/serializers/workflow_engine_action.py b/src/sentry/incidents/endpoints/serializers/workflow_engine_action.py index 60a1620ee492..3482d8326aec 100644 --- a/src/sentry/incidents/endpoints/serializers/workflow_engine_action.py +++ b/src/sentry/incidents/endpoints/serializers/workflow_engine_action.py @@ -53,13 +53,14 @@ def serialize( alert_rule_trigger_id = kwargs.get("alert_rule_trigger_id", -1) aarta = attrs.get("aarta") - priority = obj.data.get("priority") + priority: str | None = obj.data.get("priority") type_value = ActionService.get_value(obj.type) + assert type_value is not None, f"Unknown ActionService for type {obj.type}" target = attrs.get("target") - target_type = obj.config.get("target_type") - target_identifier = obj.config.get("target_identifier") - target_display = obj.config.get("target_display") + target_type: int = obj.config.get("target_type") + target_identifier: str | None = obj.config.get("target_identifier") + target_display: str | None = obj.config.get("target_display") sentry_app_id = None sentry_app_config = None @@ -85,11 +86,9 @@ def serialize( "dateCreated": obj.date_added, "desc": human_desc( type_value, - target_type, target_identifier, target, target_display, - target_identifier, priority, ), "priority": priority, diff --git a/tests/sentry/incidents/endpoints/serializers/test_alert_rule_trigger_action.py b/tests/sentry/incidents/endpoints/serializers/test_alert_rule_trigger_action.py index 1945596ed008..142f36d56aca 100644 --- a/tests/sentry/incidents/endpoints/serializers/test_alert_rule_trigger_action.py +++ b/tests/sentry/incidents/endpoints/serializers/test_alert_rule_trigger_action.py @@ -50,7 +50,7 @@ def test_simple(self) -> None: ) result = serialize(action) self.assert_action_serialized(action, result) - assert result["desc"] == action.target_display + assert result["desc"] == "Send an email to [removed]" @responses.activate def test_discord(self) -> None: @@ -135,7 +135,25 @@ def test_pagerduty_priority(self, mock_get: MagicMock) -> None: result = serialize(action) self.assert_action_serialized(action, result) assert result["priority"] == priority - assert result["desc"] == "Send a critical level PagerDuty notification to test" + + @patch( + "sentry.incidents.logic.get_target_identifier_display_for_integration", + return_value=AlertTarget("123", "test"), + ) + def test_pagerduty_no_priority(self, mock_get: MagicMock) -> None: + alert_rule = self.create_alert_rule() + trigger = create_alert_rule_trigger(alert_rule, "hi", 1000) + action = create_alert_rule_trigger_action( + trigger, + AlertRuleTriggerAction.Type.PAGERDUTY, + AlertRuleTriggerAction.TargetType.SPECIFIC, + target_identifier="123", + ) + result = serialize(action) + self.assert_action_serialized(action, result) + assert result["priority"] is None + assert "None" not in result["desc"] + assert result["desc"] == "Send a PagerDuty notification to test" @responses.activate @patch(