Skip to content

Commit 7547479

Browse files
authored
feat(integration): Expand Issue Alert notification (#66625)
For Issue Alert notification messages, we need to be able to retrieve the parent message such that we can create a thread. Added logic to help achieve that query and flow, along with some tests. Requires: #66623
1 parent 374edcf commit 7547479

File tree

4 files changed

+137
-2
lines changed

4 files changed

+137
-2
lines changed

src/sentry/integrations/repository/issue_alert.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,39 @@
11
from __future__ import annotations
22

3+
from dataclasses import dataclass
34
from logging import Logger, getLogger
45

6+
from sentry.integrations.repository.base import BaseNotificationMessage
57
from sentry.models.notificationmessage import NotificationMessage
8+
from sentry.models.rulefirehistory import RuleFireHistory
69

710
_default_logger: Logger = getLogger(__name__)
811

912

13+
@dataclass(frozen=True)
14+
class IssueAlertNotificationMessage(BaseNotificationMessage):
15+
# TODO: https://github.com/getsentry/sentry/issues/66751
16+
rule_fire_history: RuleFireHistory | None = None
17+
rule_action_uuid: str | None = None
18+
19+
@classmethod
20+
def from_model(cls, instance: NotificationMessage) -> IssueAlertNotificationMessage:
21+
return IssueAlertNotificationMessage(
22+
id=instance.id,
23+
error_code=instance.error_code,
24+
error_details=instance.error_details,
25+
message_identifier=instance.message_identifier,
26+
parent_notification_message_id=(
27+
instance.parent_notification_message.id
28+
if instance.parent_notification_message
29+
else None
30+
),
31+
rule_fire_history=instance.rule_fire_history,
32+
rule_action_uuid=instance.rule_action_uuid,
33+
date_added=instance.date_added,
34+
)
35+
36+
1037
class IssueAlertNotificationMessageRepository:
1138
"""
1239
Repository class that is responsible for querying the data store for notification messages in relation to issue
@@ -21,3 +48,33 @@ def __init__(self, logger: Logger) -> None:
2148
@classmethod
2249
def default(cls) -> IssueAlertNotificationMessageRepository:
2350
return cls(logger=_default_logger)
51+
52+
def get_parent_notification_message(
53+
self, rule_id: int, group_id: int, rule_action_uuid: str
54+
) -> IssueAlertNotificationMessage | None:
55+
"""
56+
Returns the parent notification message for a metric rule if it exists, otherwise returns None.
57+
Will raise an exception if the query fails and logs the error with associated data.
58+
"""
59+
try:
60+
instance: NotificationMessage = self._model.objects.get(
61+
rule_fire_history__rule__id=rule_id,
62+
rule_fire_history__group__id=group_id,
63+
rule_action_uuid=rule_action_uuid,
64+
parent_notification_message__isnull=True,
65+
error_code__isnull=True,
66+
)
67+
return IssueAlertNotificationMessage.from_model(instance=instance)
68+
except NotificationMessage.DoesNotExist:
69+
return None
70+
except Exception as e:
71+
self._logger.exception(
72+
"Failed to get parent notification for issue rule",
73+
exc_info=e,
74+
extra={
75+
"rule_id": rule_id,
76+
"group_id": group_id,
77+
"rule_action_uuid": rule_action_uuid,
78+
},
79+
)
80+
raise

src/sentry/integrations/repository/metric_alert.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@
1717

1818
@dataclass(frozen=True)
1919
class MetricAlertNotificationMessage(BaseNotificationMessage):
20-
# TODO(Yash): do we really need this entire model, or can we whittle it down to what we need?
20+
# TODO: https://github.com/getsentry/sentry/issues/66751
2121
incident: Incident | None = None
22-
# TODO(Yash): do we really need this entire model, or can we whittle it down to what we need?
22+
# TODO: https://github.com/getsentry/sentry/issues/66751
2323
trigger_action: AlertRuleTriggerAction | None = None
2424

2525
@classmethod

tests/sentry/integrations/repository/issue_alert/__init__.py

Whitespace-only changes.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
from uuid import uuid4
2+
3+
from sentry.integrations.repository.issue_alert import (
4+
IssueAlertNotificationMessage,
5+
IssueAlertNotificationMessageRepository,
6+
)
7+
from sentry.models.notificationmessage import NotificationMessage
8+
from sentry.models.rulefirehistory import RuleFireHistory
9+
from sentry.testutils.cases import TestCase
10+
11+
12+
class TestGetParentNotificationMessage(TestCase):
13+
def setUp(self) -> None:
14+
self.action_uuid = str(uuid4())
15+
self.rule = self.create_project_rule(
16+
project=self.project,
17+
action_match=[
18+
{
19+
"id": "sentry.rules.actions.notify_event_service.NotifyEventServiceAction",
20+
"service": "mail",
21+
"name": "Send a notification via mail",
22+
"uuid": self.action_uuid,
23+
},
24+
],
25+
)
26+
self.rule_fire_history = RuleFireHistory.objects.create(
27+
project=self.project,
28+
rule=self.rule,
29+
group=self.group,
30+
)
31+
self.parent_notification_message = NotificationMessage.objects.create(
32+
rule_fire_history=self.rule_fire_history,
33+
rule_action_uuid=self.action_uuid,
34+
message_identifier="123abc",
35+
)
36+
self.repository = IssueAlertNotificationMessageRepository.default()
37+
38+
def test_returns_parent_notification_message(self) -> None:
39+
instance = self.repository.get_parent_notification_message(
40+
rule_id=self.rule.id,
41+
group_id=self.group.id,
42+
rule_action_uuid=self.action_uuid,
43+
)
44+
45+
assert instance is not None
46+
assert instance == IssueAlertNotificationMessage.from_model(
47+
self.parent_notification_message
48+
)
49+
50+
def test_returns_none_when_filter_does_not_exist(self) -> None:
51+
instance = self.repository.get_parent_notification_message(
52+
rule_id=9999,
53+
group_id=self.group.id,
54+
rule_action_uuid=self.action_uuid,
55+
)
56+
57+
assert instance is None
58+
59+
def test_when_parent_has_child(self) -> None:
60+
child = NotificationMessage.objects.create(
61+
rule_fire_history=self.rule_fire_history,
62+
rule_action_uuid=self.action_uuid,
63+
message_identifier="456abc",
64+
parent_notification_message=self.parent_notification_message,
65+
)
66+
67+
assert child.id != self.parent_notification_message.id
68+
69+
instance = self.repository.get_parent_notification_message(
70+
rule_id=self.rule.id,
71+
group_id=self.group.id,
72+
rule_action_uuid=self.action_uuid,
73+
)
74+
75+
assert instance is not None
76+
assert instance == IssueAlertNotificationMessage.from_model(
77+
self.parent_notification_message
78+
)

0 commit comments

Comments
 (0)