update slack_sdk dependency to latest version (#2947)

# What this PR does

- update `slackclient` dependency to latest version. The version we were
using was 5 years old 😲
- first followed the v2 migration guide
[here](https://github.com/slackapi/python-slack-sdk/wiki/Migrating-to-2.x)
followed by the v3 migration guide
[here](https://slack.dev/python-slack-sdk/v3-migration/). The main
changes were:
    - The PyPI project was renamed from `slackclient` to `slack_sdk`
- it is discouraged/harder to call `api_call` and encouraged to call the
helper methods (ex. `chat_postMessage`;
[note](https://github.com/slackapi/python-slack-sdk/wiki/Migrating-to-2.x#web-client-api-changes)
in migration guide docs)
- In 1.x, a failed api call would return the error payload to you and
have you handle the error. In 2.x, a failed api call will throw an
exception. To handle this in your code, you will have to wrap api calls
with a try except block. Since we overload `WebClient.api_call` this was
an easy change and only required a one line change
- remove `apps.slack.slack_client.slack_server.SlackClientServer` class.
The new version of `slack_sdk` handles the case that we needed to
overload for in the first place.
- merged `apps/slack/slack_client/slack_client.py` and
`apps/slack/slack_client/exceptions.py` into `apps/slack/client.py`

## 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] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
This commit is contained in:
Joey Orlando 2023-09-05 11:31:59 +02:00 committed by GitHub
parent 0dea5661c4
commit a9155130df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
43 changed files with 253 additions and 502 deletions

View file

@ -6,9 +6,8 @@ from typing import TYPE_CHECKING
from django.utils import timezone
from apps.schedules.ical_utils import calculate_shift_diff, parse_event_uid
from apps.slack.client import SlackAPIException, SlackAPITokenException, SlackClientWithErrorHandling
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
from .task_logger import task_logger
@ -152,8 +151,7 @@ def notify_ical_schedule_shift(schedule_pk):
report_blocks = step.get_report_blocks_ical(new_shifts, upcoming_shifts, schedule, schedule.empty_oncall)
try:
slack_client.api_call(
"chat.postMessage",
slack_client.chat_postMessage(
channel=schedule.channel,
blocks=report_blocks,
text=f"On-call shift for schedule {schedule.name} has changed",

View file

@ -100,7 +100,7 @@ def test_next_shift_notification_long_shifts(
with patch("apps.alerts.tasks.notify_ical_schedule_shift.datetime", Mock(wraps=datetime)) as mock_datetime:
mock_datetime.datetime.now.return_value = datetime.datetime(2021, 9, 29, 12, 0, tzinfo=pytz.UTC)
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(ical_schedule.pk)
slack_blocks = mock_slack_api_call.call_args_list[0][1]["blocks"]
@ -203,7 +203,7 @@ def test_overrides_changes_no_current_no_triggering_notification(
schedule.prev_ical_file_overrides = ical_before
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -251,7 +251,7 @@ def test_no_changes_no_triggering_notification(
schedule.empty_oncall = False
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -299,7 +299,7 @@ def test_current_shift_changes_trigger_notification(
schedule.empty_oncall = False
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert mock_slack_api_call.called
@ -363,7 +363,7 @@ def test_current_shift_changes_swap_split(
schedule.empty_oncall = False
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
text_block = mock_slack_api_call.call_args_list[0][1]["blocks"][0]["text"]["text"]
@ -432,7 +432,7 @@ def test_next_shift_changes_no_triggering_notification(
on_call_shift_2.add_rolling_users([[user2]])
schedule.refresh_ical_file()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -499,7 +499,7 @@ def test_lower_priority_changes_no_triggering_notification(
on_call_shift_2.add_rolling_users([[user2]])
schedule.refresh_ical_file()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -629,7 +629,7 @@ def test_vtimezone_changes_no_triggering_notification(
schedule.cached_ical_file_primary = ical_after
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -686,7 +686,7 @@ def test_no_changes_no_triggering_notification_from_old_to_new_task_version(
schedule.empty_oncall = False
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert not mock_slack_api_call.called
@ -748,7 +748,7 @@ def test_current_shift_changes_trigger_notification_from_old_to_new_task_version
on_call_shift.add_rolling_users([[user2]])
schedule.refresh_ical_file()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert mock_slack_api_call.called
@ -813,7 +813,7 @@ def test_next_shift_notification_long_and_short_shifts(
schedule.empty_oncall = False
schedule.save()
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_ical_schedule_shift(schedule.pk)
assert mock_slack_api_call.called

View file

@ -3,8 +3,8 @@ from datetime import timedelta
import pytest
from django.utils import timezone
from apps.slack.client import SlackClientWithErrorHandling
from apps.slack.scenarios.distribute_alerts import AlertShootingStep
from apps.slack.slack_client import SlackClientWithErrorHandling
@pytest.fixture()

View file

@ -8,8 +8,7 @@ from django.core.cache import cache
from apps.alerts.models.alert_group_counter import ConcurrentUpdateError
from apps.alerts.tasks import resolve_alert_group_by_source_if_needed
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.client import SlackAPIException, SlackClientWithErrorHandling
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
from common.custom_celery_tasks.create_alert_base_task import CreateAlertBaseTask
@ -159,8 +158,6 @@ def notify_about_integration_ratelimit_in_slack(organization_id, text, **kwargs)
if slack_team_identity is not None:
try:
sc = SlackClientWithErrorHandling(slack_team_identity.bot_access_token)
sc.api_call(
"chat.postMessage", channel=organization.general_log_channel_id, text=text, team=slack_team_identity
)
sc.chat_postMessage(channel=organization.general_log_channel_id, text=text, team=slack_team_identity)
except SlackAPIException as e:
logger.warning(f"Slack exception {e} while sending message for organization {organization_id}")

View file

@ -1,15 +1,12 @@
from apps.public_api.constants import VALID_DATE_FOR_DELETE_INCIDENT
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPITokenException
from apps.slack.client import SlackAPITokenException, SlackClientWithErrorHandling
def team_has_slack_token_for_deleting(alert_group):
if alert_group.slack_message and alert_group.slack_message.slack_team_identity:
sc = SlackClientWithErrorHandling(alert_group.slack_message.slack_team_identity.bot_access_token)
try:
sc.api_call(
"auth.test",
)
sc.auth_test()
except SlackAPITokenException:
return False
return True

View file

@ -154,14 +154,13 @@ def test_followup_offsets():
assert ShiftSwapRequest.FOLLOWUP_OFFSETS[idx] > FOLLOWUP_WINDOW
@patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call")
@patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage")
@pytest.mark.django_db
def test_send_shift_swap_request_followup(mock_slack_api_call, shift_swap_request_test_setup):
def test_send_shift_swap_request_followup(mock_slack_chat_post_message, shift_swap_request_test_setup):
shift_swap_request = shift_swap_request_test_setup()
send_shift_swap_request_slack_followup(shift_swap_request.pk)
mock_slack_api_call.assert_called_once_with(
"chat.postMessage",
mock_slack_chat_post_message.assert_called_once_with(
channel=shift_swap_request.slack_message.channel_id,
thread_ts=shift_swap_request.slack_message.slack_id,
reply_broadcast=True,

View file

@ -49,7 +49,7 @@ def test_no_empty_shifts_no_triggering_notification(
empty_shifts_report_sent_at = schedule.empty_shifts_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_empty_shifts_in_schedule(schedule.pk)
assert not mock_slack_api_call.called
@ -97,7 +97,7 @@ def test_empty_shifts_trigger_notification(
empty_shifts_report_sent_at = schedule.empty_shifts_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_empty_shifts_in_schedule(schedule.pk)
assert mock_slack_api_call.called
@ -160,7 +160,7 @@ def test_empty_non_empty_shifts_trigger_notification(
empty_shifts_report_sent_at = schedule.empty_shifts_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_empty_shifts_in_schedule(schedule.pk)
assert mock_slack_api_call.called

View file

@ -48,7 +48,7 @@ def test_no_gaps_no_triggering_notification(
gaps_report_sent_at = schedule.gaps_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_gaps_in_schedule(schedule.pk)
assert not mock_slack_api_call.called
@ -113,7 +113,7 @@ def test_gaps_in_the_past_no_triggering_notification(
gaps_report_sent_at = schedule.gaps_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_gaps_in_schedule(schedule.pk)
assert not mock_slack_api_call.called
@ -165,7 +165,7 @@ def test_gaps_now_trigger_notification(
assert schedule.has_gaps is False
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_gaps_in_schedule(schedule.pk)
assert mock_slack_api_call.called
@ -219,7 +219,7 @@ def test_gaps_near_future_trigger_notification(
assert schedule.has_gaps is False
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_gaps_in_schedule(schedule.pk)
assert mock_slack_api_call.called
@ -271,7 +271,7 @@ def test_gaps_later_than_7_days_no_triggering_notification(
gaps_report_sent_at = schedule.gaps_report_sent_at
with patch("apps.slack.slack_client.SlackClientWithErrorHandling.api_call") as mock_slack_api_call:
with patch("apps.slack.client.SlackClientWithErrorHandling.chat_postMessage") as mock_slack_api_call:
notify_about_gaps_in_schedule(schedule.pk)
assert not mock_slack_api_call.called

View file

@ -1,14 +1,14 @@
import logging
import typing
from apps.slack.constants import SLACK_RATE_LIMIT_DELAY
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import (
from apps.slack.client import (
SlackAPIChannelArchivedException,
SlackAPIException,
SlackAPIRateLimitException,
SlackAPITokenException,
SlackClientWithErrorHandling,
)
from apps.slack.constants import SLACK_RATE_LIMIT_DELAY
if typing.TYPE_CHECKING:
from apps.alerts.models import AlertGroup
@ -36,8 +36,7 @@ class AlertGroupSlackService:
logger.info(f"Update message for alert_group {alert_group.pk}")
try:
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=alert_group.slack_message.channel_id,
ts=alert_group.slack_message.slack_id,
attachments=alert_group.render_slack_attachments(),
@ -77,8 +76,7 @@ class AlertGroupSlackService:
return
try:
result = self._slack_client.api_call(
"chat.postMessage",
result = self._slack_client.chat_postMessage(
channel=alert_group.slack_message.channel_id,
text=text,
attachments=attachments,

View file

@ -2,44 +2,42 @@ import logging
from typing import Optional, Tuple
from django.utils import timezone
from slackclient import SlackClient
from slackclient.exceptions import TokenRefreshError
from slack_sdk.errors import SlackApiError
from slack_sdk.web import WebClient
from apps.slack.constants import SLACK_RATE_LIMIT_DELAY
from .exceptions import (
SlackAPIChannelArchivedException,
SlackAPIException,
SlackAPIRateLimitException,
SlackAPITokenException,
)
from .slack_client_server import SlackClientServer
logger = logging.getLogger(__name__)
class SlackClientWithErrorHandling(SlackClient):
def __init__(self, token=None, **kwargs):
class SlackAPIException(Exception):
def __init__(self, *args, **kwargs):
self.response = {}
if "response" in kwargs:
self.response = kwargs["response"]
super().__init__(*args)
class SlackAPITokenException(SlackAPIException):
pass
class SlackAPIChannelArchivedException(SlackAPIException):
pass
class SlackAPIRateLimitException(SlackAPIException):
pass
class SlackClientWithErrorHandling(WebClient):
def paginated_api_call(self, method: str, paginated_key: str, **kwargs):
"""
This method is rewritten because we want to use custom server SlackClientServer for SlackClient
`paginated_key` represents a key from the response which is paginated. For example "users" or "channels"
"""
super().__init__(token=token, **kwargs)
api_method = getattr(self, method)
proxies = kwargs.get("proxies")
if self.refresh_token:
if callable(self.token_update_callback):
token = None
else:
raise TokenRefreshError("Token refresh callback function is required when using refresh token.")
# Slack app configs
self.server = SlackClientServer(token=token, connect=False, proxies=proxies)
def paginated_api_call(self, *args, **kwargs):
# It's a key from response which is paginated. For example "users" or "channels"
listed_key = kwargs["paginated_key"]
response = self.api_call(*args, **kwargs)
response = api_method(**kwargs)
cumulative_response = response
while (
@ -48,25 +46,30 @@ class SlackClientWithErrorHandling(SlackClient):
and response["response_metadata"]["next_cursor"] != ""
):
kwargs["cursor"] = response["response_metadata"]["next_cursor"]
response = self.api_call(*args, **kwargs)
cumulative_response[listed_key] += response[listed_key]
response = api_method(**kwargs)
cumulative_response[paginated_key] += response[paginated_key]
return cumulative_response
def paginated_api_call_with_ratelimit(self, *args, **kwargs) -> Tuple[dict, Optional[str], bool]:
def paginated_api_call_with_ratelimit(
self, method: str, paginated_key: str, **kwargs
) -> Tuple[dict, Optional[str], bool]:
"""
This method do paginated api call and handle slack rate limit error in order to return collected data and have
ability to continue doing paginated requests from the last successful cursor. Return last successful cursor
instead of next cursor to avoid data loss during delay time
This method does paginated api calls and handle slack rate limit errors in order to return collected data
and have the ability to continue doing paginated requests from the last successful cursor.
Return last successful cursor instead of next cursor to avoid data loss during delay time.
`paginated_key` represents a key from the response which is paginated. For example "users" or "channels"
"""
# It's a key from response which is paginated. For example "users" or "channels"
listed_key = kwargs["paginated_key"]
api_method = getattr(self, method)
cumulative_response = {}
cursor = kwargs.get("cursor")
cursor = kwargs["cursor"]
rate_limited = False
try:
response = self.api_call(*args, **kwargs)
response = api_method(**kwargs)
cumulative_response = response
cursor = response["response_metadata"]["next_cursor"]
@ -77,8 +80,8 @@ class SlackClientWithErrorHandling(SlackClient):
):
next_cursor = response["response_metadata"]["next_cursor"]
kwargs["cursor"] = next_cursor
response = self.api_call(*args, **kwargs)
cumulative_response[listed_key] += response[listed_key]
response = api_method(**kwargs)
cumulative_response[paginated_key] += response[paginated_key]
cursor = next_cursor
except SlackAPIRateLimitException:
@ -87,7 +90,10 @@ class SlackClientWithErrorHandling(SlackClient):
return cumulative_response, cursor, rate_limited
def api_call(self, *args, **kwargs):
response = super(SlackClientWithErrorHandling, self).api_call(*args, **kwargs)
try:
response = super(SlackClientWithErrorHandling, self).api_call(*args, **kwargs)
except SlackApiError as err:
response = err.response
if not response["ok"]:
exception_text = "Slack API Call Error: {} \nArgs: {} \nKwargs: {} \nResponse: {}".format(

View file

@ -5,11 +5,11 @@ import uuid
from django.db import models
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import (
from apps.slack.client import (
SlackAPIChannelArchivedException,
SlackAPIException,
SlackAPITokenException,
SlackClientWithErrorHandling,
)
if typing.TYPE_CHECKING:
@ -83,11 +83,7 @@ class SlackMessage(models.Model):
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
result = None
try:
result = sc.api_call(
"chat.getPermalink",
channel=self.channel_id,
message_ts=self.slack_id,
)
result = sc.chat_getPermalink(channel=self.channel_id, message_ts=self.slack_id)
except SlackAPIException as e:
if e.response["error"] == "message_not_found":
return "https://slack.com/resources/using-slack/page/404"
@ -143,8 +139,7 @@ class SlackMessage(models.Model):
channel_id = slack_message.channel_id
try:
result = sc.api_call(
"chat.postMessage",
result = sc.chat_postMessage(
channel=channel_id,
text=text,
blocks=blocks,
@ -190,7 +185,7 @@ class SlackMessage(models.Model):
if slack_user_identity:
channel_members = []
try:
channel_members = sc.api_call("conversations.members", channel=channel_id)["members"]
channel_members = sc.conversations_members(channel=channel_id)["members"]
except SlackAPIException as e:
if e.response["error"] == "fetch_members_failed":
logger.warning(

View file

@ -5,9 +5,8 @@ from django.db import models
from django.db.models import JSONField
from apps.api.permissions import RBACPermission
from apps.slack.client import SlackAPIException, SlackAPITokenException, SlackClientWithErrorHandling
from apps.slack.constants import SLACK_INVALID_AUTH_RESPONSE, SLACK_WRONG_TEAM_NAMES
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from apps.user_management.models.user import User
from common.insight_log.chatops_insight_logs import ChatOpsEvent, ChatOpsTypePlug, write_chatops_insight_log
@ -87,7 +86,7 @@ class SlackTeamIdentity(models.Model):
def bot_id(self):
if self.cached_bot_id is None:
sc = SlackClientWithErrorHandling(self.bot_access_token)
auth = sc.api_call("auth.test")
auth = sc.auth_test()
self.cached_bot_id = auth.get("bot_id")
self.save(update_fields=["cached_bot_id"])
return self.cached_bot_id
@ -99,7 +98,7 @@ class SlackTeamIdentity(models.Model):
next_cursor = None
members = []
while next_cursor != "" or next_cursor is None:
result = sc.api_call("users.list", cursor=next_cursor, team=self)
result = sc.users_list(cursor=next_cursor, team=self)
next_cursor = result["response_metadata"]["next_cursor"]
members += result["members"]
@ -110,7 +109,7 @@ class SlackTeamIdentity(models.Model):
if self.cached_name is None or self.cached_name in SLACK_WRONG_TEAM_NAMES:
try:
sc = SlackClientWithErrorHandling(self.bot_access_token)
result = sc.api_call("team.info")
result = sc.team_info()
self.cached_name = result["team"]["name"]
self.save()
except SlackAPIException as e:
@ -125,7 +124,7 @@ class SlackTeamIdentity(models.Model):
def app_id(self):
if not self.cached_app_id:
sc = SlackClientWithErrorHandling(self.bot_access_token)
result = sc.api_call("bots.info", bot=self.bot_id)
result = sc.bots_info(bot=self.bot_id)
app_id = result["bot"]["app_id"]
self.cached_app_id = app_id
self.save(update_fields=["cached_app_id"])
@ -140,10 +139,10 @@ class SlackTeamIdentity(models.Model):
**User.build_permissions_query(RBACPermission.Permissions.CHATOPS_WRITE, organization),
)
def get_conversation_members(self, slack_client, channel_id):
def get_conversation_members(self, slack_client: SlackClientWithErrorHandling, channel_id: str):
try:
members = slack_client.paginated_api_call(
"conversations.members", channel=channel_id, paginated_key="members"
"conversations_members", paginated_key="members", channel=channel_id
)["members"]
except SlackAPITokenException as e:
logger.warning(

View file

@ -4,10 +4,9 @@ import typing
import requests
from django.db import models
from apps.slack.client import SlackAPIException, SlackAPITokenException, SlackClientWithErrorHandling
from apps.slack.constants import SLACK_BOT_ID
from apps.slack.scenarios.notified_user_not_in_channel import NotifiedUserNotInChannelStep
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from apps.user_management.models import Organization, User
if typing.TYPE_CHECKING:
@ -135,8 +134,7 @@ class SlackUserIdentity(models.Model):
]
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
return sc.api_call(
"chat.postMessage",
return sc.chat_postMessage(
channel=self.im_channel_id,
text="You are invited to look at an alert group!",
blocks=blocks,
@ -158,11 +156,7 @@ class SlackUserIdentity(models.Model):
if self.cached_slack_login is None or self.cached_slack_login == "slack_token_revoked_unable_to_cache_login":
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
try:
result = sc.api_call(
"users.info",
user=self.slack_id,
team=self.slack_team_identity,
)
result = sc.users_info(user=self.slack_id, team=self.slack_team_identity)
self.cached_slack_login = result["user"]["name"]
self.save()
except SlackAPITokenException as e:
@ -187,11 +181,7 @@ class SlackUserIdentity(models.Model):
if self.cached_timezone is None or self.cached_timezone == "None":
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
try:
result = sc.api_call(
"users.info",
user=self.slack_id,
timeout=5,
)
result = sc.users_info(user=self.slack_id)
tz_from_slack = result["user"].get("tz", "UTC")
if tz_from_slack == "None" or tz_from_slack is None:
tz_from_slack = "UTC"
@ -210,7 +200,7 @@ class SlackUserIdentity(models.Model):
if self.cached_im_channel_id is None:
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
try:
result = sc.api_call("conversations.open", users=self.slack_id, return_im=True)
result = sc.conversations_open(users=self.slack_id, return_im=True)
self.cached_im_channel_id = result["channel"]["id"]
self.save()
except SlackAPIException as e:
@ -225,11 +215,7 @@ class SlackUserIdentity(models.Model):
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
logger.info("Update user profile info")
try:
result = sc.api_call(
"users.info",
user=self.slack_id,
team=self.slack_team_identity,
)
result = sc.users_info(user=self.slack_id, team=self.slack_team_identity)
except SlackAPITokenException as e:
logger.warning(f"Unable to get user info due token revoked or account inactive: {e}")
result = None

View file

@ -9,8 +9,7 @@ from django.db.models import JSONField
from django.utils import timezone
from apps.api.permissions import RBACPermission
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.client import SlackAPIException, SlackClientWithErrorHandling
from apps.user_management.models.user import User
from common.public_primary_keys import generate_public_primary_key, increase_public_primary_key_length
@ -69,10 +68,10 @@ class SlackUserGroup(models.Model):
@property
def can_be_updated(self) -> bool:
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token, timeout=5)
try:
sc.api_call("usergroups.update", usergroup=self.slack_id, timeout=5)
sc.usergroups_update(usergroup=self.slack_id)
return True
except (SlackAPIException, requests.exceptions.Timeout):
return False
@ -112,11 +111,7 @@ class SlackUserGroup(models.Model):
def update_members(self, slack_ids):
sc = SlackClientWithErrorHandling(self.slack_team_identity.bot_access_token)
sc.api_call(
"usergroups.users.update",
usergroup=self.slack_id,
users=slack_ids,
)
sc.usergroups_users_update(usergroup=self.slack_id, users=slack_ids)
self.members = slack_ids
self.save(update_fields=("members",))
@ -132,18 +127,14 @@ class SlackUserGroup(models.Model):
sc = SlackClientWithErrorHandling(slack_team_identity.bot_access_token)
bot_access_token_accepted = True
try:
usergroups_list = sc.api_call(
"usergroups.list",
)
usergroups_list = sc.usergroups_list()
except SlackAPIException as e:
if e.response["error"] == "not_allowed_token_type":
# Trying same request with access token. It is required due to migration to granular permissions
# and can be removed after clients reinstall their bots
try:
sc_with_access_token = SlackClientWithErrorHandling(slack_team_identity.access_token)
usergroups_list = sc_with_access_token.api_call(
"usergroups.list",
)
usergroups_list = sc_with_access_token.usergroups_list()
bot_access_token_accepted = False
except SlackAPIException as err:
if err.response["error"] == "missing_scope":
@ -159,16 +150,10 @@ class SlackUserGroup(models.Model):
if usergroup["id"] == slack_id:
try:
if bot_access_token_accepted:
usergroups_users = sc.api_call(
"usergroups.users.list",
usergroup=usergroup["id"],
)
usergroups_users = sc.usergroups_users_list(usergroup=usergroup["id"])
else:
sc_with_access_token = SlackClientWithErrorHandling(slack_team_identity.access_token)
usergroups_users = sc_with_access_token.api_call(
"usergroups.users.list",
usergroup=usergroup["id"],
)
usergroups_users = sc_with_access_token.usergroups_users_list(usergroup=usergroup["id"])
except SlackAPIException as e:
if e.response["error"] == "no_such_subteam":
logger.info("User group does not exist")

View file

@ -66,11 +66,7 @@ class OpenAlertAppearanceDialogStep(AlertGroupActionsMixin, scenario_step.Scenar
"private_metadata": json.dumps(private_metadata),
}
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
class UpdateAppearanceStep(scenario_step.ScenarioStep):
@ -90,8 +86,7 @@ class UpdateAppearanceStep(scenario_step.ScenarioStep):
attachments = alert_group.render_slack_attachments()
blocks = alert_group.render_slack_blocks()
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=alert_group.slack_message.channel_id,
ts=alert_group.slack_message.slack_id,
attachments=attachments,

View file

@ -15,16 +15,16 @@ from apps.alerts.models import Alert, AlertGroup, AlertGroupLogRecord, AlertRece
from apps.alerts.tasks import custom_button_result
from apps.alerts.utils import render_curl_command
from apps.api.permissions import RBACPermission
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME, SLACK_RATE_LIMIT_DELAY
from apps.slack.scenarios import scenario_step
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import (
from apps.slack.client import (
SlackAPIChannelArchivedException,
SlackAPIException,
SlackAPIRateLimitException,
SlackAPITokenException,
SlackClientWithErrorHandling,
)
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME, SLACK_RATE_LIMIT_DELAY
from apps.slack.scenarios import scenario_step
from apps.slack.scenarios.slack_renderer import AlertGroupLogSlackRenderer
from apps.slack.slack_formatter import SlackFormatter
from apps.slack.tasks import (
post_or_update_log_report_message_task,
@ -133,9 +133,7 @@ class AlertShootingStep(scenario_step.ScenarioStep):
return
try:
result = self._slack_client.api_call(
"chat.postMessage", channel=channel_id, attachments=attachments, blocks=blocks
)
result = self._slack_client.chat_postMessage(channel=channel_id, attachments=attachments, blocks=blocks)
alert_group.slack_messages.create(
slack_id=result["ts"],
@ -147,8 +145,7 @@ class AlertShootingStep(scenario_step.ScenarioStep):
# If alert was made out of a message:
if alert_group.channel.integration == AlertReceiveChannel.INTEGRATION_SLACK_CHANNEL:
channel = json.loads(alert.integration_unique_data)["channel"]
result = self._slack_client.api_call(
"chat.postMessage",
result = self._slack_client.chat_postMessage(
channel=channel,
thread_ts=json.loads(alert.integration_unique_data)["ts"],
text=":rocket: <{}|Incident registered!>".format(alert_group.slack_message.permalink),
@ -200,8 +197,7 @@ class AlertShootingStep(scenario_step.ScenarioStep):
blocks: Block.AnyBlocks = []
text = "Escalations are silenced due to Debug mode"
blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": text}})
self._slack_client.api_call(
"chat.postMessage",
self._slack_client.chat_postMessage(
channel=channel_id,
text=text,
attachments=[],
@ -394,11 +390,7 @@ class SelectAttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
},
}
)
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
def get_select_incidents_blocks(self, alert_group: AlertGroup) -> Block.AnyBlocks:
collected_options: typing.List[CompositionObjectOption] = []
@ -482,8 +474,7 @@ class AttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
slack_user_identity = log_record.author.slack_user_identity
if slack_user_identity:
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
user=slack_user_identity.slack_id,
channel=alert_group.slack_message.channel_id,
text="{}{}".format(ephemeral_text[:1].upper(), ephemeral_text[1:]),
@ -757,8 +748,7 @@ class UnAcknowledgeGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep)
)
if alert_group.slack_message.ack_reminder_message_ts:
try:
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=channel_id,
ts=alert_group.slack_message.ack_reminder_message_ts,
text=text,
@ -803,17 +793,11 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep):
if self.user == alert_group.acknowledged_by_user:
user_verbal = alert_group.acknowledged_by_user.get_username_with_slack_verbal()
text = f"{user_verbal} confirmed that the Alert Group is still acknowledged."
self._slack_client.api_call(
"chat.update",
channel=channel,
ts=message_ts,
text=text,
)
self._slack_client.chat_update(channel=channel, ts=message_ts, text=text)
alert_group.acknowledged_by_confirmed = datetime.utcnow()
alert_group.save(update_fields=["acknowledged_by_confirmed"])
else:
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=channel,
user=slack_user_identity.slack_id,
text="This Alert Group is acknowledged by another user. Acknowledge it yourself first.",
@ -821,22 +805,12 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep):
elif alert_group.acknowledged_by == AlertGroup.SOURCE:
user_verbal = self.user.get_username_with_slack_verbal()
text = f"{user_verbal} confirmed that the Alert Group is still acknowledged."
self._slack_client.api_call(
"chat.update",
channel=channel,
ts=message_ts,
text=text,
)
self._slack_client.chat_update(channel=channel, ts=message_ts, text=text)
alert_group.acknowledged_by_confirmed = datetime.utcnow()
alert_group.save(update_fields=["acknowledged_by_confirmed"])
else:
self._slack_client.api_call(
"chat.delete",
channel=channel,
ts=message_ts,
)
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_delete(channel=channel, ts=message_ts)
self._slack_client.chat_postEphemeral(
channel=channel,
user=slack_user_identity.slack_id,
text="This Alert Group is already unacknowledged.",
@ -877,8 +851,7 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep):
}
]
try:
response = self._slack_client.api_call(
"chat.postMessage",
response = self._slack_client.chat_postMessage(
channel=channel_id,
text=text,
attachments=attachments,
@ -944,11 +917,7 @@ class DeleteGroupStep(scenario_step.ScenarioStep):
for message_ts in bot_messages_ts:
try:
self._slack_client.api_call(
"chat.delete",
channel=channel_id,
ts=message_ts,
)
self._slack_client.chat_delete(channel=channel_id, ts=message_ts)
except SlackAPITokenException as e:
logger.error(
f"Unable to delete messages in slack. Message ts: {message_ts}"
@ -981,11 +950,7 @@ class DeleteGroupStep(scenario_step.ScenarioStep):
sc_with_access_token = SlackClientWithErrorHandling(
self.slack_team_identity.access_token
) # used access_token instead of bot_access_token
sc_with_access_token.api_call(
"chat.delete",
channel=channel_id,
ts=message_ts,
)
sc_with_access_token.chat_delete(channel=channel_id, ts=message_ts)
else:
raise e
@ -994,12 +959,7 @@ class DeleteGroupStep(scenario_step.ScenarioStep):
message.added_to_resolution_note = False
message.save(update_fields=["added_to_resolution_note"])
try:
self._slack_client.api_call(
"reactions.remove",
channel=message.slack_channel_id,
name="memo",
timestamp=message.ts,
)
self._slack_client.reactions_remove(channel=message.slack_channel_id, name="memo", timestamp=message.ts)
except SlackAPITokenException as e:
logger.warning(
f"Unable to delete resolution note reaction in slack. "
@ -1030,8 +990,8 @@ class UpdateLogReportMessageStep(scenario_step.ScenarioStep):
if slack_log_message is None:
logger.debug(f"Start posting new log message for alert_group {alert_group.pk}")
try:
result = self._slack_client.api_call(
"chat.postMessage", channel=slack_message.channel_id, thread_ts=slack_message.slack_id, text=text
result = self._slack_client.chat_postMessage(
channel=slack_message.channel_id, thread_ts=slack_message.slack_id, text=text
)
except SlackAPITokenException as e:
print(e)
@ -1090,8 +1050,7 @@ class UpdateLogReportMessageStep(scenario_step.ScenarioStep):
f"Update log message for alert_group {alert_group.pk}, slack_log_message {slack_log_message.pk}"
)
try:
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=slack_message.channel_id,
text="Alert Group log",
ts=slack_log_message.slack_id,

View file

@ -3,8 +3,8 @@ import typing
from django.utils import timezone
from apps.slack.client import SlackClientWithErrorHandling
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
if typing.TYPE_CHECKING:
@ -24,7 +24,7 @@ class InvitedToChannelStep(scenario_step.ScenarioStep):
if payload["event"]["user"] == slack_team_identity.bot_user_id:
channel_id = payload["event"]["channel"]
slack_client = SlackClientWithErrorHandling(slack_team_identity.bot_access_token)
channel = slack_client.api_call("conversations.info", channel=channel_id)["channel"]
channel = slack_client.conversations_info(channel=channel_id)["channel"]
slack_team_identity.cached_channels.update_or_create(
slack_id=channel["id"],

View file

@ -46,11 +46,7 @@ class StartManageResponders(AlertGroupActionsMixin, scenario_step.ScenarioStep):
return
view = render_dialog(alert_group)
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
class ManageRespondersUserChange(scenario_step.ScenarioStep):
@ -77,11 +73,7 @@ class ManageRespondersUserChange(scenario_step.ScenarioStep):
ManageRespondersConfirmUserChange.routing_uid(),
json.dumps({USER_DATA_KEY: selected_user.id, ALERT_GROUP_DATA_KEY: alert_group.pk}),
)
self._slack_client.api_call(
"views.push",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_push(trigger_id=payload["trigger_id"], view=view)
else:
try:
# no warnings, proceed with paging
@ -96,8 +88,7 @@ class ManageRespondersUserChange(scenario_step.ScenarioStep):
except DirectPagingAlertGroupResolvedError:
view = render_dialog(alert_group, alert_group_resolved_warning=True)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -129,8 +120,7 @@ class ManageRespondersConfirmUserChange(scenario_step.ScenarioStep):
except DirectPagingAlertGroupResolvedError:
view = render_dialog(alert_group, alert_group_resolved_warning=True)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["previous_view_id"],
@ -162,8 +152,7 @@ class ManageRespondersScheduleChange(scenario_step.ScenarioStep):
except DirectPagingAlertGroupResolvedError:
view = render_dialog(alert_group, alert_group_resolved_warning=True)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -185,8 +174,7 @@ class ManageRespondersRemoveUser(scenario_step.ScenarioStep):
unpage_user(alert_group, selected_user, from_user)
view = render_dialog(alert_group)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],

View file

@ -5,9 +5,9 @@ from uuid import uuid4
from django.conf import settings
from apps.alerts.models import AlertReceiveChannel, ChannelFilter
from apps.slack.client import SlackAPIException
from apps.slack.constants import DIVIDER
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.types import (
Block,
BlockActionType,
@ -68,11 +68,7 @@ class StartCreateIncidentFromSlashCommand(scenario_step.ScenarioStep):
FinishCreateIncidentFromSlashCommand.routing_uid(), blocks, json.dumps(private_metadata)
)
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
class FinishCreateIncidentFromSlashCommand(scenario_step.ScenarioStep):
@ -115,16 +111,14 @@ class FinishCreateIncidentFromSlashCommand(scenario_step.ScenarioStep):
author_username = slack_user_identity.slack_verbal
try:
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=channel_id,
user=slack_user_identity.slack_id,
text=":white_check_mark: Alert *{}* successfully submitted".format(title),
)
except SlackAPIException as e:
if e.response["error"] == "channel_not_found":
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=slack_user_identity.im_channel_id,
user=slack_user_identity.slack_id,
text=":white_check_mark: Alert *{}* successfully submitted".format(title),
@ -201,8 +195,7 @@ class OnOrgChange(scenario_step.ScenarioStep):
if with_title_and_message_inputs:
blocks.extend([_get_title_input(payload), _get_message_input(payload)])
view = _get_manual_incident_form_view(submit_routing_uid, blocks, json.dumps(new_private_metadata))
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -249,8 +242,7 @@ class OnTeamChange(scenario_step.ScenarioStep):
if with_title_and_message_inputs:
blocks.extend([_get_title_input(payload), _get_message_input(payload)])
view = _get_manual_incident_form_view(submit_routing_uid, blocks, json.dumps(new_private_metadata))
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],

View file

@ -1,7 +1,7 @@
import typing
from apps.slack.client import SlackAPIException, SlackAPITokenException
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from apps.slack.types import Block
if typing.TYPE_CHECKING:
@ -72,8 +72,7 @@ class NotificationDeliveryStep(scenario_step.ScenarioStep):
]
try:
# TODO: slack-onprem, check exceptions
self._slack_client.api_call(
"chat.postMessage",
self._slack_client.chat_postMessage(
channel=channel,
text=text,
blocks=blocks,

View file

@ -15,9 +15,9 @@ from apps.alerts.paging import (
check_user_availability,
direct_paging,
)
from apps.slack.client import SlackAPIException
from apps.slack.constants import DIVIDER, PRIVATE_METADATA_MAX_LENGTH
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.types import (
Block,
BlockActionType,
@ -147,8 +147,7 @@ class StartDirectPaging(scenario_step.ScenarioStep):
}
initial_payload = {"view": {"private_metadata": json.dumps(private_metadata)}}
view = render_dialog(slack_user_identity, slack_team_identity, initial_payload, initial=True)
self._slack_client.api_call(
"views.open",
self._slack_client.views_open(
trigger_id=payload["trigger_id"],
view=view,
)
@ -203,16 +202,14 @@ class FinishDirectPaging(scenario_step.ScenarioStep):
text = ":white_check_mark: Alert group *{}* created: {}".format(title, alert_group.web_link)
try:
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=channel_id,
user=slack_user_identity.slack_id,
text=text,
)
except SlackAPIException as e:
if e.response["error"] == "channel_not_found":
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=slack_user_identity.im_channel_id,
user=slack_user_identity.slack_id,
text=text,
@ -235,8 +232,7 @@ class OnPagingOrgChange(scenario_step.ScenarioStep):
) -> None:
updated_payload = reset_items(payload)
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=updated_payload["trigger_id"],
view=view,
view_id=updated_payload["view"]["id"],
@ -253,8 +249,7 @@ class OnPagingTeamChange(scenario_step.ScenarioStep):
payload: EventPayload,
) -> None:
view = render_dialog(slack_user_identity, slack_team_identity, payload)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -290,11 +285,7 @@ class OnPagingUserChange(scenario_step.ScenarioStep):
if availability_warnings:
# display warnings and require additional confirmation
view = _display_availability_warnings(payload, availability_warnings, selected_organization, selected_user)
self._slack_client.api_call(
"views.push",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_push(trigger_id=payload["trigger_id"], view=view)
else:
# user is available to be paged
error_msg = None
@ -304,8 +295,7 @@ class OnPagingUserChange(scenario_step.ScenarioStep):
updated_payload = payload
error_msg = "Cannot add user, maximum responders exceeded"
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload, error_msg=error_msg)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -338,12 +328,7 @@ class OnPagingItemActionChange(scenario_step.ScenarioStep):
error_msg = "Cannot update policy, maximum responders exceeded"
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload, error_msg=error_msg)
self._slack_client.api_call(
"views.update",
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
)
self._slack_client.views_update(trigger_id=payload["trigger_id"], view=view, view_id=payload["view"]["id"])
class OnPagingConfirmUserChange(scenario_step.ScenarioStep):
@ -380,8 +365,7 @@ class OnPagingConfirmUserChange(scenario_step.ScenarioStep):
updated_payload = payload
error_msg = "Cannot add user, maximum responders exceeded"
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload, error_msg=error_msg)
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["previous_view_id"],
@ -412,12 +396,7 @@ class OnPagingScheduleChange(scenario_step.ScenarioStep):
updated_payload = payload
error_msg = "Cannot add schedule, maximum responders exceeded"
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload, error_msg=error_msg)
self._slack_client.api_call(
"views.update",
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
)
self._slack_client.views_update(trigger_id=payload["trigger_id"], view=view, view_id=payload["view"]["id"])
# slack view/blocks rendering helpers

View file

@ -6,9 +6,9 @@ import typing
from django.db.models import Q
from apps.api.permissions import RBACPermission
from apps.slack.client import SlackAPIException
from apps.slack.constants import DIVIDER
from apps.slack.scenarios import scenario_step
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.types import (
Block,
BlockActionType,
@ -69,7 +69,7 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep):
alert_group = slack_message.alert_group
if not alert_group:
self.open_warning_window(payload, warning_text)
print(
logger.exception(
f"Exception: tried to add message from thread to Resolution Note: "
f"Slack Team Identity pk: {self.slack_team_identity.pk}, "
f"Slack Message id: {slack_message.slack_id}"
@ -80,11 +80,7 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep):
message_ts = payload["message_ts"]
thread_ts = payload["message"]["thread_ts"]
result = self._slack_client.api_call(
"chat.getPermalink",
channel=channel_id,
message_ts=message_ts,
)
result = self._slack_client.chat_getPermalink(channel=channel_id, message_ts=message_ts)
permalink = None
if result["permalink"] is not None:
permalink = result["permalink"]
@ -155,8 +151,7 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep):
else:
resolution_note.recreate()
try:
self._slack_client.api_call(
"reactions.add",
self._slack_client.reactions_add(
channel=channel_id,
name="memo",
timestamp=resolution_note_slack_message.ts,
@ -189,8 +184,7 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep):
resolution_note_slack_message.save(update_fields=["added_to_resolution_note"])
if resolution_note_slack_message.posted_by_bot:
try:
self._slack_client.api_call(
"chat.delete",
self._slack_client.chat_delete(
channel=resolution_note_slack_message.slack_channel_id,
ts=resolution_note_slack_message.ts,
)
@ -240,8 +234,7 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep):
if resolution_note_slack_message is None:
try:
result = self._slack_client.api_call(
"chat.postMessage",
result = self._slack_client.chat_postMessage(
channel=alert_group_slack_message.channel_id,
thread_ts=alert_group_slack_message.slack_id,
text=resolution_note.text,
@ -270,8 +263,7 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep):
raise e
else:
message_ts = result["message"]["ts"]
result_permalink = self._slack_client.api_call(
"chat.getPermalink",
result_permalink = self._slack_client.chat_getPermalink(
channel=alert_group_slack_message.channel_id,
message_ts=message_ts,
)
@ -295,8 +287,7 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep):
resolution_note.save(update_fields=["resolution_note_slack_message"])
elif resolution_note_slack_message.posted_by_bot:
try:
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=alert_group_slack_message.channel_id,
ts=resolution_note_slack_message.ts,
text=resolution_note_slack_message.text,
@ -345,25 +336,23 @@ class UpdateResolutionNoteStep(scenario_step.ScenarioStep):
def add_resolution_note_reaction(self, slack_thread_message: "ResolutionNoteSlackMessage"):
try:
self._slack_client.api_call(
"reactions.add",
self._slack_client.reactions_add(
channel=slack_thread_message.slack_channel_id,
name="memo",
timestamp=slack_thread_message.ts,
)
except SlackAPIException as e:
print(e) # TODO:770: log instead of print
logger.exception(e)
def remove_resolution_note_reaction(self, slack_thread_message: "ResolutionNoteSlackMessage") -> None:
try:
self._slack_client.api_call(
"reactions.remove",
self._slack_client.reactions_remove(
channel=slack_thread_message.slack_channel_id,
name="memo",
timestamp=slack_thread_message.ts,
)
except SlackAPIException as e:
print(e)
logger.exception(e)
def get_resolution_note_blocks(self, resolution_note: "ResolutionNote") -> Block.AnyBlocks:
blocks: Block.AnyBlocks = []
@ -453,8 +442,7 @@ class ResolutionNoteModalStep(AlertGroupActionsMixin, scenario_step.ScenarioStep
if "update" in resolution_note_window_action:
try:
self._slack_client.api_call(
"views.update",
self._slack_client.views_update(
trigger_id=payload["trigger_id"],
view=view,
view_id=payload["view"]["id"],
@ -470,11 +458,7 @@ class ResolutionNoteModalStep(AlertGroupActionsMixin, scenario_step.ScenarioStep
else:
raise
else:
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
def get_resolution_notes_blocks(
self, alert_group: "AlertGroup", resolution_note_window_action: str, action_resolve: bool

View file

@ -3,7 +3,7 @@ import logging
import typing
from apps.slack.alert_group_slack_service import AlertGroupSlackService
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.client import SlackClientWithErrorHandling
if typing.TYPE_CHECKING:
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
@ -77,8 +77,4 @@ class ScenarioStep(object):
},
],
}
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)

View file

@ -61,11 +61,7 @@ class EditScheduleShiftNotifyStep(scenario_step.ScenarioStep):
"private_metadata": json.dumps(private_metadata),
}
self._slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
self._slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
def set_selected_value(self, slack_user_identity: "SlackUserIdentity", payload: EventPayload) -> None:
action = payload["actions"][0]

View file

@ -156,7 +156,7 @@ class BaseShiftSwapRequestStep(scenario_step.ScenarioStep):
organization = self.organization
blocks = self._generate_blocks(shift_swap_request)
result = self._slack_client.api_call("chat.postMessage", channel=channel_id, blocks=blocks)
result = self._slack_client.chat_postMessage(channel=channel_id, blocks=blocks)
return SlackMessage.objects.create(
slack_id=result["ts"],
@ -166,9 +166,7 @@ class BaseShiftSwapRequestStep(scenario_step.ScenarioStep):
)
def update_message(self, shift_swap_request: "ShiftSwapRequest") -> None:
# TODO: better error handling here...
self._slack_client.api_call(
"chat.update",
self._slack_client.chat_update(
channel=shift_swap_request.slack_channel_id,
ts=shift_swap_request.slack_message.slack_id,
blocks=self._generate_blocks(shift_swap_request),
@ -229,8 +227,7 @@ class ShiftSwapRequestFollowUp(scenario_step.ScenarioStep):
]
def post_message(self, shift_swap_request: "ShiftSwapRequest") -> None:
self._slack_client.api_call(
"chat.postMessage",
self._slack_client.chat_postMessage(
channel=shift_swap_request.slack_message.channel_id,
thread_ts=shift_swap_request.slack_message.slack_id,
reply_broadcast=True,

View file

@ -75,18 +75,13 @@ class SlackChannelMessageEventStep(scenario_step.ScenarioStep):
# SlackMessage instances without alert_group set (e.g., SSR Slack messages)
return
result = self._slack_client.api_call(
"chat.getPermalink",
channel=channel,
message_ts=message_ts,
)
result = self._slack_client.chat_getPermalink(channel=channel, message_ts=message_ts)
permalink = None
if result["permalink"] is not None:
permalink = result["permalink"]
if len(text) > 2900:
self._slack_client.api_call(
"chat.postEphemeral",
self._slack_client.chat_postEphemeral(
channel=channel,
user=slack_user_identity.slack_id,
text=":warning: Unable to show the <{}|message> in Resolution Note: the message is too long ({}). "

View file

@ -1 +0,0 @@
from .slack_client import SlackClientWithErrorHandling # noqa: F401

View file

@ -1,22 +0,0 @@
class SlackAPIException(Exception):
def __init__(self, *args, **kwargs):
self.response = {}
if "response" in kwargs:
self.response = kwargs["response"]
super().__init__(*args)
class SlackAPITokenException(SlackAPIException):
pass
class SlackAPIChannelArchivedException(SlackAPIException):
pass
class SlackAPIRateLimitException(SlackAPIException):
pass
class SlackClientException(Exception):
pass

View file

@ -1,26 +0,0 @@
import json
from slackclient.server import Server
from .exceptions import SlackClientException
class SlackClientServer(Server):
def api_call(self, token, request="?", timeout=None, **kwargs):
"""
This method is rewritten because we want to handle JSONDecodeError and add more information about response
"""
response = self.api_requester.do(token, request, kwargs, timeout=timeout)
response_json = {"headers": dict(response.headers)}
resp_text = response.text
try:
response_json.update(json.loads(resp_text))
except json.JSONDecodeError:
response_json["response_text"] = resp_text
exception_text = (
f"Slack API Call Error: unexpected response from Slack \n"
f"Status: {response.status_code}\nArgs: ('{request}',) \nKwargs: {kwargs} \n"
f"Response: {response_json}"
)
raise SlackClientException(exception_text)
return json.dumps(response_json)

View file

@ -10,10 +10,9 @@ from django.utils import timezone
from apps.alerts.tasks.compare_escalations import compare_escalations
from apps.slack.alert_group_slack_service import AlertGroupSlackService
from apps.slack.client import SlackAPIException, SlackAPITokenException, SlackClientWithErrorHandling
from apps.slack.constants import CACHE_UPDATE_INCIDENT_SLACK_MESSAGE_LIFETIME, SLACK_BOT_ID
from apps.slack.scenarios.scenario_step import ScenarioStep
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from apps.slack.utils import (
get_cache_key_update_incident_slack_message,
get_populate_slack_channel_task_id_key,
@ -371,9 +370,7 @@ def populate_slack_usergroups_for_team(slack_team_identity_id):
usergroups_list = None
bot_access_token_accepted = True
try:
usergroups_list = sc.api_call(
"usergroups.list",
)
usergroups_list = sc.usergroups_list()
except SlackAPITokenException as e:
logger.info(f"token revoked\n{e}")
except SlackAPIException as e:
@ -382,9 +379,7 @@ def populate_slack_usergroups_for_team(slack_team_identity_id):
# Trying same request with access token. It is required due to migration to granular permissions
# and can be removed after clients reinstall their bots
sc_with_access_token = SlackClientWithErrorHandling(slack_team_identity.access_token)
usergroups_list = sc_with_access_token.api_call(
"usergroups.list",
)
usergroups_list = sc_with_access_token.usergroups_list()
bot_access_token_accepted = False
except SlackAPIException as err:
handle_usergroups_list_slack_api_exception(err)
@ -402,16 +397,10 @@ def populate_slack_usergroups_for_team(slack_team_identity_id):
continue
try:
if bot_access_token_accepted:
usergroups_users = sc.api_call(
"usergroups.users.list",
usergroup=usergroup["id"],
)
usergroups_users = sc.usergroups_users_list(usergroup=usergroup["id"])
else:
sc_with_access_token = SlackClientWithErrorHandling(slack_team_identity.access_token)
usergroups_users = sc_with_access_token.api_call(
"usergroups.users.list",
usergroup=usergroup["id"],
)
usergroups_users = sc_with_access_token.usergroups_users_list(usergroup=usergroup["id"])
except SlackAPIException as e:
if e.response["error"] == "no_such_subteam":
logger.info("User group does not exist")
@ -549,11 +538,13 @@ def populate_slack_channels_for_team(slack_team_identity_id: int, cursor: Option
return start_populate_slack_channels_for_team(slack_team_identity_id, delay)
try:
response, cursor, rate_limited = sc.paginated_api_call_with_ratelimit(
"conversations.list",
types="public_channel,private_channel",
"conversations_list",
paginated_key="channels",
limit=1000,
cursor=cursor,
json={
"types": "public_channel,private_channel",
"limit": 1000,
"cursor": cursor,
},
)
except SlackAPITokenException as e:
logger.info(f"token revoked\n{e}")

View file

@ -3,7 +3,7 @@ from unittest.mock import patch
import pytest
from django.utils import timezone
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.client import SlackClientWithErrorHandling
from apps.slack.tasks import populate_slack_channels_for_team

View file

@ -801,9 +801,14 @@ def test_step_format_alert(
assert mock_slack_api_call.call_args.args == ("views.open",)
@patch("apps.slack.models.SlackTeamIdentity.get_conversation_members")
@pytest.mark.django_db
def test_step_resolution_note(
make_organization_and_user_with_slack_identities, make_alert_receive_channel, make_alert_group, make_alert
mock_get_conversation_members,
make_organization_and_user_with_slack_identities,
make_alert_receive_channel,
make_alert_group,
make_alert,
):
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
@ -811,6 +816,7 @@ def test_step_resolution_note(
alert_group = make_alert_group(alert_receive_channel)
make_alert(alert_group, raw_request_data={})
channel_id = "RANDOM_CHANNEL_ID"
payload = {
"trigger_id": "RANDOM_TRIGGER_ID",
"actions": [
@ -825,14 +831,15 @@ def test_step_resolution_note(
),
}
],
"channel": {"id": "RANDOM_CHANNEL_ID"},
"channel": {"id": channel_id},
"message": {"ts": "RANDOM_MESSAGE_TS"},
}
step_class = ScenarioStep.get_step("resolution_note", "ResolutionNoteModalStep")
step = step_class(organization=organization, user=user, slack_team_identity=slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_open") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.open",)
mock_slack_api_call.assert_called_once()
mock_get_conversation_members.assert_called_once_with(step._slack_client, channel_id)

View file

@ -3,9 +3,9 @@ from unittest.mock import patch
import pytest
from apps.alerts.models import AlertGroup
from apps.slack.client import SlackAPIException
from apps.slack.models import SlackMessage
from apps.slack.scenarios.scenario_step import ScenarioStep
from apps.slack.slack_client.exceptions import SlackAPIException
@pytest.mark.django_db

View file

@ -94,10 +94,9 @@ def test_initial_state(manage_responders_setup):
organization, user, slack_team_identity, slack_user_identity = manage_responders_setup
step = StartManageResponders(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_open") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.open",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[ALERT_GROUP_DATA_KEY] == ALERT_GROUP_ID
@ -137,11 +136,9 @@ def test_add_user_no_warning(manage_responders_setup, make_schedule, make_on_cal
payload = make_slack_payload(user=user)
step = ManageRespondersUserChange(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
# check there's a delete button for the user
assert mock_slack_api_call.call_args.kwargs["view"]["blocks"][0]["accessory"]["value"] == str(user.pk)
@ -153,10 +150,9 @@ def test_add_user_raise_warning(manage_responders_setup):
payload = make_slack_payload(user=user)
step = ManageRespondersUserChange(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_push") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.push",)
assert mock_slack_api_call.call_args.kwargs["view"]["callback_id"] == "ManageRespondersConfirmUserChange"
text_from_blocks = "".join(
b["text"]["text"] for b in mock_slack_api_call.call_args.kwargs["view"]["blocks"] if b["type"] == "section"
@ -188,10 +184,9 @@ def test_add_schedule(manage_responders_setup, make_schedule, make_on_call_shift
payload = make_slack_payload(schedule=schedule)
step = ManageRespondersScheduleChange(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
assert mock_slack_api_call.call_args.kwargs["view"]["blocks"][0]["accessory"]["value"] == str(user.pk)
@ -221,10 +216,9 @@ def test_add_schedule_alert_group_resolved(
payload = make_slack_payload(schedule=schedule)
step = ManageRespondersScheduleChange(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
assert (
DirectPagingAlertGroupResolvedError.DETAIL
in mock_slack_api_call.call_args.kwargs["view"]["blocks"][0]["text"]["text"]
@ -237,10 +231,9 @@ def test_remove_user(manage_responders_setup):
payload = make_slack_payload(actions=[{"value": user.pk}])
step = ManageRespondersRemoveUser(slack_team_identity, organization, user)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
# check there's no list of users in the view
assert mock_slack_api_call.call_args.kwargs["view"]["blocks"][0]["accessory"]["type"] != "button"

View file

@ -89,14 +89,13 @@ def make_slack_payload(
def test_initial_state(
make_organization_and_user_with_slack_identities,
):
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
_, _, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
payload = {"channel_id": "123", "trigger_id": "111"}
step = StartDirectPaging(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_open") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.open",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.USERS] == {}
assert metadata[DataKey.SCHEDULES] == {}
@ -138,10 +137,9 @@ def test_add_user_no_warning(
payload = make_slack_payload(organization=organization, user=user)
step = OnPagingUserChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.USERS] == {str(user.pk): Policy.DEFAULT}
@ -183,10 +181,9 @@ def test_add_user_maximum_exceeded(
step = OnPagingUserChange(slack_team_identity)
with patch("apps.slack.scenarios.paging.PRIVATE_METADATA_MAX_LENGTH", 100):
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
view_data = mock_slack_api_call.call_args.kwargs["view"]
metadata = json.loads(view_data["private_metadata"])
# metadata unchanged, ignoring the prefix
@ -210,10 +207,9 @@ def test_add_user_raise_warning(make_organization_and_user_with_slack_identities
payload = make_slack_payload(organization=organization, user=user)
step = OnPagingUserChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_push") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.push",)
assert mock_slack_api_call.call_args.kwargs["view"]["callback_id"] == "OnPagingConfirmUserChange"
text_from_blocks = "".join(
b["text"]["text"] for b in mock_slack_api_call.call_args.kwargs["view"]["blocks"] if b["type"] == "section"
@ -232,10 +228,9 @@ def test_change_user_policy(make_organization_and_user_with_slack_identities):
)
step = OnPagingItemActionChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.USERS] == {str(user.pk): Policy.IMPORTANT}
@ -249,10 +244,9 @@ def test_remove_user(make_organization_and_user_with_slack_identities):
)
step = OnPagingItemActionChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.USERS] == {}
@ -324,10 +318,9 @@ def test_add_schedule(make_organization_and_user_with_slack_identities, make_sch
)
step = OnPagingScheduleChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.SCHEDULES] == {str(schedule.pk): Policy.DEFAULT}
assert metadata[DataKey.USERS] == {str(user.pk): Policy.IMPORTANT}
@ -345,10 +338,9 @@ def test_add_schedule_responders_exceeded(make_organization_and_user_with_slack_
step = OnPagingScheduleChange(slack_team_identity)
with patch("apps.slack.scenarios.paging.PRIVATE_METADATA_MAX_LENGTH", 100):
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
view_data = mock_slack_api_call.call_args.kwargs["view"]
metadata = json.loads(view_data["private_metadata"])
# metadata unchanged, ignoring the prefix
@ -376,10 +368,9 @@ def test_change_schedule_policy(make_organization_and_user_with_slack_identities
)
step = OnPagingItemActionChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.SCHEDULES] == {str(schedule.pk): Policy.IMPORTANT}
assert metadata[DataKey.USERS] == {str(user.pk): Policy.DEFAULT}
@ -396,10 +387,9 @@ def test_remove_schedule(make_organization_and_user_with_slack_identities, make_
)
step = OnPagingItemActionChange(slack_team_identity)
with patch.object(step._slack_client, "api_call") as mock_slack_api_call:
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
step.process_scenario(slack_user_identity, slack_team_identity, payload)
assert mock_slack_api_call.call_args.args == ("views.update",)
metadata = json.loads(mock_slack_api_call.call_args.kwargs["view"]["private_metadata"])
assert metadata[DataKey.SCHEDULES] == {}
assert metadata[DataKey.USERS] == {str(user.pk): Policy.DEFAULT}

View file

@ -3,9 +3,8 @@ from unittest.mock import patch
import pytest
from apps.slack.client import SlackAPIException, SlackClientWithErrorHandling
from apps.slack.scenarios.scenario_step import ScenarioStep
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException
from common.api_helpers.utils import create_engine_url

View file

@ -146,13 +146,13 @@ class TestBaseShiftSwapRequestStep:
step = scenarios.BaseShiftSwapRequestStep(slack_team_identity, organization)
with patch.object(step, "_slack_client") as mock_slack_client:
mock_slack_client.api_call.return_value = {"ts": ts}
mock_slack_client.chat_postMessage.return_value = {"ts": ts}
slack_message = step.create_message(ssr)
mock_generate_blocks.assert_called_once_with(ssr)
mock_slack_client.api_call.assert_called_once_with(
"chat.postMessage", channel=ssr.slack_channel_id, blocks=mock_generate_blocks.return_value
mock_slack_client.chat_postMessage.assert_called_once_with(
channel=ssr.slack_channel_id, blocks=mock_generate_blocks.return_value
)
assert slack_message.slack_id == ts
@ -179,8 +179,8 @@ class TestBaseShiftSwapRequestStep:
step.update_message(ssr)
mock_generate_blocks.assert_called_once_with(ssr)
mock_slack_client.api_call.assert_called_once_with(
"chat.update", channel=ssr.slack_channel_id, ts=ts, blocks=mock_generate_blocks.return_value
mock_slack_client.chat_update.assert_called_once_with(
channel=ssr.slack_channel_id, ts=ts, blocks=mock_generate_blocks.return_value
)

View file

@ -1,4 +1,4 @@
from unittest.mock import Mock, call, patch
from unittest.mock import Mock, patch
import pytest
@ -237,7 +237,7 @@ class TestSlackChannelMessageEventStep:
step = SlackChannelMessageEventStep(slack_team_identity, organization, user)
step._slack_client = Mock()
step._slack_client.api_call.side_effect = [{"permalink": mock_permalink}, None]
step._slack_client.chat_getPermalink.return_value = {"permalink": mock_permalink}
payload = {
"event": {
@ -250,21 +250,15 @@ class TestSlackChannelMessageEventStep:
step.save_thread_message_for_resolution_note(slack_user_identity, payload)
step._slack_client.api_call.assert_has_calls(
[
call(
"chat.getPermalink",
channel=payload["event"]["channel"],
message_ts=payload["event"]["ts"],
),
call(
"chat.postEphemeral",
channel=payload["event"]["channel"],
user=slack_user_identity.slack_id,
text=":warning: Unable to show the <{}|message> in Resolution Note: the message is too long ({}). "
"Max length - 2900 symbols.".format(mock_permalink, len(payload["event"]["text"])),
),
]
step._slack_client.chat_getPermalink.assert_called_once_with(
channel=payload["event"]["channel"],
message_ts=payload["event"]["ts"],
)
step._slack_client.chat_postEphemeral.assert_called_once_with(
channel=payload["event"]["channel"],
user=slack_user_identity.slack_id,
text=":warning: Unable to show the <{}|message> in Resolution Note: the message is too long ({}). "
"Max length - 2900 symbols.".format(mock_permalink, len(payload["event"]["text"])),
)
MockResolutionNoteSlackMessage.objects.get_or_create.assert_not_called()
@ -306,7 +300,7 @@ class TestSlackChannelMessageEventStep:
step = SlackChannelMessageEventStep(slack_team_identity, organization, user)
step._slack_client = Mock()
step._slack_client.api_call.side_effect = [{"permalink": mock_permalink}, None]
step._slack_client.chat_getPermalink.side_effect = [{"permalink": mock_permalink}, None]
payload = {
"event": {
@ -319,14 +313,9 @@ class TestSlackChannelMessageEventStep:
step.save_thread_message_for_resolution_note(slack_user_identity, payload)
step._slack_client.api_call.assert_has_calls(
[
call(
"chat.getPermalink",
channel=payload["event"]["channel"],
message_ts=payload["event"]["ts"],
),
]
step._slack_client.chat_getPermalink.assert_called_once_with(
channel=payload["event"]["channel"],
message_ts=payload["event"]["ts"],
)
if resolution_note_slack_message_already_exists:

View file

@ -3,8 +3,8 @@ from unittest.mock import PropertyMock, patch
import pytest
from apps.schedules.models.on_call_schedule import OnCallScheduleQuerySet
from apps.slack.client import SlackClientWithErrorHandling
from apps.slack.models import SlackUserGroup
from apps.slack.slack_client import SlackClientWithErrorHandling
@pytest.mark.django_db

View file

@ -2,8 +2,7 @@ import enum
import typing
from datetime import datetime
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException
from apps.slack.client import SlackAPIException, SlackClientWithErrorHandling
if typing.TYPE_CHECKING:
from apps.user_management.models import Organization
@ -65,7 +64,7 @@ def post_message_to_channel(organization: "Organization", channel_id: str, text:
if organization.slack_team_identity:
slack_client = SlackClientWithErrorHandling(organization.slack_team_identity.bot_access_token)
try:
slack_client.api_call("chat.postMessage", channel=channel_id, text=text)
slack_client.chat_postMessage(channel=channel_id, text=text)
except SlackAPIException as e:
if e.response["error"] == "channel_not_found":
pass

View file

@ -15,6 +15,7 @@ from rest_framework.views import APIView
from apps.api.permissions import RBACPermission
from apps.auth_token.auth import PluginAuthentication
from apps.base.utils import live_settings
from apps.slack.client import SlackAPIException, SlackAPITokenException, SlackClientWithErrorHandling
from apps.slack.scenarios.alertgroup_appearance import STEPS_ROUTING as ALERTGROUP_APPEARANCE_ROUTING
# Importing routes from scenarios
@ -34,8 +35,6 @@ from apps.slack.scenarios.shift_swap_requests import STEPS_ROUTING as SHIFT_SWAP
from apps.slack.scenarios.slack_channel import STEPS_ROUTING as CHANNEL_ROUTING
from apps.slack.scenarios.slack_channel_integration import STEPS_ROUTING as SLACK_CHANNEL_INTEGRATION_ROUTING
from apps.slack.scenarios.slack_usergroup import STEPS_ROUTING as SLACK_USERGROUP_UPDATE_ROUTING
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.slack_client.exceptions import SlackAPIException, SlackAPITokenException
from apps.slack.tasks import clean_slack_integration_leftovers, unpopulate_slack_user_identities
from apps.slack.types import EventPayload, EventType, MessageEventSubtype, PayloadType, ScenarioRoute
from apps.user_management.models import Organization
@ -195,10 +194,7 @@ class SlackEventApiEndpointView(APIView):
if slack_team_identity.detected_token_revoked is not None:
# check if token is still invalid
try:
sc.api_call(
"auth.test",
team=slack_team_identity,
)
sc.auth_test(team=slack_team_identity)
except SlackAPITokenException:
logger.info(f"Team {slack_team_identity.slack_id} has revoked token, dropping request.")
return Response(status=200)
@ -223,7 +219,7 @@ class SlackEventApiEndpointView(APIView):
elif (
payload_event_bot_id and slack_team_identity and payload_event_channel_type == EventType.MESSAGE_CHANNEL
):
response = sc.api_call("bots.info", bot=payload_event_bot_id)
response = sc.bots_info(bot=payload_event_bot_id)
bot_user_id = response.get("bot", {}).get("user_id", "")
# Don't react on own bot's messages.
@ -537,11 +533,7 @@ class SlackEventApiEndpointView(APIView):
"text": "One more step!",
},
}
slack_client.api_call(
"views.open",
trigger_id=payload["trigger_id"],
view=view,
)
slack_client.views_open(trigger_id=payload["trigger_id"], view=view)
class ResetSlackView(APIView):

View file

@ -69,7 +69,7 @@ from apps.schedules.tests.factories import (
OnCallScheduleICalFactory,
ShiftSwapRequestFactory,
)
from apps.slack.slack_client import SlackClientWithErrorHandling
from apps.slack.client import SlackClientWithErrorHandling
from apps.slack.tests.factories import (
SlackChannelFactory,
SlackMessageFactory,

View file

@ -1,6 +1,6 @@
django==3.2.20
djangorestframework==3.12.4
slackclient==1.3.0
slack_sdk==3.21.3
whitenoise==5.3.0
twilio~=6.37.0
phonenumbers==8.10.0