Skip to content

Commit 9322245

Browse files
committed
Convert slack notification title to rich text
1 parent b154c98 commit 9322245

File tree

4 files changed

+64
-36
lines changed

4 files changed

+64
-36
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ def get_markdown_block(text: str, emoji: str | None = None) -> SlackBlock:
3939
"text": {"type": "mrkdwn", "text": text},
4040
}
4141

42+
@staticmethod
43+
def get_rich_text_link(emojis: list[str], text: str, link: str) -> SlackBlock:
44+
elements = []
45+
for emoji in emojis:
46+
elements.append({"type": "emoji", "name": emoji})
47+
elements.append({"type": "text", "text": " "})
48+
49+
elements.append({"type": "link", "url": link, "text": text, "style": {"bold": True}})
50+
51+
return {
52+
"type": "rich_text",
53+
"elements": [{"type": "rich_text_section", "elements": elements}],
54+
}
55+
4256
@staticmethod
4357
def get_markdown_quote_block(text: str, max_block_text_length: int) -> SlackBlock:
4458
if len(text) > max_block_text_length:

src/sentry/integrations/slack/message_builder/issues.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -458,30 +458,28 @@ def get_title_block(
458458
) -> SlackBlock:
459459
summary_headline = self.get_issue_summary_headline(event_or_group)
460460
title = summary_headline or build_attachment_title(event_or_group)
461-
title_emoji = self.get_title_emoji(has_action)
461+
title_emojis = self.get_title_emoji(has_action)
462462

463-
title_text = f"{title_emoji}<{title_link}|*{escape_slack_text(title)}*>"
464-
return self.get_markdown_block(title_text)
463+
return self.get_rich_text_link(title_emojis, title, title_link)
465464

466-
def get_title_emoji(self, has_action: bool) -> str | None:
465+
def get_title_emoji(self, has_action: bool) -> list[str]:
467466
is_error_issue = self.group.issue_category == GroupCategory.ERROR
468467

