Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(workflow-engine): add EventAttributeConditionHandler #82741

Merged
merged 4 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/sentry/workflow_engine/handlers/condition/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
"ReappearedEventConditionHandler",
"RegressionEventConditionHandler",
"ExistingHighPriorityIssueConditionHandler",
"EventAttributeConditionHandler",
]

from .group_event_handlers import (
EventAttributeConditionHandler,
EventCreatedByDetectorConditionHandler,
EventSeenCountConditionHandler,
EveryEventConditionHandler,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from typing import Any

import sentry_sdk

from sentry.rules import MatchType, match_values
from sentry.rules.conditions.event_attribute import attribute_registry
from sentry.utils.registry import NoRegistrationExistsError
from sentry.workflow_engine.models.data_condition import Condition
from sentry.workflow_engine.registry import condition_handler_registry
from sentry.workflow_engine.types import DataConditionHandler, WorkflowJob
Expand Down Expand Up @@ -29,3 +34,45 @@ class EventSeenCountConditionHandler(DataConditionHandler[WorkflowJob]):
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
return event.group.times_seen == comparison


@condition_handler_registry.register(Condition.EVENT_ATTRIBUTE)
class EventAttributeConditionHandler(DataConditionHandler[WorkflowJob]):
@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: if there's a lot happening in this function you can also break it up into several smaller ones

event = job["event"]

attribute = comparison.get("attribute", "")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think comparison is stored as a string so you'll need to do json.loads(comparison) to get it as a dictionary

path = attribute.split(".")
first_attribute = path[0]
try:
attribute_handler = attribute_registry.get(first_attribute)
except NoRegistrationExistsError:
attribute_handler = None

if not attribute_handler:
attribute_values = []
else:
try:
attribute_values = attribute_handler.handle(path, event)
except KeyError as e:
attribute_values = []
sentry_sdk.capture_exception(e)

match = comparison.get("match")
desired_value = comparison.get("value")
if not (match and desired_value) and not (match in (MatchType.IS_SET, MatchType.NOT_SET)):
return False

desired_value = str(desired_value).lower()
attribute_values = [str(value).lower() for value in attribute_values if value is not None]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this go inside get_attribute_values?


# NOTE: IS_SET condition differs btw tagged_event and event_attribute so not handled by match_values
if match == MatchType.IS_SET:
return bool(attribute_values)
elif match == MatchType.NOT_SET:
return not attribute_values

return match_values(
group_values=attribute_values, match_value=desired_value, match_type=match
)
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections.abc import Callable
from typing import Any

from sentry.rules.conditions.event_attribute import EventAttributeCondition
from sentry.rules.conditions.every_event import EveryEventCondition
from sentry.rules.conditions.existing_high_priority_issue import ExistingHighPriorityIssueCondition
from sentry.rules.conditions.reappeared_event import ReappearedEventCondition
Expand Down Expand Up @@ -65,3 +66,15 @@ def create_existing_high_priority_issue_data_condition(
condition_result=True,
condition_group=dcg,
)


@data_condition_translator_registry.register(EventAttributeCondition.id)
def create_event_attribute_data_condition(
data: dict[str, Any], dcg: DataConditionGroup
) -> DataCondition:
return DataCondition.objects.create(
type=Condition.EVENT_ATTRIBUTE,
comparison=True,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where are the attribute, match type, and desired value being stored? one way to do this is to put them in a dictionary and json.dump them into comparison here

condition_result=True,
condition_group=dcg,
)
1 change: 1 addition & 0 deletions src/sentry/workflow_engine/models/data_condition.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Condition(models.TextChoices):
LESS_OR_EQUAL = "lte"
LESS = "lt"
NOT_EQUAL = "ne"
EVENT_ATTRIBUTE = "event_attribute"
EVENT_CREATED_BY_DETECTOR = "event_created_by_detector"
EVENT_SEEN_COUNT = "event_seen_count"
EVERY_EVENT = "every_event"
Expand Down
Loading