Support message shortcut broadcast (#4518)

# What this PR does

Related to https://github.com/grafana/oncall-gateway/issues/206

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.
This commit is contained in:
Vadim Stepanov 2024-06-17 11:31:43 +01:00 committed by GitHub
parent 8f64a44e54
commit b7dbb2a26e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 133 additions and 4 deletions

View file

@ -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

View file

@ -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