diff --git a/engine/apps/slack/scenarios/resolution_note.py b/engine/apps/slack/scenarios/resolution_note.py index a5bfb279..67ddea66 100644 --- a/engine/apps/slack/scenarios/resolution_note.py +++ b/engine/apps/slack/scenarios/resolution_note.py @@ -3,6 +3,7 @@ import json import logging import typing +from django.conf import settings from django.db.models import Q from django.utils.text import Truncator @@ -76,16 +77,28 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep): warning_text = "Unable to add this message to resolution note, this command works only in incident threads." + # thread_ts is only present for thread messages + thread_ts = payload.get("message", {}).get("thread_ts") + if not thread_ts: + if settings.UNIFIED_SLACK_APP_ENABLED: + # Message shortcut events are broadcasted to multiple regions by chatops-proxy + # Do not open a warning window to avoid multiple regions opening the same window multiple times + return + + self.open_warning_window(payload, warning_text) + return + try: slack_message = SlackMessage.objects.get( slack_id=payload["message"]["thread_ts"], _slack_team_identity=slack_team_identity, channel_id=channel_id, ) - except KeyError: - self.open_warning_window(payload, warning_text) - return except SlackMessage.DoesNotExist: + if settings.UNIFIED_SLACK_APP_ENABLED: + # Message shortcut events are broadcasted to multiple regions by chatops-proxy + # Don't open a warning window as this event could be handled by another region + return self.open_warning_window(payload, warning_text) return @@ -99,9 +112,17 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep): ) return + if alert_group.channel.organization.deleted_at is not None: + if settings.UNIFIED_SLACK_APP_ENABLED: + # Message shortcut events are broadcasted to multiple regions by chatops-proxy + # Don't open a warning window as this event could be handled by another region + return + + self.open_warning_window(payload, warning_text) + return + if payload["message"]["type"] == "message" and "user" in payload["message"]: message_ts = payload["message_ts"] - thread_ts = payload["message"]["thread_ts"] result = self._slack_client.chat_getPermalink(channel=channel_id, message_ts=message_ts) permalink = None 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 5ed39ecf..e3e9cb02 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 @@ -334,3 +334,111 @@ def test_resolution_notes_modal_closed_before_update( # Check that "views.update" API call was made call_args, _ = mock_slack_api_call.call_args assert call_args[0] == "views.update" + + +@patch.object(SlackClient, "chat_getPermalink", return_value={"permalink": "https://example.com"}) +@pytest.mark.django_db +def test_add_to_resolution_note( + _, + make_organization_and_user_with_slack_identities, + make_alert_receive_channel, + make_alert_group, + make_alert, + make_slack_message, + settings, +): + organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities() + alert_receive_channel = make_alert_receive_channel(organization) + alert_group = make_alert_group(alert_receive_channel) + make_alert(alert_group=alert_group, raw_request_data={}) + slack_message = make_slack_message(alert_group=alert_group) + + payload = { + "channel": {"id": slack_message.channel_id}, + "message_ts": "random_ts", + "message": { + "type": "message", + "text": "Test resolution note", + "ts": "random_ts", + "thread_ts": slack_message.slack_id, + "user": slack_user_identity.slack_id, + }, + "trigger_id": "random_trigger_id", + } + + AddToResolutionNoteStep = ScenarioStep.get_step("resolution_note", "AddToResolutionNoteStep") + step = AddToResolutionNoteStep(organization=organization, user=user, slack_team_identity=slack_team_identity) + with patch.object(SlackClient, "reactions_add") as mock_reactions_add: + step.process_scenario(slack_user_identity, slack_team_identity, payload) + + mock_reactions_add.assert_called_once() + assert alert_group.resolution_notes.get().text == "Test resolution note" + + +@pytest.mark.django_db +def test_add_to_resolution_note_broadcast(make_organization_and_user_with_slack_identities, settings): + settings.UNIFIED_SLACK_APP_ENABLED = True + + organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities() + + payload = { + "channel": {"id": "TEST"}, + "message_ts": "TEST", + "message": {"thread_ts": "TEST"}, + "trigger_id": "TEST", + } + + AddToResolutionNoteStep = ScenarioStep.get_step("resolution_note", "AddToResolutionNoteStep") + step = AddToResolutionNoteStep(organization=organization, user=user, slack_team_identity=slack_team_identity) + with patch.object(SlackClient, "api_call") as mock_api_call: + step.process_scenario(slack_user_identity, slack_team_identity, payload) + + mock_api_call.assert_not_called() # no Slack API calls should be made + + +@patch.object(SlackClient, "chat_getPermalink", return_value={"permalink": "https://example.com"}) +@pytest.mark.django_db +def test_add_to_resolution_note_deleted_org( + _, + make_organization_and_user_with_slack_identities, + make_alert_receive_channel, + make_alert_group, + make_alert, + make_slack_message, + make_organization, + make_user_for_organization, + settings, +): + settings.UNIFIED_SLACK_APP_ENABLED = True + + organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities() + alert_receive_channel = make_alert_receive_channel(organization) + alert_group = make_alert_group(alert_receive_channel) + make_alert(alert_group=alert_group, raw_request_data={}) + slack_message = make_slack_message(alert_group=alert_group) + organization.delete() + + other_organization = make_organization(slack_team_identity=slack_team_identity) + other_user = make_user_for_organization(organization=other_organization, slack_user_identity=slack_user_identity) + + payload = { + "channel": {"id": slack_message.channel_id}, + "message_ts": "random_ts", + "message": { + "type": "message", + "text": "Test resolution note", + "ts": "random_ts", + "thread_ts": slack_message.slack_id, + "user": slack_user_identity.slack_id, + }, + "trigger_id": "random_trigger_id", + } + + AddToResolutionNoteStep = ScenarioStep.get_step("resolution_note", "AddToResolutionNoteStep") + step = AddToResolutionNoteStep( + organization=other_organization, user=other_user, slack_team_identity=slack_team_identity + ) + with patch.object(SlackClient, "api_call") as mock_api_call: + step.process_scenario(slack_user_identity, slack_team_identity, payload) + + mock_api_call.assert_not_called() # no Slack API calls should be made