Skip to content

Commit bfaacc5

Browse files
authored
ref(aci): remove passing in detector to action.trigger (#103000)
1 parent 6b62b84 commit bfaacc5

File tree

10 files changed

+76
-77
lines changed

10 files changed

+76
-77
lines changed

src/sentry/api/endpoints/project_rule_actions.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from sentry.workflow_engine.migration_helpers.rule_action import (
2626
translate_rule_data_actions_to_notification_actions,
2727
)
28-
from sentry.workflow_engine.models import Detector, Workflow
28+
from sentry.workflow_engine.models import Workflow
2929
from sentry.workflow_engine.types import WorkflowEventData
3030

3131
logger = logging.getLogger(__name__)
@@ -161,14 +161,6 @@ def execute_future_on_test_event_workflow_engine(
161161
organization=rule.project.organization,
162162
)
163163

164-
detector = Detector(
165-
id=TEST_NOTIFICATION_ID,
166-
project=rule.project,
167-
name=rule.label,
168-
enabled=True,
169-
type=ErrorGroupType.slug,
170-
)
171-
172164
event_data = WorkflowEventData(
173165
event=test_event,
174166
group=test_event.group,
@@ -190,7 +182,7 @@ def execute_future_on_test_event_workflow_engine(
190182
action_exceptions.append(f"An unexpected error occurred. Error ID: '{error_id}'")
191183
continue
192184

193-
action_exceptions.extend(test_fire_action(action, event_data, detector))
185+
action_exceptions.extend(test_fire_action(action, event_data))
194186

195187
status = None
196188
data = None

src/sentry/workflow_engine/endpoints/organization_test_fire_action.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
)
2424
from sentry.workflow_engine.endpoints.utils.test_fire_action import test_fire_action
2525
from sentry.workflow_engine.endpoints.validators.base.action import BaseActionValidator
26-
from sentry.workflow_engine.models import Action, Detector
26+
from sentry.workflow_engine.models import Action
2727
from sentry.workflow_engine.types import WorkflowEventData
2828

2929
logger = logging.getLogger(__name__)
@@ -121,14 +121,6 @@ def test_fire_actions(actions: list[dict[str, Any]], project: Project):
121121
group=test_event.group,
122122
)
123123

124-
detector = Detector(
125-
id=TEST_NOTIFICATION_ID,
126-
project=project,
127-
name="Test Detector",
128-
enabled=True,
129-
type="error",
130-
)
131-
132124
for action_data in actions:
133125
# Create a temporary Action object (not saved to database)
134126
action = Action(
@@ -143,7 +135,7 @@ def test_fire_actions(actions: list[dict[str, Any]], project: Project):
143135
setattr(action, "workflow_id", workflow_id)
144136

145137
# Test fire the action and collect any exceptions
146-
exceptions = test_fire_action(action, workflow_event_data, detector)
138+
exceptions = test_fire_action(action, workflow_event_data)
147139
if exceptions:
148140
action_exceptions.extend(exceptions)
149141

src/sentry/workflow_engine/endpoints/utils/test_fire_action.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,20 @@
33
import sentry_sdk
44

55
from sentry.shared_integrations.exceptions import IntegrationFormError
6-
from sentry.workflow_engine.models import Action, Detector
6+
from sentry.workflow_engine.models import Action
77
from sentry.workflow_engine.types import WorkflowEventData
88

99
logger = logging.getLogger(__name__)
1010

1111

12-
def test_fire_action(
13-
action: Action, event_data: WorkflowEventData, detector: Detector
14-
) -> list[str]:
12+
def test_fire_action(action: Action, event_data: WorkflowEventData) -> list[str]:
1513
"""
1614
This function will fire an action and return a list of exceptions that occurred.
1715
"""
1816
action_exceptions = []
1917
try:
2018
action.trigger(
2119
event_data=event_data,
22-
detector=detector,
2320
)
2421
except Exception as exc:
2522
if isinstance(exc, IntegrationFormError):

src/sentry/workflow_engine/models/action.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import builtins
44
import logging
55
from enum import StrEnum
6-
from typing import TYPE_CHECKING, ClassVar
6+
from typing import ClassVar
77

88
from django.db import models
99
from django.db.models import Q
@@ -23,10 +23,6 @@
2323
from sentry.workflow_engine.registry import action_handler_registry
2424
from sentry.workflow_engine.types import ActionHandler, WorkflowEventData
2525

26-
if TYPE_CHECKING:
27-
from sentry.workflow_engine.models import Detector
28-
29-
3026
logger = logging.getLogger(__name__)
3127

3228

@@ -116,7 +112,10 @@ def get_handler(self) -> builtins.type[ActionHandler]:
116112
action_type = Action.Type(self.type)
117113
return action_handler_registry.get(action_type)
118114

119-
def trigger(self, event_data: WorkflowEventData, detector: Detector) -> None:
115+
def trigger(self, event_data: WorkflowEventData) -> None:
116+
from sentry.workflow_engine.processors.detector import get_detector_by_event
117+
118+
detector = get_detector_by_event(event_data)
120119
with metrics.timer(
121120
"workflow_engine.action.trigger.execution_time",
122121
tags={"action_type": self.type, "detector_type": detector.type},

src/sentry/workflow_engine/tasks/actions.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ def build_trigger_action_task_params(
6767
@retry(timeouts=True, raise_on_no_retries=False, ignore_and_capture=Action.DoesNotExist)
6868
def trigger_action(
6969
action_id: int,
70-
detector_id: int,
7170
workflow_id: int,
7271
event_id: str | None,
7372
activity_id: int | None,
@@ -76,8 +75,10 @@ def trigger_action(
7675
group_state: GroupState,
7776
has_reappeared: bool,
7877
has_escalated: bool,
78+
detector_id: int | None = None,
7979
) -> None:
8080
from sentry.notifications.notification_action.utils import should_fire_workflow_actions
81+
from sentry.workflow_engine.processors.detector import get_detector_by_event
8182

8283
# XOR check to ensure exactly one of event_id or activity_id is provided
8384
if (event_id is not None) == (activity_id is not None):
@@ -88,19 +89,14 @@ def trigger_action(
8889
raise ValueError("Exactly one of event_id or activity_id must be provided")
8990

9091
action = Action.objects.annotate(workflow_id=Value(workflow_id)).get(id=action_id)
91-
detector = Detector.objects.get(id=detector_id)
9292

93-
metrics.incr(
94-
"workflow_engine.tasks.trigger_action_task_started",
95-
tags={"action_type": action.type, "detector_type": detector.type},
96-
sample_rate=1.0,
97-
)
98-
99-
project_id = detector.project_id
93+
# TODO: remove detector usage from this task
94+
detector: Detector | None = None
95+
if detector_id is not None:
96+
detector = Detector.objects.get(id=detector_id)
10097

10198
if event_id is not None:
10299
event_data = build_workflow_event_data_from_event(
103-
project_id=project_id,
104100
event_id=event_id,
105101
group_id=group_id,
106102
workflow_id=workflow_id,
@@ -122,6 +118,15 @@ def trigger_action(
122118
)
123119
raise ValueError("Exactly one of event_id or activity_id must be provided")
124120

121+
if detector is None:
122+
detector = get_detector_by_event(event_data)
123+
124+
metrics.incr(
125+
"workflow_engine.tasks.trigger_action_task_started",
126+
tags={"action_type": action.type, "detector_type": detector.type},
127+
sample_rate=1.0,
128+
)
129+
125130
should_trigger_actions = should_fire_workflow_actions(
126131
detector.project.organization, event_data.group.type
127132
)
@@ -130,7 +135,7 @@ def trigger_action(
130135
# Set up a timeout grouping context because we want to make sure any Sentry timeout reporting
131136
# in this scope is grouped properly.
132137
with timeout_grouping_context(action.type):
133-
action.trigger(event_data, detector)
138+
action.trigger(event_data)
134139
else:
135140
logger.info(
136141
"workflow_engine.triggered_actions.dry-run",

src/sentry/workflow_engine/tasks/utils.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ def __init__(self, event_id: str, project_id: int):
6464

6565
@scopedstats.timer()
6666
def build_workflow_event_data_from_event(
67-
project_id: int,
6867
event_id: str,
6968
group_id: int,
7069
workflow_id: int | None = None,
@@ -78,14 +77,14 @@ def build_workflow_event_data_from_event(
7877
This method handles all the database fetching and object construction logic.
7978
Raises EventNotFoundError if the event is not found.
8079
"""
81-
80+
group = Group.objects.get_from_cache(id=group_id)
81+
project_id = group.project_id
8282
event = fetch_event(event_id, project_id)
8383
if event is None:
8484
raise EventNotFoundError(event_id, project_id)
8585

8686
occurrence = IssueOccurrence.fetch(occurrence_id, project_id) if occurrence_id else None
8787

88-
group = Group.objects.get_from_cache(id=group_id)
8988
group_event = GroupEvent.from_event(event, group)
9089
group_event.occurrence = occurrence
9190

src/sentry/workflow_engine/tasks/workflows.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,14 @@ def process_workflow_activity(activity_id: int, group_id: int, detector_id: int)
9393
on_silent=DataConditionGroup.DoesNotExist,
9494
)
9595
def process_workflows_event(
96-
project_id: int,
9796
event_id: str,
9897
group_id: int,
9998
occurrence_id: str | None,
10099
group_state: GroupState,
101100
has_reappeared: bool,
102101
has_escalated: bool,
103102
start_timestamp_seconds: float | None = None,
103+
project_id: int | None = None,
104104
**kwargs: dict[str, Any],
105105
) -> None:
106106
from sentry.workflow_engine.buffer.batch_client import DelayedWorkflowClient
@@ -111,7 +111,6 @@ def process_workflows_event(
111111
with recorder.record():
112112
try:
113113
event_data = build_workflow_event_data_from_event(
114-
project_id=project_id,
115114
event_id=event_id,
116115
group_id=group_id,
117116
occurrence_id=occurrence_id,

tests/sentry/workflow_engine/endpoints/test_organization_test_fire_action.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from sentry.testutils.silo import assume_test_silo_mode
2222
from sentry.testutils.skips import requires_snuba
2323
from sentry.workflow_engine.models import Action
24+
from sentry.workflow_engine.typings.grouptype import IssueStreamGroupType
2425
from tests.sentry.workflow_engine.test_base import BaseWorkflowTest
2526

2627
pytestmark = [requires_snuba]
@@ -34,6 +35,10 @@ def setUp(self) -> None:
3435
super().setUp()
3536
self.login_as(self.user)
3637
self.project = self.create_project(organization=self.organization)
38+
self.detector = self.create_detector(project=self.project)
39+
self.issue_stream_detector = self.create_detector(
40+
project=self.project, type=IssueStreamGroupType.slug
41+
)
3742
self.workflow = self.create_workflow()
3843

3944
def setup_pd_service(self) -> PagerDutyServiceDict:
@@ -90,7 +95,7 @@ def test_pagerduty_action(
9095
assert response.status_code == 200
9196
assert mock_send_trigger.call_count == 1
9297
pagerduty_data = mock_send_trigger.call_args.kwargs.get("data")
93-
assert pagerduty_data["payload"]["summary"].startswith("[Test Detector]:")
98+
assert pagerduty_data["payload"]["summary"].startswith(f"[{self.detector.name}]:")
9499

95100
@mock.patch.object(NotifyEventAction, "after")
96101
@mock.patch(

tests/sentry/workflow_engine/handlers/action/test_action_handlers.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33
from sentry.grouping.grouptype import ErrorGroupType
44
from sentry.incidents.grouptype import MetricIssue
5+
from sentry.types.group import PriorityLevel
56
from sentry.utils.registry import NoRegistrationExistsError
67
from sentry.workflow_engine.models import Action
78
from sentry.workflow_engine.types import WorkflowEventData
8-
from tests.sentry.workflow_engine.test_base import BaseWorkflowTest
9+
from tests.sentry.notifications.notification_action.test_metric_alert_registry_handlers import (
10+
MetricAlertHandlerBase,
11+
)
912

1013

11-
class TestNotificationActionHandler(BaseWorkflowTest):
14+
class TestNotificationActionHandler(MetricAlertHandlerBase):
1215
def setUp(self) -> None:
1316
super().setUp()
1417
self.project = self.create_project()
@@ -17,18 +20,6 @@ def setUp(self) -> None:
1720
self.group, self.event, self.group_event = self.create_group_event()
1821
self.event_data = WorkflowEventData(event=self.group_event, group=self.group)
1922

20-
@mock.patch("sentry.notifications.notification_action.utils.execute_via_issue_alert_handler")
21-
def test_execute_without_group_type(
22-
self, mock_execute_via_issue_alert_handler: mock.MagicMock
23-
) -> None:
24-
"""Test that execute does nothing when detector has no group_type"""
25-
self.detector.type = ""
26-
self.action.trigger(self.event_data, self.detector)
27-
28-
mock_execute_via_issue_alert_handler.assert_called_once_with(
29-
self.event_data, self.action, self.detector
30-
)
31-
3223
@mock.patch(
3324
"sentry.notifications.notification_action.registry.group_type_notification_registry.get"
3425
)
@@ -40,7 +31,7 @@ def test_execute_error_group_type(self, mock_registry_get: mock.MagicMock) -> No
4031
mock_handler = mock.Mock()
4132
mock_registry_get.return_value = mock_handler
4233

43-
self.action.trigger(self.event_data, self.detector)
34+
self.action.trigger(self.event_data)
4435

4536
mock_registry_get.assert_called_once_with(ErrorGroupType.slug)
4637
mock_handler.handle_workflow_action.assert_called_once_with(
@@ -56,10 +47,25 @@ def test_execute_metric_alert_type(self, mock_registry_get: mock.MagicMock) -> N
5647
self.detector.config = {"threshold_period": 1, "detection_type": "static"}
5748
self.detector.save()
5849

50+
self.group.type = MetricIssue.type_id
51+
self.group.save()
52+
53+
group, _, group_event = self.create_group_event(
54+
group_type_id=MetricIssue.type_id,
55+
occurrence=self.create_issue_occurrence(
56+
priority=PriorityLevel.HIGH.value,
57+
level="error",
58+
evidence_data={
59+
"detector_id": self.detector.id,
60+
},
61+
),
62+
)
63+
self.event_data = WorkflowEventData(event=group_event, group=group)
64+
5965
mock_handler = mock.Mock()
6066
mock_registry_get.return_value = mock_handler
6167

62-
self.action.trigger(self.event_data, self.detector)
68+
self.action.trigger(self.event_data)
6369

6470
mock_registry_get.assert_called_once_with(MetricIssue.slug)
6571
mock_handler.handle_workflow_action.assert_called_once_with(
@@ -72,15 +78,15 @@ def test_execute_metric_alert_type(self, mock_registry_get: mock.MagicMock) -> N
7278
side_effect=NoRegistrationExistsError,
7379
)
7480
@mock.patch("sentry.notifications.notification_action.utils.logger")
75-
def test_execute_unknown_group_type(
81+
def test_execute_unknown_detector(
7682
self,
7783
mock_logger: mock.MagicMock,
7884
mock_registry_get: mock.MagicMock,
7985
mock_execute_via_issue_alert_handler: mock.MagicMock,
8086
) -> None:
81-
"""Test that execute does nothing when detector has no group_type"""
87+
"""Test that execute does nothing when we can't find the detector"""
8288

83-
self.action.trigger(self.event_data, self.detector)
89+
self.action.trigger(self.event_data)
8490

8591
mock_logger.warning.assert_called_once_with(
8692
"group_type_notification_registry.get.NoRegistrationExistsError",

0 commit comments

Comments
 (0)