Skip to content

Commit

Permalink
feat(workflow-engine): add FirstSeenEventConditionHandler (#82822)
Browse files Browse the repository at this point in the history
add `FirstSeenEventConditionHandler`

note that we need to add `Workflow` to `WorkflowJob` in order to get the
environment id
  • Loading branch information
ameliahsu authored Jan 3, 2025
1 parent fa25473 commit 6484e45
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 1 deletion.
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 @@ -6,6 +6,7 @@
"RegressionEventConditionHandler",
"ExistingHighPriorityIssueConditionHandler",
"EventAttributeConditionHandler",
"FirstSeenEventConditionHandler",
]

from .group_event_handlers import (
Expand All @@ -16,6 +17,7 @@
)
from .group_state_handlers import (
ExistingHighPriorityIssueConditionHandler,
FirstSeenEventConditionHandler,
ReappearedEventConditionHandler,
RegressionEventConditionHandler,
)
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,18 @@ def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
has_escalated = job.get("has_escalated", False)
is_escalating = has_reappeared or has_escalated
return is_escalating and job["event"].group.priority == PriorityLevel.HIGH


@condition_handler_registry.register(Condition.FIRST_SEEN_EVENT)
class FirstSeenEventConditionHandler(DataConditionHandler[WorkflowJob]):
@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
state = job.get("group_state")
if state is None:
return False

workflow = job.get("workflow")
if workflow is None or workflow.environment_id is None:
return state["is_new"]

return state["is_new_group_environment"]
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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.first_seen_event import FirstSeenEventCondition
from sentry.rules.conditions.reappeared_event import ReappearedEventCondition
from sentry.rules.conditions.regression_event import RegressionEventCondition
from sentry.utils.registry import Registry
Expand Down Expand Up @@ -79,3 +80,15 @@ def create_event_attribute_data_condition(
condition_result=True,
condition_group=dcg,
)


@data_condition_translator_registry.register(FirstSeenEventCondition.id)
def create_first_seen_event_data_condition(
data: dict[str, Any], dcg: DataConditionGroup
) -> DataCondition:
return DataCondition.objects.create(
type=Condition.FIRST_SEEN_EVENT,
comparison=True,
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 @@ -25,6 +25,7 @@ class Condition(models.TextChoices):
EVENT_SEEN_COUNT = "event_seen_count"
EVERY_EVENT = "every_event"
EXISTING_HIGH_PRIORITY_ISSUE = "existing_high_priority_issue"
FIRST_SEEN_EVENT = "first_seen_event"
REGRESSION_EVENT = "regression_event"
REAPPEARED_EVENT = "reappeared_event"

Expand Down
1 change: 1 addition & 0 deletions src/sentry/workflow_engine/models/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def evaluate_trigger_conditions(self, job: WorkflowJob) -> bool:
if self.when_condition_group is None:
return True

job["workflow"] = self
evaluation, _ = evaluate_condition_group(self.when_condition_group, job)
return evaluation

Expand Down
3 changes: 2 additions & 1 deletion src/sentry/workflow_engine/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
if TYPE_CHECKING:
from sentry.eventstore.models import GroupEvent
from sentry.eventstream.base import GroupState
from sentry.workflow_engine.models import Action, Detector
from sentry.workflow_engine.models import Action, Detector, Workflow

T = TypeVar("T")

Expand Down Expand Up @@ -39,6 +39,7 @@ class WorkflowJob(EventJob, total=False):
has_reappeared: bool
has_alert: bool
has_escalated: bool
workflow: Workflow


class ActionHandler:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from sentry.eventstream.base import GroupState
from sentry.rules.conditions.existing_high_priority_issue import ExistingHighPriorityIssueCondition
from sentry.rules.conditions.first_seen_event import FirstSeenEventCondition
from sentry.rules.conditions.reappeared_event import ReappearedEventCondition
from sentry.rules.conditions.regression_event import RegressionEventCondition
from sentry.types.group import PriorityLevel
from sentry.workflow_engine.models.data_condition import Condition
from sentry.workflow_engine.models.workflow import Workflow
from sentry.workflow_engine.types import WorkflowJob
from tests.sentry.workflow_engine.handlers.condition.test_base import ConditionTestCase

Expand Down Expand Up @@ -145,3 +147,63 @@ def test_dual_write(self):
assert dc.comparison is True
assert dc.condition_result is True
assert dc.condition_group == dcg


class TestFirstSeenEventCondition(ConditionTestCase):
condition = Condition.FIRST_SEEN_EVENT
rule_cls = FirstSeenEventCondition
payload = {"id": FirstSeenEventCondition.id}

def setUp(self):
super().setUp()
self.job = WorkflowJob(
{
"event": self.group_event,
"group_state": GroupState(
{
"id": 1,
"is_regression": True,
"is_new": True,
"is_new_group_environment": True,
}
),
"workflow": Workflow(environment_id=None),
}
)
self.dc = self.create_data_condition(
type=self.condition,
comparison=True,
condition_result=True,
)

def test(self):
self.assert_passes(self.dc, self.job)

self.job["group_state"]["is_new"] = False
self.assert_does_not_pass(self.dc, self.job)

def test_with_environment(self):
self.job["workflow"] = Workflow(environment_id=1)

self.assert_passes(self.dc, self.job)

self.job["group_state"]["is_new"] = False
self.job["group_state"]["is_new_group_environment"] = True
self.assert_passes(self.dc, self.job)

self.job["group_state"]["is_new"] = True
self.job["group_state"]["is_new_group_environment"] = False
self.assert_does_not_pass(self.dc, self.job)

self.job["group_state"]["is_new"] = False
self.job["group_state"]["is_new_group_environment"] = False
self.assert_does_not_pass(self.dc, self.job)

def test_dual_write(self):
dcg = self.create_data_condition_group()
dc = self.translate_to_data_condition(self.payload, dcg)

assert dc.type == self.condition
assert dc.comparison is True
assert dc.condition_result is True
assert dc.condition_group == dcg

0 comments on commit 6484e45

Please sign in to comment.