Skip to content

Commit 30d47e5

Browse files
authored
feat(workflow-engine): add EventAttributeConditionHandler (#82741)
add `EventAttributeConditionHandler`
1 parent 1668090 commit 30d47e5

File tree

5 files changed

+970
-0
lines changed

5 files changed

+970
-0
lines changed

src/sentry/workflow_engine/handlers/condition/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
"ReappearedEventConditionHandler",
66
"RegressionEventConditionHandler",
77
"ExistingHighPriorityIssueConditionHandler",
8+
"EventAttributeConditionHandler",
89
]
910

1011
from .group_event_handlers import (
12+
EventAttributeConditionHandler,
1113
EventCreatedByDetectorConditionHandler,
1214
EventSeenCountConditionHandler,
1315
EveryEventConditionHandler,

src/sentry/workflow_engine/handlers/condition/group_event_handlers.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
from typing import Any
22

3+
import sentry_sdk
4+
5+
from sentry.eventstore.models import GroupEvent
6+
from sentry.rules import MatchType, match_values
7+
from sentry.rules.conditions.event_attribute import attribute_registry
8+
from sentry.utils import json
9+
from sentry.utils.registry import NoRegistrationExistsError
310
from sentry.workflow_engine.models.data_condition import Condition
411
from sentry.workflow_engine.registry import condition_handler_registry
512
from sentry.workflow_engine.types import DataConditionHandler, WorkflowJob
@@ -29,3 +36,53 @@ class EventSeenCountConditionHandler(DataConditionHandler[WorkflowJob]):
2936
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
3037
event = job["event"]
3138
return event.group.times_seen == comparison
39+
40+
41+
@condition_handler_registry.register(Condition.EVENT_ATTRIBUTE)
42+
class EventAttributeConditionHandler(DataConditionHandler[WorkflowJob]):
43+
@staticmethod
44+
def get_attribute_values(event: GroupEvent, attribute: str) -> list[str]:
45+
path = attribute.split(".")
46+
first_attribute = path[0]
47+
try:
48+
attribute_handler = attribute_registry.get(first_attribute)
49+
except NoRegistrationExistsError:
50+
attribute_handler = None
51+
52+
if not attribute_handler:
53+
attribute_values = []
54+
else:
55+
try:
56+
attribute_values = attribute_handler.handle(path, event)
57+
except KeyError as e:
58+
attribute_values = []
59+
sentry_sdk.capture_exception(e)
60+
61+
attribute_values = [str(value).lower() for value in attribute_values if value is not None]
62+
63+
return attribute_values
64+
65+
@staticmethod
66+
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
67+
comparison_dict = json.loads(comparison)
68+
69+
event = job["event"]
70+
attribute = comparison_dict.get("attribute", "")
71+
attribute_values = EventAttributeConditionHandler.get_attribute_values(event, attribute)
72+
73+
match = comparison_dict.get("match")
74+
desired_value = comparison_dict.get("value")
75+
if not (match and desired_value) and not (match in (MatchType.IS_SET, MatchType.NOT_SET)):
76+
return False
77+
78+
desired_value = str(desired_value).lower()
79+
80+
# NOTE: IS_SET condition differs btw tagged_event and event_attribute so not handled by match_values
81+
if match == MatchType.IS_SET:
82+
return bool(attribute_values)
83+
elif match == MatchType.NOT_SET:
84+
return not attribute_values
85+
86+
return match_values(
87+
group_values=attribute_values, match_value=desired_value, match_type=match
88+
)

src/sentry/workflow_engine/migration_helpers/issue_alert_conditions.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from collections.abc import Callable
22
from typing import Any
33

4+
from sentry.rules.conditions.event_attribute import EventAttributeCondition
45
from sentry.rules.conditions.every_event import EveryEventCondition
56
from sentry.rules.conditions.existing_high_priority_issue import ExistingHighPriorityIssueCondition
67
from sentry.rules.conditions.reappeared_event import ReappearedEventCondition
78
from sentry.rules.conditions.regression_event import RegressionEventCondition
9+
from sentry.utils import json
810
from sentry.utils.registry import Registry
911
from sentry.workflow_engine.models.data_condition import Condition, DataCondition
1012
from sentry.workflow_engine.models.data_condition_group import DataConditionGroup
@@ -65,3 +67,16 @@ def create_existing_high_priority_issue_data_condition(
6567
condition_result=True,
6668
condition_group=dcg,
6769
)
70+
71+
72+
@data_condition_translator_registry.register(EventAttributeCondition.id)
73+
def create_event_attribute_data_condition(
74+
data: dict[str, Any], dcg: DataConditionGroup
75+
) -> DataCondition:
76+
comparison = {"match": data["match"], "value": data["value"], "attribute": data["attribute"]}
77+
return DataCondition.objects.create(
78+
type=Condition.EVENT_ATTRIBUTE,
79+
comparison=json.dumps(comparison),
80+
condition_result=True,
81+
condition_group=dcg,
82+
)

src/sentry/workflow_engine/models/data_condition.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Condition(models.TextChoices):
2020
LESS_OR_EQUAL = "lte"
2121
LESS = "lt"
2222
NOT_EQUAL = "ne"
23+
EVENT_ATTRIBUTE = "event_attribute"
2324
EVENT_CREATED_BY_DETECTOR = "event_created_by_detector"
2425
EVENT_SEEN_COUNT = "event_seen_count"
2526
EVERY_EVENT = "every_event"

0 commit comments

Comments
 (0)