Skip to content

Commit

Permalink
✨ feat(aci): create legacy issue alert handlers for messaging and onc…
Browse files Browse the repository at this point in the history
…all integrations (#84829)

builds on #84524

adds legacy rule registry invokers/handlers for Messaging and Oncall
Integrations
  • Loading branch information
iamrajjoshi authored Feb 11, 2025
1 parent 66fd267 commit d791c85
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import uuid
from abc import ABC, abstractmethod
from abc import ABC
from collections.abc import Callable, Collection, Sequence
from typing import Any

Expand All @@ -20,6 +20,8 @@
ActionFieldMapping,
ActionFieldMappingKeys,
DiscordDataBlob,
OnCallDataBlob,
SlackDataBlob,
)

logger = logging.getLogger(__name__)
Expand All @@ -33,12 +35,16 @@ class BaseIssueAlertHandler(ABC):
@staticmethod
def get_integration_id(action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
if mapping.get(ActionFieldMappingKeys.INTEGRATION_ID_KEY.value):
if action.integration_id is None:
raise ValueError(f"No integration id found for action type: {action.type}")
return {mapping[ActionFieldMappingKeys.INTEGRATION_ID_KEY.value]: action.integration_id}
raise ValueError(f"No integration id key found for action type: {action.type}")

@classmethod
def get_target_identifier(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
if mapping.get(ActionFieldMappingKeys.TARGET_IDENTIFIER_KEY.value):
if action.target_identifier is None:
raise ValueError(f"No target identifier found for action type: {action.type}")
return {
mapping[
ActionFieldMappingKeys.TARGET_IDENTIFIER_KEY.value
Expand All @@ -49,16 +55,15 @@ def get_target_identifier(cls, action: Action, mapping: ActionFieldMapping) -> d
@classmethod
def get_target_display(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
if mapping.get(ActionFieldMappingKeys.TARGET_DISPLAY_KEY.value):
if action.target_display is None:
raise ValueError(f"No target display found for action type: {action.type}")
return {mapping[ActionFieldMappingKeys.TARGET_DISPLAY_KEY.value]: action.target_display}
raise ValueError(f"No target display key found for action type: {action.type}")

@classmethod
@abstractmethod
def get_additional_fields(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
"""Add additional fields to the blob"""
raise NotImplementedError(
f"get_additional_fields not implemented for action type: {action.type}"
)
return {}

@classmethod
def build_rule_action_blob(
Expand Down Expand Up @@ -186,3 +191,43 @@ def get_additional_fields(cls, action: Action, mapping: ActionFieldMapping) -> d
@classmethod
def get_target_display(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
return {}


@issue_alert_handler_registry.register(Action.Type.MSTEAMS)
class MSTeamsIssueAlertHandler(BaseIssueAlertHandler):
pass


@issue_alert_handler_registry.register(Action.Type.SLACK)
class SlackIssueAlertHandler(BaseIssueAlertHandler):
@classmethod
def get_additional_fields(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
blob = SlackDataBlob(**action.data)
return {
"tags": blob.tags,
"notes": blob.notes,
}


@issue_alert_handler_registry.register(Action.Type.PAGERDUTY)
class PagerDutyIssueAlertHandler(BaseIssueAlertHandler):
@classmethod
def get_target_display(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
return {}

@classmethod
def get_additional_fields(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
blob = OnCallDataBlob(**action.data)
return {"severity": blob.priority}


@issue_alert_handler_registry.register(Action.Type.OPSGENIE)
class OpsgenieIssueAlertHandler(BaseIssueAlertHandler):
@classmethod
def get_target_display(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
return {}

@classmethod
def get_additional_fields(cls, action: Action, mapping: ActionFieldMapping) -> dict[str, Any]:
blob = OnCallDataBlob(**action.data)
return {"priority": blob.priority}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
from sentry.workflow_engine.handlers.action.notification.issue_alert import (
BaseIssueAlertHandler,
DiscordIssueAlertHandler,
MSTeamsIssueAlertHandler,
OpsgenieIssueAlertHandler,
PagerDutyIssueAlertHandler,
SlackIssueAlertHandler,
)
from sentry.workflow_engine.models import Action
from sentry.workflow_engine.types import WorkflowJob
Expand Down Expand Up @@ -75,6 +79,11 @@ def test_create_rule_instance_from_action(self):
assert rule.status == ObjectStatus.ACTIVE
assert rule.source == RuleSource.ISSUE

def test_create_rule_instance_from_action_missing_action_properties_raises_value_error(self):
action = self.create_action(type=Action.Type.DISCORD)
with pytest.raises(ValueError):
self.handler.create_rule_instance_from_action(action, self.detector, self.job)

def test_create_rule_instance_from_action_no_environment(self):
"""Test that create_rule_instance_from_action creates a Rule with correct attributes"""
workflow = self.create_workflow()
Expand Down Expand Up @@ -163,3 +172,136 @@ def test_build_rule_action_blob_no_tags(self):
"channel_id": "channel456",
"tags": "",
}


class TestMSTeamsIssueAlertHandler(BaseWorkflowTest):
def setUp(self):
super().setUp()
self.handler = MSTeamsIssueAlertHandler()
self.action = self.create_action(
type=Action.Type.MSTEAMS,
integration_id="1234567890",
target_identifier="channel789",
target_display="General Channel",
)

def test_build_rule_action_blob(self):
"""Test that build_rule_action_blob creates correct MSTeams action data"""
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.msteams.notify_action.MsTeamsNotifyServiceAction",
"team": "1234567890",
"channel_id": "channel789",
"channel": "General Channel",
}


class TestSlackIssueAlertHandler(BaseWorkflowTest):
def setUp(self):
super().setUp()
self.handler = SlackIssueAlertHandler()
self.action = self.create_action(
type=Action.Type.SLACK,
integration_id="1234567890",
target_identifier="channel789",
target_display="#general",
data={"tags": "environment,user", "notes": "Important alert"},
)

def test_build_rule_action_blob(self):
"""Test that build_rule_action_blob creates correct Slack action data"""
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.slack.notify_action.SlackNotifyServiceAction",
"workspace": "1234567890",
"channel_id": "channel789",
"channel": "#general",
"tags": "environment,user",
"notes": "Important alert",
}

def test_build_rule_action_blob_no_data(self):
"""Test that build_rule_action_blob handles missing data"""
self.action.data = {}
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.slack.notify_action.SlackNotifyServiceAction",
"workspace": "1234567890",
"channel_id": "channel789",
"channel": "#general",
"tags": "",
"notes": "",
}


class TestPagerDutyIssueAlertHandler(BaseWorkflowTest):
def setUp(self):
super().setUp()
self.handler = PagerDutyIssueAlertHandler()
self.action = self.create_action(
type=Action.Type.PAGERDUTY,
integration_id="1234567890",
target_identifier="service789",
data={"priority": "P1"},
)

def test_build_rule_action_blob(self):
"""Test that build_rule_action_blob creates correct PagerDuty action data"""
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.pagerduty.notify_action.PagerDutyNotifyServiceAction",
"account": "1234567890",
"service": "service789",
"severity": "P1",
}

def test_build_rule_action_blob_no_priority(self):
"""Test that build_rule_action_blob handles missing priority"""
self.action.data = {}
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.pagerduty.notify_action.PagerDutyNotifyServiceAction",
"account": "1234567890",
"service": "service789",
"severity": "",
}


class TestOpsgenieIssueAlertHandler(BaseWorkflowTest):
def setUp(self):
super().setUp()
self.handler = OpsgenieIssueAlertHandler()
self.action = self.create_action(
type=Action.Type.OPSGENIE,
integration_id="1234567890",
target_identifier="team789",
data={"priority": "P1"},
)

def test_build_rule_action_blob(self):
"""Test that build_rule_action_blob creates correct Opsgenie action data"""
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.opsgenie.notify_action.OpsgenieNotifyTeamAction",
"account": "1234567890",
"team": "team789",
"priority": "P1",
}

def test_build_rule_action_blob_no_priority(self):
"""Test that build_rule_action_blob handles missing priority"""
self.action.data = {}
blob = self.handler.build_rule_action_blob(self.action)

assert blob == {
"id": "sentry.integrations.opsgenie.notify_action.OpsgenieNotifyTeamAction",
"account": "1234567890",
"team": "team789",
"priority": "",
}

0 comments on commit d791c85

Please sign in to comment.