From e1e56fc4145eb72033c1ec40f978d9085ea51b0a Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Thu, 16 Nov 2023 10:15:04 -0300 Subject: [PATCH] Truncate resolution note text in slack message to satisfy block limits (#3351) This should help with some retrying tasks. --- .../renderers/slack_renderer.py | 2 +- engine/apps/slack/constants.py | 1 + .../apps/slack/scenarios/resolution_note.py | 6 ++- .../test_resolution_note.py | 42 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py b/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py index 2cb7ced3..f4f36b61 100644 --- a/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py +++ b/engine/apps/alerts/incident_appearance/renderers/slack_renderer.py @@ -5,6 +5,7 @@ from django.utils.text import Truncator from apps.alerts.incident_appearance.renderers.base_renderer import AlertBaseRenderer, AlertGroupBaseRenderer from apps.alerts.incident_appearance.templaters import AlertSlackTemplater +from apps.slack.constants import BLOCK_SECTION_TEXT_MAX_SIZE from apps.slack.scenarios.scenario_step import ScenarioStep from apps.slack.types import Block from common.utils import is_string_with_visible_characters, str_or_backup @@ -23,7 +24,6 @@ class AlertSlackRenderer(AlertBaseRenderer): return AlertSlackTemplater def render_alert_blocks(self) -> Block.AnyBlocks: - BLOCK_SECTION_TEXT_MAX_SIZE = 2800 blocks: Block.AnyBlocks = [] title = Truncator(str_or_backup(self.templated_alert.title, "Alert")) diff --git a/engine/apps/slack/constants.py b/engine/apps/slack/constants.py index 4dd73bb1..f72529fd 100644 --- a/engine/apps/slack/constants.py +++ b/engine/apps/slack/constants.py @@ -12,6 +12,7 @@ SLACK_RATE_LIMIT_TIMEOUT = datetime.timedelta(minutes=5) SLACK_RATE_LIMIT_DELAY = 10 CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME = 60 * 10 +BLOCK_SECTION_TEXT_MAX_SIZE = 2800 PRIVATE_METADATA_MAX_LENGTH = 3000 DIVIDER: Block.Divider = {"type": "divider"} diff --git a/engine/apps/slack/scenarios/resolution_note.py b/engine/apps/slack/scenarios/resolution_note.py index 09eea274..8a0f9c5a 100644 --- a/engine/apps/slack/scenarios/resolution_note.py +++ b/engine/apps/slack/scenarios/resolution_note.py @@ -4,9 +4,10 @@ import logging import typing from django.db.models import Q +from django.utils.text import Truncator from apps.api.permissions import RBACPermission -from apps.slack.constants import DIVIDER +from apps.slack.constants import BLOCK_SECTION_TEXT_MAX_SIZE, DIVIDER from apps.slack.errors import ( SlackAPIChannelArchivedError, SlackAPIChannelInactiveError, @@ -295,9 +296,10 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep): def get_resolution_note_blocks(self, resolution_note: "ResolutionNote") -> Block.AnyBlocks: blocks: Block.AnyBlocks = [] author_verbal = resolution_note.author_verbal(mention=False) + resolution_note_text = Truncator(resolution_note.text) resolution_note_text_block = { "type": "section", - "text": {"type": "mrkdwn", "text": resolution_note.text}, + "text": {"type": "mrkdwn", "text": resolution_note_text.chars(BLOCK_SECTION_TEXT_MAX_SIZE)}, } blocks.append(resolution_note_text_block) context_block = { diff --git a/engine/apps/slack/tests/test_scenario_steps/test_resolution_note.py b/engine/apps/slack/tests/test_scenario_steps/test_resolution_note.py index 7e2b500c..a64d0208 100644 --- a/engine/apps/slack/tests/test_scenario_steps/test_resolution_note.py +++ b/engine/apps/slack/tests/test_scenario_steps/test_resolution_note.py @@ -4,6 +4,7 @@ from unittest.mock import patch import pytest from apps.slack.client import SlackClient +from apps.slack.constants import BLOCK_SECTION_TEXT_MAX_SIZE from apps.slack.errors import SlackAPIViewNotFoundError from apps.slack.scenarios.scenario_step import ScenarioStep from apps.slack.tests.conftest import build_slack_response @@ -106,6 +107,47 @@ def test_get_resolution_notes_blocks_non_empty( assert blocks == expected_blocks +@pytest.mark.django_db +def test_get_resolution_note_blocks_truncate_text( + make_organization_and_user_with_slack_identities, + make_alert_receive_channel, + make_alert_group, + make_resolution_note, +): + UpdateResolutionNoteStep = ScenarioStep.get_step("resolution_note", "UpdateResolutionNoteStep") + organization, user, slack_team_identity, _ = make_organization_and_user_with_slack_identities() + step = UpdateResolutionNoteStep(slack_team_identity) + + alert_receive_channel = make_alert_receive_channel(organization) + alert_group = make_alert_group(alert_receive_channel) + resolution_note = make_resolution_note(alert_group=alert_group, author=user, message_text="a" * 3000) + author_verbal = resolution_note.author_verbal(mention=False) + + blocks = step.get_resolution_note_blocks(resolution_note) + + expected_blocks = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + # text is truncated, ellipsis added + "text": resolution_note.text[: BLOCK_SECTION_TEXT_MAX_SIZE - 1] + "…", + }, + }, + { + "type": "context", + "elements": [ + { + "type": "mrkdwn", + "text": f"{author_verbal} resolution note from {resolution_note.get_source_display()}.", + } + ], + }, + ] + + assert blocks == expected_blocks + + @pytest.mark.django_db def test_get_resolution_notes_blocks_latest_limit( make_organization_and_user_with_slack_identities,