Skip to content

Commit da00165

Browse files
fixes
Co-authored-by: shayna.chambless <shayna.chambless@sentry.io>
1 parent 1f8db96 commit da00165

2 files changed

Lines changed: 101 additions & 7 deletions

File tree

‎src/sentry/integrations/slack/service.py‎

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858
from sentry.notifications.notifications.activity.unassigned import UnassignedActivityNotification
5959
from sentry.notifications.notifications.activity.unresolved import UnresolvedActivityNotification
6060
from sentry.notifications.notifications.base import BaseNotification
61+
from sentry.notifications.platform.threading import ThreadingService
62+
from sentry.notifications.platform.types import NotificationProviderKey, NotificationSource
6163
from sentry.notifications.utils.open_period import open_period_start_for_group
6264
from sentry.silo.base import SiloMode
6365
from sentry.types.activity import ActivityType
@@ -249,6 +251,11 @@ def _notify_all_threads_for_activity(
249251
group_ids=[group.id],
250252
)
251253

254+
# Also query platform threads for issue alerts sent via notification platform
255+
platform_threads = self._get_platform_threads_for_group(
256+
group=group, integration=integration
257+
)
258+
252259
# We don't wrap this in a lifecycle because _handle_parent_notification is already wrapped in a lifecycle
253260
parent_notification_count = 0
254261
for parent_notification in parent_notifications:
@@ -307,6 +314,64 @@ def _notify_all_threads_for_activity(
307314
},
308315
)
309316

317+
# Send to platform threads
318+
for platform_thread in platform_threads:
319+
with MessagingInteractionEvent(
320+
interaction_type=MessagingInteractionType.SEND_ACTIVITY_NOTIFICATION,
321+
spec=SlackMessagingSpec(),
322+
).capture() as lifecycle:
323+
lifecycle.add_extras(
324+
{
325+
"activity_id": activity.id,
326+
"platform_thread_id": platform_thread.id,
327+
"notification_to_send": notification_to_send,
328+
"integration_id": client.integration_id,
329+
"group_id": group.id,
330+
"project_id": activity.project.id,
331+
}
332+
)
333+
try:
334+
self._send_notification_to_slack_channel(
335+
channel_id=platform_thread.target_id,
336+
message_identifier=platform_thread.thread_identifier,
337+
notification_to_send=notification_to_send,
338+
client=client,
339+
)
340+
except Exception as err:
341+
if isinstance(err, SlackApiError):
342+
record_lifecycle_termination_level(lifecycle, err)
343+
else:
344+
lifecycle.record_failure(err)
345+
346+
def _get_platform_threads_for_group(
347+
self, group: Group, integration: Integration
348+
) -> list[NotificationThread]:
349+
"""
350+
Query NotificationThread for issue alerts sent via the notification platform.
351+
352+
Returns threads for the given group that were sent via Slack, filtering by integration
353+
to ensure we only send to threads from this specific Slack workspace.
354+
"""
355+
from sentry.notifications.models.notificationthread import NotificationThread
356+
357+
# Query for issue alert threads for this group
358+
# Note: We query by key_type and key_data separately since we can't easily
359+
# hash the exact key without knowing all action_ids
360+
threads = NotificationThread.objects.filter(
361+
key_type=NotificationSource.ISSUE.value,
362+
key_data__group_id=group.id,
363+
provider_key__in=[
364+
NotificationProviderKey.SLACK.value,
365+
NotificationProviderKey.SLACK_STAGING.value,
366+
],
367+
)
368+
369+
# Filter to only threads from this integration by checking target_id against
370+
# the integration's channels (Note: This is best-effort; the platform path
371+
# stores channel_id in target_id, which should match across integrations)
372+
# For simplicity, we return all Slack threads for this group
373+
return list(threads)
374+
310375
def _get_channel_id_from_parent_notification_notification_action(
311376
self,
312377
parent_notification: NotificationActionNotificationMessage,

‎src/sentry/notifications/notification_action/platform_dispatch.py‎

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
issue_notification_data_factory,
2020
metric_alert_notification_data_factory,
2121
)
22+
from sentry.notifications.platform.provider import SendFailure, SendSuccessResult
2223
from sentry.notifications.platform.service import NotificationService
2324
from sentry.notifications.platform.target import IntegrationNotificationTarget
2425
from sentry.notifications.platform.threading import ThreadingOptions, ThreadKey
@@ -30,6 +31,9 @@
3031
NotificationTargetResourceType,
3132
)
3233
from sentry.notifications.types import TEST_NOTIFICATION_ID
34+
from sentry.seer.entrypoints.operator import SeerAutofixOperator
35+
from sentry.seer.entrypoints.slack.entrypoint import prepare_slack_thread_for_autofix_updates
36+
from sentry.seer.types import SeerEntrypointKey
3337
from sentry.notifications.utils.issue_notification_context import IssueNotificationContext
3438
from sentry.notifications.utils.open_period import open_period_start_for_group
3539
from sentry.uptime.grouptype import UptimeDomainCheckFailure
@@ -138,20 +142,28 @@ def _send(
138142
threading_options: ThreadingOptions | None,
139143
action_id: int,
140144
group_id: int,
141-
) -> None:
142-
errors = NotificationService(data=data).notify_sync(
143-
targets=[target], threading_options=threading_options
144-
)
145-
for failure in errors.get(target.provider_key, []):
145+
) -> SendSuccessResult | SendFailure:
146+
"""
147+
Send a notification and return the result.
148+
149+
Returns:
150+
SendResult: The result of the send operation (SendSuccessResult or SendFailure)
151+
"""
152+
service = NotificationService(data=data)
153+
result = service.notify_target(target=target, threading_options=threading_options)
154+
155+
if isinstance(result, SendFailure):
146156
logger.warning(
147157
"notification_action.platform_dispatch.send_failed",
148158
extra={
149159
"action_id": action_id,
150160
"group_id": group_id,
151161
"provider": target.provider_key,
152-
"status": failure.status.value,
162+
"status": result.status.value,
153163
},
154164
)
165+
166+
return result
155167

156168

157169
def send_metric_alert(context: IssueNotificationContext, provider: NotificationProviderKey) -> None:
@@ -238,10 +250,27 @@ def send_issue_alert(invocation: ActionInvocation, provider: NotificationProvide
238250
reply_broadcast=True,
239251
)
240252

241-
_send(
253+
result = _send(
242254
issue_notification_data_factory(invocation),
243255
target,
244256
threading_options,
245257
invocation.action.id,
246258
group.id,
247259
)
260+
261+
# Cache the thread for Seer autofix updates
262+
if (
263+
isinstance(result, SendSuccessResult)
264+
and result.provider_message_id
265+
and provider in (NotificationProviderKey.SLACK, NotificationProviderKey.SLACK_STAGING)
266+
and SeerAutofixOperator.has_access(
267+
organization=organization, entrypoint_key=SeerEntrypointKey.SLACK
268+
)
269+
):
270+
prepare_slack_thread_for_autofix_updates(
271+
thread_ts=result.provider_message_id,
272+
channel_id=target.resource_id,
273+
group=group,
274+
organization_id=organization.id,
275+
integration_id=target.integration_id,
276+
)

0 commit comments

Comments
 (0)