From dae574617dc53d8116852908018dc9d34e5af5ad Mon Sep 17 00:00:00 2001 From: Raj Joshi Date: Mon, 14 Apr 2025 19:58:22 -0700 Subject: [PATCH 1/3] :wrench: chore: update msteams notifications typing --- pyproject.toml | 1 - .../integrations/msteams/notifications.py | 29 ++++++++++++++----- src/sentry/integrations/msteams/utils.py | 4 +-- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0704f32954ca92..9a886af38ffcdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -131,7 +131,6 @@ module = [ "sentry.integrations.github.integration", "sentry.integrations.gitlab.issues", "sentry.integrations.jira.integration", - "sentry.integrations.msteams.notifications", "sentry.integrations.pagerduty.actions.form", "sentry.integrations.pipeline", "sentry.integrations.slack.message_builder.notifications.issues", diff --git a/src/sentry/integrations/msteams/notifications.py b/src/sentry/integrations/msteams/notifications.py index 0152243dabc5b1..f4f01915b2835a 100644 --- a/src/sentry/integrations/msteams/notifications.py +++ b/src/sentry/integrations/msteams/notifications.py @@ -10,8 +10,8 @@ from sentry.integrations.msteams.utils import get_user_conversation_id from sentry.integrations.notifications import get_context, get_integrations_by_channel_by_recipient from sentry.integrations.types import ExternalProviders -from sentry.models.team import Team from sentry.notifications.notifications.activity.assigned import AssignedActivityNotification +from sentry.notifications.notifications.activity.base import GroupActivityNotification from sentry.notifications.notifications.activity.escalating import EscalatingActivityNotification from sentry.notifications.notifications.activity.note import NoteActivityNotification from sentry.notifications.notifications.activity.regression import RegressionActivityNotification @@ -25,7 +25,6 @@ from sentry.notifications.notifications.rules import AlertRuleNotification from sentry.notifications.notify import register_notification_provider from sentry.types.actor import Actor -from sentry.users.models.user import User from sentry.utils import metrics from .card_builder.notifications import ( @@ -47,11 +46,15 @@ RegressionActivityNotification, EscalatingActivityNotification, ] -MESSAGE_BUILDERS = { - "SlackNotificationsMessageBuilder": MSTeamsNotificationsMessageBuilder, + +GROUP_MESSAGE_BUILDERS = { "IssueNotificationMessageBuilder": MSTeamsIssueNotificationsMessageBuilder, } +BASE_MESSAGE_BUILDERS = { + "SlackNotificationsMessageBuilder": MSTeamsNotificationsMessageBuilder, +} + def is_supported_notification_type(notification: BaseNotification) -> bool: return any( @@ -62,10 +65,17 @@ def is_supported_notification_type(notification: BaseNotification) -> bool: ) -def get_notification_card( - notification: BaseNotification, context: Mapping[str, Any], recipient: User | Team | Actor +def get_group_notification_card( + notification: GroupActivityNotification, context: Mapping[str, Any], recipient: Actor +) -> AdaptiveCard: + cls = GROUP_MESSAGE_BUILDERS[notification.message_builder] + return cls(notification, context, recipient).build_notification_card() + + +def get_base_notification_card( + notification: BaseNotification, context: Mapping[str, Any], recipient: Actor ) -> AdaptiveCard: - cls = MESSAGE_BUILDERS[notification.message_builder] + cls = BASE_MESSAGE_BUILDERS[notification.message_builder] return cls(notification, context, recipient).build_notification_card() @@ -95,7 +105,10 @@ def send_notification_as_msteams( context = get_context(notification, recipient, shared_context, extra_context) with sentry_sdk.start_span(op="notification.send_msteams", name="gen_attachments"): - card = get_notification_card(notification, context, recipient) + if isinstance(notification, GroupActivityNotification): + card = get_group_notification_card(notification, context, recipient) + else: + card = get_base_notification_card(notification, context, recipient) for channel, integration in integrations_by_channel.items(): conversation_id = get_user_conversation_id(integration, channel) diff --git a/src/sentry/integrations/msteams/utils.py b/src/sentry/integrations/msteams/utils.py index 5d242fe533ebeb..71f749aa14883d 100644 --- a/src/sentry/integrations/msteams/utils.py +++ b/src/sentry/integrations/msteams/utils.py @@ -12,7 +12,7 @@ OpenPeriodContext, ) from sentry.integrations.models.integration import Integration -from sentry.integrations.services.integration import integration_service +from sentry.integrations.services.integration import RpcIntegration, integration_service from sentry.models.organization import Organization from .client import MsTeamsClient, MsTeamsPreInstallClient, get_token_data @@ -41,7 +41,7 @@ def channel_filter(channel, name): return name.lower() == "general" -def get_user_conversation_id(integration: Integration, user_id: str) -> str: +def get_user_conversation_id(integration: Integration | RpcIntegration, user_id: str) -> str: """ Get the user_conversation_id even if `integration.metadata.tenant_id` is not set. """ From 41a5d16b435acedd1eb92123794abb9318ab030c Mon Sep 17 00:00:00 2001 From: Raj Joshi Date: Mon, 14 Apr 2025 20:41:15 -0700 Subject: [PATCH 2/3] :bug: fix: fix typing --- src/sentry/integrations/msteams/notifications.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sentry/integrations/msteams/notifications.py b/src/sentry/integrations/msteams/notifications.py index f4f01915b2835a..2e5d1ae21041f1 100644 --- a/src/sentry/integrations/msteams/notifications.py +++ b/src/sentry/integrations/msteams/notifications.py @@ -47,14 +47,15 @@ EscalatingActivityNotification, ] -GROUP_MESSAGE_BUILDERS = { - "IssueNotificationMessageBuilder": MSTeamsIssueNotificationsMessageBuilder, -} - BASE_MESSAGE_BUILDERS = { "SlackNotificationsMessageBuilder": MSTeamsNotificationsMessageBuilder, } +# For Group-based notifications, it is possible we use a builder that is generic OR uses a group-specific builder +GROUP_MESSAGE_BUILDERS = BASE_MESSAGE_BUILDERS | { + "IssueNotificationMessageBuilder": MSTeamsIssueNotificationsMessageBuilder, +} + def is_supported_notification_type(notification: BaseNotification) -> bool: return any( From 80f9dac5cea8a1166fa7e5d66d3bed520ae7ae8c Mon Sep 17 00:00:00 2001 From: Raj Joshi Date: Mon, 14 Apr 2025 21:03:30 -0700 Subject: [PATCH 3/3] :wrench: chore: add better typing --- .../integrations/msteams/card_builder/notifications.py | 3 ++- src/sentry/integrations/msteams/notifications.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sentry/integrations/msteams/card_builder/notifications.py b/src/sentry/integrations/msteams/card_builder/notifications.py index ef447868248c2c..ec4faf22e8f510 100644 --- a/src/sentry/integrations/msteams/card_builder/notifications.py +++ b/src/sentry/integrations/msteams/card_builder/notifications.py @@ -14,6 +14,7 @@ from sentry.integrations.types import ExternalProviders from sentry.notifications.notifications.activity.base import GroupActivityNotification from sentry.notifications.notifications.base import BaseNotification +from sentry.notifications.notifications.rules import AlertRuleNotification from sentry.notifications.utils.actions import MessageAction from sentry.types.actor import Actor @@ -122,7 +123,7 @@ def build_notification_card(self): class MSTeamsIssueNotificationsMessageBuilder(MSTeamsNotificationsMessageBuilder): def __init__( self, - notification: GroupActivityNotification, + notification: GroupActivityNotification | AlertRuleNotification, context: Mapping[str, Any], recipient: Actor, ): diff --git a/src/sentry/integrations/msteams/notifications.py b/src/sentry/integrations/msteams/notifications.py index 2e5d1ae21041f1..a0e5026270ccdb 100644 --- a/src/sentry/integrations/msteams/notifications.py +++ b/src/sentry/integrations/msteams/notifications.py @@ -67,7 +67,9 @@ def is_supported_notification_type(notification: BaseNotification) -> bool: def get_group_notification_card( - notification: GroupActivityNotification, context: Mapping[str, Any], recipient: Actor + notification: GroupActivityNotification | AlertRuleNotification, + context: Mapping[str, Any], + recipient: Actor, ) -> AdaptiveCard: cls = GROUP_MESSAGE_BUILDERS[notification.message_builder] return cls(notification, context, recipient).build_notification_card() @@ -106,7 +108,9 @@ def send_notification_as_msteams( context = get_context(notification, recipient, shared_context, extra_context) with sentry_sdk.start_span(op="notification.send_msteams", name="gen_attachments"): - if isinstance(notification, GroupActivityNotification): + if isinstance(notification, GroupActivityNotification) or isinstance( + notification, AlertRuleNotification + ): card = get_group_notification_card(notification, context, recipient) else: card = get_base_notification_card(notification, context, recipient)