Skip to content

Commit a620103

Browse files
authored
fix(aci): fix rule serializer lastTriggered to account for WorkflowFireHistory (#95939)
1 parent 8210cc7 commit a620103

File tree

2 files changed

+84
-1
lines changed

2 files changed

+84
-1
lines changed

src/sentry/api/serializers/models/rule.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from django.db.models import Max, Prefetch, Q, prefetch_related_objects
1010
from rest_framework import serializers
1111

12+
from sentry import features
1213
from sentry.api.serializers import Serializer, register
1314
from sentry.constants import ObjectStatus
1415
from sentry.db.models.manager.base_query_set import BaseQuerySet
@@ -207,6 +208,37 @@ def get_attrs(self, item_list, user, **kwargs):
207208
.values("rule_id")
208209
.annotate(date_added=Max("date_added"))
209210
}
211+
212+
# Update lastTriggered with WorkflowFireHistory if available
213+
if item_list and features.has(
214+
"organizations:workflow-engine-single-process-workflows",
215+
item_list[0].project.organization,
216+
):
217+
rule_ids = [rule.id for rule in item_list]
218+
workflow_rule_lookup = dict(
219+
AlertRuleWorkflow.objects.filter(rule_id__in=rule_ids).values_list(
220+
"workflow_id", "rule_id"
221+
)
222+
)
223+
224+
workflow_fire_results = (
225+
WorkflowFireHistory.objects.filter(
226+
workflow_id__in=workflow_rule_lookup.keys(), is_single_written=True
227+
)
228+
.values("workflow_id")
229+
.annotate(date_added=Max("date_added"))
230+
)
231+
232+
for wfh in workflow_fire_results:
233+
rule_id = workflow_rule_lookup.get(wfh["workflow_id"])
234+
if rule_id:
235+
# Take the maximum date between RuleFireHistory and WorkflowFireHistory
236+
existing_date = last_triggered_lookup[rule_id]
237+
new_date = wfh["date_added"]
238+
if new_date > existing_date:
239+
last_triggered_lookup[rule_id] = new_date
240+
241+
# Set the results
210242
for rule in item_list:
211243
result[rule]["last_triggered"] = last_triggered_lookup.get(rule.id, None)
212244

tests/sentry/api/serializers/test_rule.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from django.utils import timezone
44

55
from sentry.api.serializers import serialize
6-
from sentry.api.serializers.models.rule import WorkflowEngineRuleSerializer
6+
from sentry.api.serializers.models.rule import RuleSerializer, WorkflowEngineRuleSerializer
77
from sentry.models.rulefirehistory import RuleFireHistory
88
from sentry.rules.conditions.event_frequency import EventUniqueUserFrequencyConditionWithConditions
99
from sentry.rules.conditions.reappeared_event import ReappearedEventCondition
@@ -14,12 +14,63 @@
1414
from sentry.rules.filters.tagged_event import TaggedEventFilter
1515
from sentry.testutils.cases import TestCase
1616
from sentry.testutils.helpers.datetime import before_now, freeze_time
17+
from sentry.testutils.helpers.features import with_feature
1718
from sentry.users.services.user.serial import serialize_rpc_user
1819
from sentry.workflow_engine.migration_helpers.issue_alert_migration import IssueAlertMigrator
1920
from sentry.workflow_engine.models import WorkflowDataConditionGroup, WorkflowFireHistory
2021
from sentry.workflow_engine.models.data_condition import Condition
2122

2223

24+
@freeze_time()
25+
class RuleSerializerTest(TestCase):
26+
def test_last_triggered_rule_only(self):
27+
rule = self.create_project_rule()
28+
29+
# Initially no fire history
30+
result = serialize(rule, self.user, RuleSerializer(expand=["lastTriggered"]))
31+
assert result["lastTriggered"] is None
32+
33+
# Create a RuleFireHistory
34+
RuleFireHistory.objects.create(project=self.project, rule=rule, group=self.group)
35+
36+
result = serialize(rule, self.user, RuleSerializer(expand=["lastTriggered"]))
37+
assert result["lastTriggered"] == timezone.now()
38+
39+
@with_feature("organizations:workflow-engine-single-process-workflows")
40+
def test_last_triggered_with_workflow(self):
41+
rule = self.create_project_rule()
42+
43+
# Create a workflow for the rule
44+
workflow = IssueAlertMigrator(rule).run()
45+
46+
# Create an older RuleFireHistory
47+
rfh = RuleFireHistory.objects.create(project=self.project, rule=rule, group=self.group)
48+
rfh.update(date_added=before_now(hours=2))
49+
50+
# Create a newer WorkflowFireHistory
51+
WorkflowFireHistory.objects.create(
52+
workflow=workflow, group=self.group, event_id="test-event-id", is_single_written=True
53+
)
54+
55+
result = serialize(rule, self.user, RuleSerializer(expand=["lastTriggered"]))
56+
assert result["lastTriggered"] == timezone.now()
57+
58+
def test_last_triggered_workflow_ignore_single_written_false(self):
59+
"""Test that WorkflowFireHistory with is_single_written=False is ignored."""
60+
rule = self.create_project_rule()
61+
62+
# Create a workflow for the rule
63+
workflow = IssueAlertMigrator(rule).run()
64+
65+
# Create a WorkflowFireHistory with is_single_written=False
66+
WorkflowFireHistory.objects.create(
67+
workflow=workflow, group=self.group, event_id="test-event-id", is_single_written=False
68+
)
69+
70+
result = serialize(rule, self.user, RuleSerializer(expand=["lastTriggered"]))
71+
assert result["lastTriggered"] is None
72+
73+
2374
@freeze_time()
2475
class WorkflowRuleSerializerTest(TestCase):
2576
def setUp(self):

0 commit comments

Comments
 (0)