Skip to content

Commit 4408129

Browse files
committed
fix the processing and some of the tests to be dynamic to the configurations.
1 parent df0e9cd commit 4408129

File tree

2 files changed

+66
-18
lines changed

2 files changed

+66
-18
lines changed

src/sentry/workflow_engine/handlers/detector/stateful.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,14 +278,33 @@ class StatefulDetectorHandler(
278278
def __init__(self, detector: Detector, thresholds: DetectorThresholds | None = None):
279279
super().__init__(detector)
280280

281+
# Default to 1 for all the possible levels on a given detector
282+
default_thresholds = {level: 1 for level in self._get_configured_detector_levels()}
283+
281284
self._thresholds: DetectorThresholds = {
282-
**{level: 1 for level in DetectorPriorityLevel}, # Default to 0 for all levels
285+
DetectorPriorityLevel.OK: 1, # Make sure the OK level is always set
286+
**default_thresholds,
283287
**(self.thresholds or {}), # Allow each handler to override
284288
**(thresholds or {}), # Allow each instance to override
285289
}
286290

287291
self.state_manager = DetectorStateManager(detector, list(self._thresholds.keys()))
288292

293+
def _get_configured_detector_levels(self) -> list[DetectorPriorityLevel]:
294+
# TODO - Is this something that should be provided by the detector itself rather
295+
# than having to query the db for each level?
296+
priority_levels: list[DetectorPriorityLevel] = [level for level in DetectorPriorityLevel]
297+
298+
if self.detector.workflow_condition_group is None:
299+
# TODO - Should this default to _all_ levels or no levels?
300+
return priority_levels
301+
302+
condition_result_levels = self.detector.workflow_condition_group.conditions.filter(
303+
condition_result__in=priority_levels
304+
).values_list("condition_result", flat=True)
305+
306+
return list(DetectorPriorityLevel(level) for level in condition_result_levels)
307+
289308
def _create_decorated_issue_occurrence(
290309
self,
291310
detector_occurrence: DetectorOccurrence,
@@ -379,11 +398,15 @@ def _has_breached_threshold(
379398
self,
380399
updated_threshold_counts: DetectorCounters,
381400
) -> DetectorPriorityLevel | None:
382-
# TODO @saponifi3d - Should this only be for thresholds configured on the detector?
383-
# If so, gather the self.detector.when_condition_group.conditions()
384-
# and see which ones are configured.
401+
"""
402+
Get the list of configured thresholds, then sort them to find the highest
403+
breached threshold.
404+
405+
If the threshold is breached, return the highest breached threshold level.
406+
"""
385407
threshold_keys: list[DetectorPriorityLevel] = list(self._thresholds.keys())
386408
threshold_keys.sort(reverse=True)
409+
387410
for level in threshold_keys:
388411
level_count = updated_threshold_counts.get(level)
389412
if level_count is not None and level_count >= self._thresholds[level]:
@@ -524,7 +547,7 @@ def evaluate_group_key_value(
524547
)
525548

526549
breached_threshold = self._has_breached_threshold(updated_threshold_count)
527-
if not breached_threshold:
550+
if breached_threshold is None:
528551
# We haven't met the threshold yet, so don't trigger
529552
return None
530553

tests/sentry/workflow_engine/handlers/detector/test_stateful.py

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ def setUp(self):
4747
detector=self.detector,
4848
thresholds={
4949
DetectorPriorityLevel.HIGH: 2,
50-
DetectorPriorityLevel.MEDIUM: 3,
5150
},
5251
)
5352

@@ -59,9 +58,9 @@ def test_increment_detector_thresholds(self):
5958
self.handler.state_manager.commit_state_updates()
6059
updated_state = self.handler.state_manager.get_state_data([self.group_key])[self.group_key]
6160

62-
# All states should be incremented by 1, except for OK
61+
# Default to all states since the detector is not configured with any.
6362
assert updated_state.counter_updates == {
64-
**{level: 1 for level in DetectorPriorityLevel},
63+
**{level: 1 for level in self.handler._thresholds},
6564
DetectorPriorityLevel.OK: None,
6665
}
6766

@@ -144,13 +143,7 @@ def setUp(self):
144143

145144
def test_evaualte__override_threshold(self):
146145
result = self.handler.evaluate(self.data_packet)
147-
evaluation_result = result[self.group_key]
148-
149-
# Check that the detector is triggered in a medium priority.
150-
assert evaluation_result
151-
assert evaluation_result.priority == DetectorPriorityLevel.MEDIUM
152-
assert evaluation_result.is_triggered is True
153-
assert isinstance(evaluation_result.result, IssueOccurrence)
146+
assert result == {}
154147

155148
def test_evaluate__override_threshold__triggered(self):
156149
self.handler.evaluate(self.data_packet)
@@ -173,10 +166,39 @@ def test_evaluate__detector_state(self):
173166
assert state_data.is_triggered is True
174167
assert state_data.status == DetectorPriorityLevel.HIGH
175168

169+
# Only has configured states
176170
assert state_data.counter_updates == {
177171
DetectorPriorityLevel.HIGH: 2,
178-
DetectorPriorityLevel.MEDIUM: 2,
179-
DetectorPriorityLevel.LOW: 2,
172+
DetectorPriorityLevel.OK: None,
173+
}
174+
175+
def test_evaluate__detector_state__all_levels(self):
176+
# Add additional levels to the detector
177+
self.create_data_condition(
178+
type="gte",
179+
comparison=0,
180+
condition_group=self.detector.workflow_condition_group,
181+
condition_result=DetectorPriorityLevel.LOW,
182+
)
183+
184+
self.create_data_condition(
185+
type="gte",
186+
comparison=0,
187+
condition_group=self.detector.workflow_condition_group,
188+
condition_result=DetectorPriorityLevel.MEDIUM,
189+
)
190+
191+
# Reinitialize the handler to include the new levels
192+
self.handler = MockDetectorStateHandler(
193+
detector=self.detector, thresholds={DetectorPriorityLevel.HIGH: 2}
194+
)
195+
196+
self.handler.evaluate(self.data_packet)
197+
state_data = self.handler.state_manager.get_state_data([self.group_key])[self.group_key]
198+
199+
# Verify all the levels are present now
200+
assert state_data.counter_updates == {
201+
**{level: 1 for level in DetectorPriorityLevel},
180202
DetectorPriorityLevel.OK: None,
181203
}
182204

@@ -200,7 +222,10 @@ def test_evaluate__resolve__detector_state(self):
200222
# Check that the state is reset
201223
assert state_data.is_triggered is False
202224
assert state_data.status == DetectorPriorityLevel.OK
203-
assert state_data.counter_updates == {level: None for level in DetectorPriorityLevel}
225+
# Only has configured states
226+
assert state_data.counter_updates == {
227+
**{level: None for level in self.handler._thresholds},
228+
}
204229

205230
def test_evaluate__trigger_after_resolve(self):
206231
self.handler.evaluate(self.data_packet)

0 commit comments

Comments
 (0)