Skip to content

Commit 4c67d2f

Browse files
fix(np): Adds attachment to Slack render type, updates metric renderer (#112312)
1 parent 266c9b6 commit 4c67d2f

File tree

9 files changed

+72
-20
lines changed

9 files changed

+72
-20
lines changed

src/sentry/integrations/slack/integration.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,12 @@ def send_notification(
114114
client = self.get_client()
115115
try:
116116
client.chat_postMessage(
117-
channel=target.resource_id, blocks=payload["blocks"], text=payload["text"]
117+
channel=target.resource_id,
118+
blocks=payload["blocks"] if len(payload["blocks"]) > 0 else None,
119+
text=payload["text"],
120+
attachments=payload.get("attachments"),
121+
unfurl_links=False,
122+
unfurl_media=False,
118123
)
119124
except SlackApiError as e:
120125
translate_slack_api_error(e)
@@ -132,8 +137,11 @@ def send_notification_with_threading(
132137
)
133138
kwargs: dict[str, Any] = dict(
134139
channel=target.resource_id,
135-
blocks=payload["blocks"],
140+
blocks=payload["blocks"] if len(payload["blocks"]) > 0 else None,
136141
text=payload["text"],
142+
attachments=payload.get("attachments"),
143+
unfurl_links=False,
144+
unfurl_media=False,
137145
)
138146

139147
if threading_context.thread_ts is not None:
@@ -159,8 +167,9 @@ def send_threaded_message(
159167
try:
160168
client.chat_postMessage(
161169
channel=channel_id,
162-
blocks=renderable["blocks"],
170+
blocks=renderable["blocks"] if len(renderable["blocks"]) > 0 else None,
163171
text=renderable["text"],
172+
attachments=renderable.get("attachments"),
164173
thread_ts=thread_ts,
165174
)
166175
except SlackApiError as e:
@@ -178,7 +187,8 @@ def send_threaded_ephemeral_message(
178187
try:
179188
client.chat_postEphemeral(
180189
channel=channel_id,
181-
blocks=renderable["blocks"],
190+
blocks=renderable["blocks"] if len(renderable["blocks"]) > 0 else None,
191+
attachments=renderable.get("attachments"),
182192
text=renderable["text"],
183193
thread_ts=thread_ts,
184194
user=slack_user_id,
@@ -199,7 +209,10 @@ def update_message(
199209
channel=channel_id,
200210
ts=message_ts,
201211
text=renderable["text"],
202-
blocks=renderable["blocks"],
212+
blocks=renderable["blocks"] if len(renderable["blocks"]) > 0 else None,
213+
attachments=renderable.get("attachments"),
214+
unfurl_links=False,
215+
unfurl_media=False,
203216
)
204217
except SlackApiError as e:
205218
translate_slack_api_error(e)

src/sentry/notifications/platform/api/endpoints/internal_registered_templates.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from sentry.notifications.platform.email.provider import EmailRenderer
1313
from sentry.notifications.platform.msteams.provider import MSTeamsRenderable, MSTeamsRenderer
1414
from sentry.notifications.platform.registry import template_registry
15-
from sentry.notifications.platform.slack.provider import SlackRenderer
15+
from sentry.notifications.platform.slack.provider import SlackNotificationProvider
1616
from sentry.notifications.platform.types import (
1717
NotificationData,
1818
NotificationProviderKey,
@@ -92,13 +92,14 @@ def serialize_slack_preview[T: NotificationData](
9292
) -> dict[str, Any]:
9393
data = template.example_data
9494
rendered_template = template.render_example()
95-
message = SlackRenderer.render(data=data, rendered_template=rendered_template)
95+
renderer = SlackNotificationProvider.get_renderer(data=data, category=template.category)
96+
message = renderer.render(data=data, rendered_template=rendered_template)
9697

9798
serialized_blocks = []
9899
for block in message.get("blocks", []):
99100
serialized_blocks.append(block.to_dict())
100101

101-
return {"blocks": serialized_blocks}
102+
return {"blocks": serialized_blocks, "attachments": message.get("attachments")}
102103

103104

104105
def serialize_discord_preview[T: NotificationData](

src/sentry/notifications/platform/slack/provider.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from dataclasses import dataclass
4-
from typing import TYPE_CHECKING, NotRequired, TypedDict
4+
from typing import TYPE_CHECKING, Any, NotRequired, TypedDict
55

66
from slack_sdk.models.blocks import (
77
ActionsBlock,
@@ -58,8 +58,8 @@ class SlackProviderThreadingContext(ProviderThreadingContext):
5858

5959
class SlackRenderable(TypedDict):
6060
blocks: list[Block]
61+
attachments: NotRequired[list[dict[str, Any]]]
6162
text: str
62-
color: NotRequired[str]
6363

6464

6565
class SlackRenderer(NotificationRenderer[SlackRenderable]):

src/sentry/notifications/platform/slack/renderers/metric_alert.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,15 @@ def render[DataT: NotificationData](
4343
*blocks, fallback_text=fallback_text, color=color
4444
)
4545

46+
attachment_blocks = [{"blocks": slack_body.get("blocks", [])}]
47+
48+
if color:
49+
attachment_blocks[0]["color"] = color
50+
4651
renderable = SlackRenderable(
47-
blocks=slack_body.get("blocks", []),
52+
blocks=[],
53+
attachments=attachment_blocks,
4854
text=slack_body.get("text", ""),
4955
)
50-
if (color := slack_body.get("color")) is not None:
51-
renderable["color"] = color
5256

5357
return renderable

src/sentry/notifications/platform/templates/metric_alert.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class MetricAlertNotificationData(NotificationData):
4242
@template_registry.register(NotificationSource.METRIC_ALERT)
4343
class MetricAlertNotificationTemplate(NotificationTemplate[MetricAlertNotificationData]):
4444
category = NotificationCategory.METRIC_ALERT
45-
hide_from_debugger = True
4645
example_data = MetricAlertNotificationData(
4746
group_id=1,
4847
organization_id=1,

tests/sentry/integrations/slack/test_integration.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,9 @@ def test_send_notification_success(self, mock_chat_post: MagicMock) -> None:
544544
channel="C1234567890",
545545
blocks=self.slack_renderable.get("blocks", []),
546546
text="Mock Notification",
547+
attachments=None,
548+
unfurl_links=False,
549+
unfurl_media=False,
547550
)
548551

549552
@patch("sentry.integrations.slack.sdk_client.SlackSdkClient.chat_postMessage")
@@ -569,6 +572,7 @@ def test_send_threaded_message_success(self, mock_chat_post: MagicMock) -> None:
569572
channel=self.channel_id,
570573
thread_ts=self.thread_ts,
571574
blocks=self.slack_renderable.get("blocks", []),
575+
attachments=self.slack_renderable.get("attachments"),
572576
text=self.slack_renderable.get("text", ""),
573577
)
574578

@@ -596,6 +600,7 @@ def test_send_threaded_ephemeral_message_success(self, mock_chat_ephemeral: Magi
596600
mock_chat_ephemeral.assert_called_once_with(
597601
channel=self.channel_id,
598602
thread_ts=self.thread_ts,
603+
attachments=self.slack_renderable.get("attachments"),
599604
user=self.slack_user_id,
600605
blocks=self.slack_renderable.get("blocks", []),
601606
text=self.slack_renderable.get("text", ""),

tests/sentry/notifications/notification_action/metric_alert_registry/test_slack_metric_alert_handler.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,10 @@ def test_send_alert_via_np_sends_to_slack_channel(
105105
mock_client_instance.chat_postMessage.assert_called_once()
106106
call_kwargs = mock_client_instance.chat_postMessage.call_args.kwargs
107107
assert call_kwargs["channel"] == "channel123"
108-
blocks = call_kwargs["blocks"]
108+
assert call_kwargs["attachments"] is not None
109+
attachments: list[Any] = call_kwargs["attachments"]
110+
assert len(attachments) == 1
111+
blocks: list[Any] = attachments[0]["blocks"]
109112
assert len(blocks) >= 1
110113
assert blocks[0]["type"] == "section"
111114
assert blocks[0]["text"]["type"] == "mrkdwn"

tests/sentry/notifications/platform/slack/renderers/test_metric_alert.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ def test_render_produces_blocks(self) -> None:
9696
)
9797

9898
# Without a chart: exactly one section block
99-
blocks: list[Any] = result["blocks"]
99+
assert result.get("attachments") is not None
100+
attachments: list[Any] = result["attachments"]
101+
assert len(attachments) == 1
102+
blocks: list[Any] = attachments[0]["blocks"]
100103
assert len(blocks) == 1
101104
assert blocks[0]["type"] == "section"
102105
assert blocks[0]["text"]["type"] == "mrkdwn"
@@ -120,8 +123,10 @@ def test_render_includes_image_block_when_chart_url_set(self) -> None:
120123
rendered_template=self.rendered_template,
121124
)
122125

126+
assert result.get("attachments") is not None
127+
123128
# With a chart: section block + image block
124-
blocks: list[Any] = result["blocks"]
129+
blocks: list[Any] = result["attachments"][0]["blocks"]
125130
assert len(blocks) == 2
126131
assert blocks[0]["type"] == "section"
127132
assert "123.45 events in the last minute" in blocks[0]["text"]["text"]
@@ -135,7 +140,10 @@ def test_render_without_chart_url(self) -> None:
135140
rendered_template=self.rendered_template,
136141
)
137142

138-
blocks: list[Any] = result["blocks"]
143+
assert result.get("attachments") is not None
144+
attachments: list[Any] = result["attachments"]
145+
assert len(attachments) == 1
146+
blocks: list[Any] = attachments[0]["blocks"]
139147
assert len(blocks) == 1
140148
assert blocks[0]["type"] == "section"
141149

tests/sentry/notifications/platform/slack/test_provider.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,12 @@ def test_send_success(self, mock_slack_client: Mock) -> None:
148148
assert isinstance(result, SendSuccessResult)
149149
assert result.provider_message_id is None
150150
mock_client_instance.chat_postMessage.assert_called_once_with(
151-
channel="C1234567890", blocks=renderable["blocks"], text=renderable["text"]
151+
channel="C1234567890",
152+
blocks=renderable["blocks"],
153+
text=renderable["text"],
154+
attachments=None,
155+
unfurl_links=False,
156+
unfurl_media=False,
152157
)
153158

154159
def test_send_invalid_target_class(self) -> None:
@@ -185,7 +190,12 @@ def test_send_to_direct_message(self, mock_slack_client: Mock) -> None:
185190
SlackNotificationProvider.send(target=target, renderable=renderable)
186191

187192
mock_client_instance.chat_postMessage.assert_called_once_with(
188-
channel="U1234567890", blocks=renderable["blocks"], text=renderable["text"]
193+
channel="U1234567890",
194+
blocks=renderable["blocks"],
195+
text=renderable["text"],
196+
attachments=None,
197+
unfurl_links=False,
198+
unfurl_media=False,
189199
)
190200

191201

@@ -278,6 +288,9 @@ def test_send_with_thread_context_existing_thread(self, mock_slack_client: Mock)
278288
blocks=renderable["blocks"],
279289
text=renderable["text"],
280290
thread_ts="1111111111.111111",
291+
attachments=None,
292+
unfurl_links=False,
293+
unfurl_media=False,
281294
)
282295

283296
@patch("sentry.integrations.slack.integration.SlackSdkClient")
@@ -316,6 +329,9 @@ def test_send_with_thread_context_reply_broadcast(self, mock_slack_client: Mock)
316329
text=renderable["text"],
317330
thread_ts="1111111111.111111",
318331
reply_broadcast=True,
332+
attachments=None,
333+
unfurl_links=False,
334+
unfurl_media=False,
319335
)
320336

321337
@patch("sentry.integrations.slack.integration.SlackSdkClient")
@@ -349,6 +365,9 @@ def test_send_with_thread_context_no_existing_thread(self, mock_slack_client: Mo
349365
channel="C1234567890",
350366
blocks=renderable["blocks"],
351367
text=renderable["text"],
368+
attachments=None,
369+
unfurl_links=False,
370+
unfurl_media=False,
352371
)
353372

354373
@patch("sentry.integrations.slack.integration.SlackSdkClient")

0 commit comments

Comments
 (0)