469-
title_emoji = None
468+
title_emojis = []
470469
if has_action:
471470
# if issue is resolved, archived, or assigned, replace circle emojis with white circle
472-
title_emoji = (
471+
title_emojis = (
473472
ACTION_EMOJI
474473
if is_error_issue
475474
else ACTIONED_CATEGORY_TO_EMOJI.get(self.group.issue_category)
476475
)
477476
elif is_error_issue:
478477
level_text = LOG_LEVELS[self.group.level]
479-
title_emoji = LEVEL_TO_EMOJI.get(level_text)
478+
title_emojis = LEVEL_TO_EMOJI.get(level_text)
480479
else:
481-
title_emoji = CATEGORY_TO_EMOJI.get(self.group.issue_category)
480+
title_emojis = CATEGORY_TO_EMOJI.get(self.group.issue_category)
482481

483-
title_emoji = title_emoji + " " if title_emoji else ""
484-
return title_emoji
482+
return title_emojis
485483

486484
def get_issue_summary_headline(self, event_or_group: Event | GroupEvent | Group) -> str | None:
487485
if self.issue_summary is None:

src/sentry/integrations/slack/message_builder/types.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,24 +16,24 @@
1616
SLACK_URL_FORMAT = "<{url}|{text}>"
1717

1818
LEVEL_TO_EMOJI = {
19-
"_incident_resolved": ":green_circle:",
20-
"debug": ":bug:",
21-
"error": ":red_circle:",
22-
"fatal": ":red_circle:",
23-
"info": ":large_blue_circle:",
24-
"warning": ":large_yellow_circle:",
19+
"_incident_resolved": ["green_circle"],
20+
"debug": ["bug"],
21+
"error": ["red_circle"],
22+
"fatal": ["red_circle"],
23+
"info": ["large_blue_circle"],
24+
"warning": ["large_yellow_circle"],
2525
}
2626

27-
ACTION_EMOJI = ":white_circle:"
27+
ACTION_EMOJI = ["white_circle"]
2828

2929
CATEGORY_TO_EMOJI = {
30-
GroupCategory.PERFORMANCE: ":large_blue_circle: :chart_with_upwards_trend:",
31-
GroupCategory.FEEDBACK: ":large_blue_circle: :busts_in_silhouette:",
32-
GroupCategory.CRON: ":large_yellow_circle: :spiral_calendar_pad:",
30+
GroupCategory.PERFORMANCE: ["large_blue_circle", "chart_with_upwards_trend"],
31+
GroupCategory.FEEDBACK: ["large_blue_circle", "busts_in_silhouette"],
32+
GroupCategory.CRON: ["large_yellow_circle", "spiral_calendar_pad"],
3333
}
3434

3535
ACTIONED_CATEGORY_TO_EMOJI = {
36-
GroupCategory.PERFORMANCE: ACTION_EMOJI + " :chart_with_upwards_trend:",
37-
GroupCategory.FEEDBACK: ACTION_EMOJI + " :busts_in_silhouette:",
38-
GroupCategory.CRON: ACTION_EMOJI + " :spiral_calendar_pad:",
36+
GroupCategory.PERFORMANCE: [ACTION_EMOJI, "chart_with_upwards_trend"],
37+
GroupCategory.FEEDBACK: [ACTION_EMOJI, "busts_in_silhouette"],
38+
GroupCategory.CRON: [ACTION_EMOJI, "spiral_calendar_pad"],
3939
}

tests/sentry/integrations/slack/test_message_builder.py

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ def build_test_message_blocks(
103103
else:
104104
title_link += f"&alert_rule_id={rule.id}&alert_type=issue"
105105

106-
title_text = f":red_circle: <{title_link}|*{formatted_title}*>"
107-
108106
if rule:
109107
if legacy_rule_id:
110108
block_id = f'{{"issue":{group.id},"rule":{legacy_rule_id}}}'
@@ -115,8 +113,22 @@ def build_test_message_blocks(
115113

116114
blocks: list[dict[str, Any]] = [
117115
{
118-
"type": "section",
119-
"text": {"type": "mrkdwn", "text": title_text},
116+
"type": "rich_text",
117+
"elements": [
118+
{
119+
"type": "rich_text_section",
120+
"elements": [
121+
{"type": "emoji", "name": "red_circle"},
122+
{"type": "text", "text": " "},
123+
{
124+
"type": "link",
125+
"url": title_link,
126+
"text": formatted_title,
127+
"style": {"bold": True},
128+
},
129+
],
130+
}
131+
],
120132
"block_id": block_id,
121133
},
122134
]
@@ -848,7 +860,8 @@ def test_build_performance_issue(self):
848860
with self.feature("organizations:performance-issues"):
849861
blocks = SlackIssuesMessageBuilder(event.group, event).build()
850862
assert isinstance(blocks, dict)
851-
assert "N+1 Query" in blocks["blocks"][0]["text"]["text"]
863+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
864+
assert "N+1 Query" in title_text
852865
assert (
853866
"db - SELECT `books_author`.`id`, `books_author`.`name` FROM `books_author` WHERE `books_author`.`id` = %s LIMIT 21"
854867
in blocks["blocks"][2]["text"]["text"]
@@ -996,8 +1009,9 @@ def test_build_group_block_with_ai_summary_with_feature_flag(
9961009
mock_get_summary.assert_called_once_with(group, source="alert")
9971010

9981011
# Verify that the original title is \\ present
999-
assert "IntegrationError" in blocks["blocks"][0]["text"]["text"]
1000-
assert "Identity not found" in blocks["blocks"][0]["text"]["text"]
1012+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
1013+
assert "IntegrationError" in title_text
1014+
assert "Identity not found" in title_text
10011015

10021016
# Verify that the AI content is used in the context block
10031017
content_block = blocks["blocks"][1]["elements"][0]["text"]
@@ -1042,7 +1056,8 @@ def test_build_group_block_with_ai_summary_without_feature_flag(
10421056
with patch(patch_path) as mock_get_summary:
10431057
mock_get_summary.assert_not_called()
10441058
blocks = SlackIssuesMessageBuilder(group).build()
1045-
assert "IntegrationError" in blocks["blocks"][0]["text"]["text"]
1059+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
1060+
assert "IntegrationError" in title_text
10461061

10471062
@override_options({"alerts.issue_summary_timeout": 5})
10481063
@with_feature({"organizations:gen-ai-features", "projects:trigger-issue-summary-on-alerts"})
@@ -1126,7 +1141,7 @@ def test_build_group_block_with_ai_summary_text_truncation(
11261141
):
11271142
mock_get_summary.return_value = (mock_summary, 200)
11281143
blocks = SlackIssuesMessageBuilder(group1, event1.for_group(group1)).build()
1129-
title_text = blocks["blocks"][0]["text"]["text"]
1144+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
11301145

11311146
assert "First line of text..." in title_text
11321147
assert "Second line" not in title_text
@@ -1138,7 +1153,7 @@ def test_build_group_block_with_ai_summary_text_truncation(
11381153
):
11391154
mock_get_summary.return_value = (mock_summary, 200)
11401155
blocks = SlackIssuesMessageBuilder(group2, event2.for_group(group2)).build()
1141-
title_text = blocks["blocks"][0]["text"]["text"]
1156+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
11421157

11431158
expected_truncated = long_text[:MAX_SUMMARY_HEADLINE_LENGTH] + "..."
11441159
assert expected_truncated in title_text
@@ -1181,8 +1196,8 @@ def test_build_group_block_with_ai_summary_text_truncation(
11811196
):
11821197
mock_get_summary.return_value = (mock_summary, 200)
11831198
blocks = SlackIssuesMessageBuilder(group_lb, event_lb.for_group(group_lb)).build()
1184-
title_block = blocks["blocks"][0]["text"]["text"]
1185-
assert f": {expected_headline_part}*>" in title_block, f"Failed for {name}"
1199+
title_block = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
1200+
assert f": {expected_headline_part}" in title_block, f"Failed for {name}"
11861201

11871202
@override_options({"alerts.issue_summary_timeout": 5})
11881203
@patch(
@@ -1214,7 +1229,8 @@ def test_build_group_block_with_ai_summary_without_org_acknowledgement(
12141229
mock_get_issue_summary.assert_not_called()
12151230

12161231
blocks = SlackIssuesMessageBuilder(group).build()
1217-
assert "IntegrationError" in blocks["blocks"][0]["text"]["text"]
1232+
title_text = blocks["blocks"][0]["elements"][0]["elements"][-1]["text"]
1233+
assert "IntegrationError" in title_text
12181234

12191235

12201236
class BuildGroupAttachmentReplaysTest(TestCase):

0 commit comments

Comments
 (0)