Skip to content

Commit b07d487

Browse files
authored
chore(slack): remove non block kit part 2 (#68831)
1 parent 2acf9cd commit b07d487

18 files changed

+250
-841
lines changed

src/sentry/integrations/message_builder.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -192,10 +192,7 @@ def build_footer(
192192
text = rules[0].label if rules[0].label else "Test Alert"
193193
footer += f" via {url_format.format(text=text, url=rule_url)}"
194194

195-
if (
196-
features.has("organizations:slack-block-kit", project.organization)
197-
and url_format == SLACK_URL_FORMAT
198-
):
195+
if url_format == SLACK_URL_FORMAT:
199196
footer = url_format.format(text=text, url=rule_url)
200197

201198
if len(rules) > 1:

src/sentry/integrations/slack/actions/notification.py

Lines changed: 23 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,10 @@ class SlackNotifyServiceAction(IntegrationEventAction):
4141
prompt = "Send a Slack notification"
4242
provider = "slack"
4343
integration_key = "workspace"
44+
label = "Send a notification to the {workspace} Slack workspace to {channel} (optionally, an ID: {channel_id}) and show tags {tags} and notes {notes} in notification"
4445

4546
def __init__(self, *args: Any, **kwargs: Any) -> None:
4647
super().__init__(*args, **kwargs)
47-
# XXX(CEO): when removing the feature flag, put `label` back up as a class var
48-
self.label = "Send a notification to the {workspace} Slack workspace to {channel} (optionally, an ID: {channel_id}) and show tags {tags} in notification" # type: ignore[misc]
49-
if features.has("organizations:slack-block-kit", self.project.organization):
50-
self.label = "Send a notification to the {workspace} Slack workspace to {channel} (optionally, an ID: {channel_id}) and show tags {tags} and notes {notes} in notification" # type: ignore[misc]
5148
self.form_fields = {
5249
"workspace": {
5350
"type": "choice",
@@ -57,11 +54,10 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
5754
"channel_id": {"type": "string", "placeholder": "e.g., CA2FRA079 or UA1J9RTE1"},
5855
"tags": {"type": "string", "placeholder": "e.g., environment,user,my_tag"},
5956
}
60-
if features.has("organizations:slack-block-kit", self.project.organization):
61-
self.form_fields["notes"] = {
62-
"type": "string",
63-
"placeholder": "e.g. @jane, @on-call-team",
64-
}
57+
self.form_fields["notes"] = {
58+
"type": "string",
59+
"placeholder": "e.g. @jane, @on-call-team",
60+
}
6561

6662
self._repository: IssueAlertNotificationMessageRepository = (
6763
get_default_issue_alert_repository()
@@ -86,45 +82,25 @@ def send_notification(event: GroupEvent, futures: Sequence[RuleFuture]) -> None:
8682
additional_attachment = get_additional_attachment(
8783
integration, self.project.organization
8884
)
89-
payload = {}
90-
if features.has("organizations:slack-block-kit", event.group.project.organization):
91-
blocks = SlackIssuesMessageBuilder(
92-
group=event.group,
93-
event=event,
94-
tags=tags,
95-
rules=rules,
96-
notes=self.get_option("notes", ""),
97-
).build(notification_uuid=notification_uuid)
98-
99-
if additional_attachment:
100-
for block in additional_attachment:
101-
blocks["blocks"].append(block)
85+
blocks = SlackIssuesMessageBuilder(
86+
group=event.group,
87+
event=event,
88+
tags=tags,
89+
rules=rules,
90+
notes=self.get_option("notes", ""),
91+
).build(notification_uuid=notification_uuid)
10292

103-
if blocks.get("blocks"):
104-
payload = {
105-
"text": blocks.get("text"),
106-
"blocks": json.dumps(blocks.get("blocks")),
107-
"channel": channel,
108-
"unfurl_links": False,
109-
"unfurl_media": False,
110-
}
111-
else:
112-
attachments = [
113-
SlackIssuesMessageBuilder(
114-
group=event.group,
115-
event=event,
116-
tags=tags,
117-
rules=rules,
118-
).build(notification_uuid=notification_uuid)
119-
]
120-
# getsentry might add a billing related attachment
121-
if additional_attachment:
122-
attachments.append(additional_attachment)
93+
if additional_attachment:
94+
for block in additional_attachment:
95+
blocks["blocks"].append(block)
12396

97+
if payload_blocks := blocks.get("blocks"):
12498
payload = {
99+
"text": blocks.get("text"),
100+
"blocks": json.dumps(payload_blocks),
125101
"channel": channel,
126-
"link_names": 1,
127-
"attachments": json.dumps(attachments),
102+
"unfurl_links": False,
103+
"unfurl_media": False,
128104
}
129105
self.logger.info(
130106
"rule.slack_post.attachments",
@@ -203,9 +179,8 @@ def send_notification(event: GroupEvent, futures: Sequence[RuleFuture]) -> None:
203179
"event_id": event.event_id,
204180
"channel_name": self.get_option("channel"),
205181
}
206-
if features.has("organizations:slack-block-kit", event.group.project.organization):
207-
# temporarily log the payload so we can debug message failures
208-
log_params["payload"] = json.dumps(payload)
182+
# temporarily log the payload so we can debug message failures
183+
log_params["payload"] = json.dumps(payload)
209184

210185
self.logger.info(
211186
"rule.fail.slack_post",
@@ -297,20 +272,12 @@ def send_confirmation_notification(
297272
def render_label(self) -> str:
298273
tags = self.get_tags_list()
299274

300-
if features.has("organizations:slack-block-kit", self.project.organization):
301-
return self.label.format(
302-
workspace=self.get_integration_name(),
303-
channel=self.get_option("channel"),
304-
channel_id=self.get_option("channel_id"),
305-
tags="[{}]".format(", ".join(tags)),
306-
notes=self.get_option("notes", ""),
307-
)
308-
309275
return self.label.format(
310276
workspace=self.get_integration_name(),
311277
channel=self.get_option("channel"),
312278
channel_id=self.get_option("channel_id"),
313279
tags="[{}]".format(", ".join(tags)),
280+
notes=self.get_option("notes", ""),
314281
)
315282

316283
def get_tags_list(self) -> Sequence[str]:

src/sentry/integrations/slack/message_builder/notifications/base.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,7 @@ def build(self) -> SlackAttachment | SlackBlock:
6565

6666
actions_block = []
6767
for action in actions:
68-
if (
69-
action.label in ["Turn on personal notifications", "Approve", "Reject"]
70-
or action.url
71-
):
72-
actions_block.append(self.get_button_action(action))
68+
actions_block.append(self.get_button_action(action))
7369

7470
if actions_block:
7571
blocks.append({"type": "actions", "elements": [action for action in actions_block]})

src/sentry/integrations/slack/notifications.py

Lines changed: 37 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77

88
import sentry_sdk
99

10-
from sentry import features
1110
from sentry.integrations.mixins import NotifyBasicMixin
1211
from sentry.integrations.notifications import get_context, get_integrations_by_channel_by_recipient
13-
from sentry.integrations.slack.message_builder import SlackAttachment, SlackBlock
12+
from sentry.integrations.slack.message_builder import SlackBlock
1413
from sentry.integrations.slack.message_builder.base.block import BlockSlackMessageBuilder
1514
from sentry.integrations.slack.message_builder.notifications import get_message_builder
1615
from sentry.models.integrations.integration import Integration
@@ -47,24 +46,20 @@ def _get_attachments(
4746
recipient: RpcActor,
4847
shared_context: Mapping[str, Any],
4948
extra_context_by_actor: Mapping[RpcActor, Mapping[str, Any]] | None,
50-
) -> list[SlackAttachment] | SlackBlock:
49+
) -> SlackBlock:
5150
extra_context = (
5251
extra_context_by_actor[recipient] if extra_context_by_actor and recipient else {}
5352
)
5453
context = get_context(notification, recipient, shared_context, extra_context)
5554
cls = get_message_builder(notification.message_builder)
5655
attachments = cls(notification, context, recipient).build()
57-
if isinstance(attachments, list) or features.has(
58-
"organizations:slack-block-kit", notification.organization
59-
):
60-
return attachments
61-
return [attachments]
56+
return attachments
6257

6358

6459
def _notify_recipient(
6560
notification: BaseNotification,
6661
recipient: RpcActor,
67-
attachments: list[SlackAttachment] | SlackBlock,
62+
attachments: SlackBlock,
6863
channel: str,
6964
integration: Integration,
7065
shared_context: Mapping[str, Any],
@@ -75,69 +70,39 @@ def _notify_recipient(
7570

7671
text = notification.get_notification_title(ExternalProviders.SLACK, shared_context)
7772

78-
# NOTE(isabella): we check that attachments consists of blocks in case the flag is turned on
79-
# while a notification with the legacy format is being sent
80-
if features.has("organizations:slack-block-kit", notification.organization) and isinstance(
81-
local_attachments, dict
82-
):
83-
blocks = []
84-
if text:
85-
# NOTE(isabella): with legacy attachments, the notification title was
86-
# automatically rendered based on the `text` field in the payload; in the block
87-
# system, that payload field is used strictly as preview/fallback text, so we need
88-
# to add this block to render the title in the notification ie. "Issue marked as
89-
# regression", "New comment by <user>"
90-
blocks.append(BlockSlackMessageBuilder.get_markdown_block(text))
91-
attachment_blocks = local_attachments.get("blocks")
92-
if attachment_blocks:
93-
for attachment in attachment_blocks:
94-
blocks.append(attachment)
95-
if len(blocks) >= 2 and blocks[1].get("block_id"):
96-
# block id needs to be in the first block
97-
blocks[0]["block_id"] = blocks[1]["block_id"]
98-
del blocks[1]["block_id"]
99-
additional_attachment = get_additional_attachment(
100-
integration, notification.organization
101-
)
102-
if additional_attachment:
103-
for block in additional_attachment:
104-
blocks.append(block)
105-
if (
106-
not text
107-
): # if there isn't a notification title, try using message description as fallback
108-
text = notification.get_message_description(recipient, ExternalProviders.SLACK)
109-
payload = {
110-
"channel": channel,
111-
"unfurl_links": False,
112-
"unfurl_media": False,
113-
"text": text if text else "",
114-
"blocks": json.dumps(blocks),
115-
}
116-
callback_id = local_attachments.get("callback_id")
117-
if callback_id:
118-
# callback_id is now at the same level as blocks, rather than within attachments
119-
if isinstance(callback_id, str):
120-
payload["callback_id"] = callback_id
121-
else:
122-
payload["callback_id"] = json.dumps(local_attachments.get("callback_id"))
123-
else:
124-
# Add optional billing related attachment.
125-
additional_attachment = get_additional_attachment(
126-
integration, notification.organization
127-
)
128-
if additional_attachment:
129-
local_attachments.append(additional_attachment)
130-
131-
# unfurl_links and unfurl_media are needed to preserve the intended message format
132-
# and prevent the app from replying with help text to the unfurl
133-
payload = {
134-
"channel": channel,
135-
"link_names": 1,
136-
"unfurl_links": False,
137-
"unfurl_media": False,
138-
"text": text,
139-
"attachments": json.dumps(local_attachments),
140-
}
73+
blocks = []
74+
if text:
75+
blocks.append(BlockSlackMessageBuilder.get_markdown_block(text))
76+
attachment_blocks = local_attachments.get("blocks")
77+
if attachment_blocks:
78+
for attachment in attachment_blocks:
79+
blocks.append(attachment)
80+
if len(blocks) >= 2 and blocks[1].get("block_id"):
81+
# block id needs to be in the first block
82+
blocks[0]["block_id"] = blocks[1]["block_id"]
83+
del blocks[1]["block_id"]
84+
additional_attachment = get_additional_attachment(integration, notification.organization)
85+
if additional_attachment:
86+
for block in additional_attachment:
87+
blocks.append(block)
88+
if (
89+
not text
90+
): # if there isn't a notification title, try using message description as fallback
91+
text = notification.get_message_description(recipient, ExternalProviders.SLACK)
92+
payload = {
93+
"channel": channel,
94+
"unfurl_links": False,
95+
"unfurl_media": False,
96+
"text": text if text else "",
97+
"blocks": json.dumps(blocks),
98+
}
99+
callback_id = local_attachments.get("callback_id")
100+
if callback_id:
101+
# callback_id is now at the same level as blocks, rather than within attachments
102+
if isinstance(callback_id, str):
103+
payload["callback_id"] = callback_id
104+
else:
105+
payload["callback_id"] = json.dumps(local_attachments.get("callback_id"))
141106

142107
post_message_task = post_message
143108
if SiloMode.get_current_mode() == SiloMode.CONTROL:

tests/sentry/api/endpoints/test_organization_invite_request_index.py

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
from sentry.models.organizationmember import InviteStatus, OrganizationMember
1010
from sentry.models.organizationmemberteam import OrganizationMemberTeam
1111
from sentry.testutils.cases import APITestCase, SlackActivityNotificationTest
12-
from sentry.testutils.helpers.features import with_feature
13-
from sentry.testutils.helpers.slack import get_attachment_no_text
12+
from sentry.testutils.helpers.slack import get_blocks_and_fallback_text
1413
from sentry.testutils.hybrid_cloud import HybridCloudTestMixin
1514
from sentry.testutils.outbox import outbox_runner
1615
from sentry.utils import json
@@ -188,9 +187,7 @@ def test_request_to_invite_email(self):
188187
assert "eric@localhost" in mail.outbox[0].body
189188

190189
@responses.activate
191-
@with_feature({"organizations:slack-block-kit": False})
192190
def test_request_to_invite_slack(self):
193-
# TODO: make this a block kit test
194191
with self.tasks():
195192
self.get_success_response(
196193
self.organization.slug,
@@ -200,44 +197,44 @@ def test_request_to_invite_slack(self):
200197
status_code=201,
201198
)
202199

203-
attachment = get_attachment_no_text()
200+
blocks, fallback_text = get_blocks_and_fallback_text()
204201
assert (
205-
attachment["text"]
202+
fallback_text
206203
== f"foo@localhost is requesting to invite eric@localhost into {self.organization.name}"
207204
)
208-
notification_uuid = parse_qs(urlparse(attachment["actions"][2]["url"]).query)[
209-
"notification_uuid"
210-
][0]
211-
assert attachment["actions"] == [
205+
query_params = parse_qs(urlparse(blocks[1]["elements"][0]["text"]).query)
206+
notification_uuid = query_params["notification_uuid"][0]
207+
notification_uuid = notification_uuid.split("|")[
208+
0
209+
] # remove method of hyperlinking in slack
210+
assert blocks[2]["elements"] == [
212211
{
213-
"text": "Approve",
214-
"name": "Approve",
215-
"style": "primary",
216212
"type": "button",
217-
"value": "approve_member",
213+
"text": {"type": "plain_text", "text": "Approve"},
218214
"action_id": "approve_request",
215+
"value": "approve_member",
219216
},
220217
{
221-
"text": "Reject",
222-
"name": "Reject",
223-
"style": "danger",
224218
"type": "button",
225-
"value": "reject_member",
219+
"text": {"type": "plain_text", "text": "Reject"},
226220
"action_id": "approve_request",
221+
"value": "reject_member",
227222
},
228223
{
229-
"text": "See Members & Requests",
230-
"name": "See Members & Requests",
231-
"url": f"http://testserver/settings/{self.organization.slug}/members/?referrer=invite_request-slack-user&notification_uuid={notification_uuid}",
232224
"type": "button",
225+
"text": {"type": "plain_text", "text": "See Members & Requests"},
226+
"url": f"http://testserver/settings/{self.organization.slug}/members/?referrer=invite_request-slack-user&notification_uuid={notification_uuid}",
227+
"value": "link_clicked",
233228
},
234229
]
230+
footer = blocks[1]["elements"][0]["text"]
235231
assert (
236-
attachment["footer"]
232+
footer
237233
== f"You are receiving this notification because you have the scope member:write | <http://testserver/settings/account/notifications/approval/?referrer=invite_request-slack-user&notification_uuid={notification_uuid}|Notification Settings>"
238234
)
239235
member = OrganizationMember.objects.get(email="eric@localhost")
240-
assert json.loads(attachment["callback_id"]) == {
236+
data = parse_qs(responses.calls[0].request.body)
237+
assert json.loads(data["callback_id"][0]) == {
241238
"member_id": member.id,
242239
"member_email": "eric@localhost",
243240
}

tests/sentry/api/endpoints/test_project_rule_details.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,9 +1002,7 @@ def test_update_channel_slack(self, mock_send_confirmation_notification):
10021002

10031003
@responses.activate
10041004
@with_feature("organizations:rule-create-edit-confirm-notification")
1005-
@with_feature({"organizations:slack-block-kit": False})
10061005
def test_slack_confirmation_notification_contents(self):
1007-
# TODO: make this a block kit test
10081006
conditions = [
10091007
{"id": "sentry.rules.conditions.first_seen_event.FirstSeenEventCondition"},
10101008
]
@@ -1093,7 +1091,7 @@ def test_slack_confirmation_notification_contents(self):
10931091
assert rendered_blocks[0]["text"]["text"] == message
10941092
changes = "*Changes*\n"
10951093
changes += "• Added condition 'The issue's category is equal to Performance'\n"
1096-
changes += "• Changed action from *Send a notification to the Awesome Team Slack workspace to #old_channel_name (optionally, an ID: old_channel_id) and show tags [] in notification* to *Send a notification to the Awesome Team Slack workspace to new_channel_name (optionally, an ID: new_channel_id) and show tags [] in notification*\n"
1094+
changes += "• Changed action from *Send a notification to the Awesome Team Slack workspace to #old_channel_name (optionally, an ID: old_channel_id) and show tags [] and notes in notification* to *Send a notification to the Awesome Team Slack workspace to new_channel_name (optionally, an ID: new_channel_id) and show tags [] and notes in notification*\n"
10971095
changes += "• Changed frequency from *5 minutes* to *3 hours*\n"
10981096
changes += f"• Added *{staging_env.name}* environment\n"
10991097
changes += "• Changed rule name from *my rule* to *new rule*\n"

0 commit comments

Comments
 (0)