Skip to content

Commit

Permalink
feat(workflow_engine): Add a DataConditionHandlerType to each DataC…
Browse files Browse the repository at this point in the history
…onditionHandler (#83276)

## Description
As we were implementing DataConditionHandlers, I realized we didn't have
a way to filter each handler in an API for the UI. This allow users to
select the type of handler they want, and then configure it.

To address this, adding a static type to the condition handlers to match
where in the product they'll be used;
- detector triggers (used for setting thresholds and then results give
detector states)
- workflow triggers (used for workflow conditions, to evaluate if the
issues are new / regressed, etc)
- action filters (used to decide if we want to invoke an action in a
workflow or not, if a tag is set or a priority needs to be met)

---------

Co-authored-by: Cathy Teng <[email protected]>
Co-authored-by: getsantry[bot] <66042841+getsantry[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 15, 2025
1 parent 05cf949 commit 792981b
Show file tree
Hide file tree
Showing 16 changed files with 53 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@
from sentry.rules.filters.age_comparison import timeranges
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.AGE_COMPARISON)
class AgeComparisonConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

comparison_json_schema = {
"type": "object",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
from sentry.utils.cache import cache
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.ASSIGNED_TO)
class AssignedToConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def get_assignees(group: Group) -> Sequence[GroupAssignee]:
cache_key = f"group:{group.id}:assignees"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.EVENT_ATTRIBUTE)
class EventAttributeConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def get_attribute_values(event: GroupEvent, attribute: str) -> list[str]:
path = attribute.split(".")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.EVENT_CREATED_BY_DETECTOR)
class EventCreatedByDetectorConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.EVENT_SEEN_COUNT)
class EventSeenCountConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.EVERY_EVENT)
class EveryEventConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
return True
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
from sentry.types.group import PriorityLevel
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.EXISTING_HIGH_PRIORITY_ISSUE)
class ExistingHighPriorityIssueConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
state = job.get("group_state")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


def is_new_event(job: WorkflowJob) -> bool:
Expand All @@ -19,6 +19,8 @@ def is_new_event(job: WorkflowJob) -> bool:

@condition_handler_registry.register(Condition.FIRST_SEEN_EVENT)
class FirstSeenEventConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
return is_new_event(job)
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
from sentry.models.group import Group
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.ISSUE_OCCURRENCES)
class IssueOccurrencesConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
group: Group = job["event"].group
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from sentry.utils.cache import cache
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


def get_latest_release_for_env(
Expand Down Expand Up @@ -41,6 +41,8 @@ def get_latest_release_for_env(

@condition_handler_registry.register(Condition.LATEST_RELEASE)
class LatestReleaseConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
from sentry.rules import MatchType
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.LEVEL)
class LevelConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
from sentry.workflow_engine.handlers.condition.first_seen_event_handler import is_new_event
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.NEW_HIGH_PRIORITY_ISSUE)
class NewHighPriorityIssueConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
is_new = is_new_event(job)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.REAPPEARED_EVENT)
class ReappearedEventConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
has_reappeared = job.get("has_reappeared")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.REGRESSION_EVENT)
class RegressionEventConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.WORKFLOW_TRIGGER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
state = job.get("group_state")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
from sentry.rules import MatchType, match_values
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
from sentry.workflow_engine.types import DataConditionHandler, DataConditionHandlerType, WorkflowJob


@condition_handler_registry.register(Condition.TAGGED_EVENT)
class TaggedEventConditionHandler(DataConditionHandler[WorkflowJob]):
type = DataConditionHandlerType.ACTION_FILTER

@staticmethod
def evaluate_value(job: WorkflowJob, comparison: Any) -> bool:
event = job["event"]
Expand Down
9 changes: 8 additions & 1 deletion src/sentry/workflow_engine/types.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from __future__ import annotations

from enum import IntEnum
from enum import IntEnum, StrEnum
from typing import TYPE_CHECKING, Any, ClassVar, Generic, TypedDict, TypeVar

from sentry.types.group import PriorityLevel
Expand All @@ -20,6 +20,12 @@ class DetectorPriorityLevel(IntEnum):
HIGH = PriorityLevel.HIGH


class DataConditionHandlerType(StrEnum):
DETECTOR_TRIGGER = "detector_trigger"
WORKFLOW_TRIGGER = "workflow_trigger"
ACTION_FILTER = "action_filter"


# The unique key used to identify a group within a DataPacket result.
# For DataPackets that don't contain multiple values the key is just None.
# This is stored in 'DetectorState.detector_group_key'
Expand Down Expand Up @@ -55,6 +61,7 @@ def bulk_get_query_object(data_sources) -> dict[int, T | None]:


class DataConditionHandler(Generic[T]):
type: ClassVar[DataConditionHandlerType] = DataConditionHandlerType.ACTION_FILTER
comparison_json_schema: ClassVar[dict[str, Any]] = {}

@staticmethod
Expand Down

0 comments on commit 792981b

Please sign in to comment.