diff --git a/awx/locale/en-us/LC_MESSAGES/django.po b/awx/locale/en-us/LC_MESSAGES/django.po index b6da611f6a..97dd6807a8 100644 --- a/awx/locale/en-us/LC_MESSAGES/django.po +++ b/awx/locale/en-us/LC_MESSAGES/django.po @@ -4856,7 +4856,7 @@ msgid "Exception connecting to PagerDuty: {}" msgstr "" #: awx/main/notifications/pagerduty_backend.py:87 -#: awx/main/notifications/slack_backend.py:48 +#: awx/main/notifications/slack_backend.py:49 #: awx/main/notifications/twilio_backend.py:47 msgid "Exception sending messages: {}" msgstr "" diff --git a/awx/main/notifications/slack_backend.py b/awx/main/notifications/slack_backend.py index 881abced70..20b7f72f14 100644 --- a/awx/main/notifications/slack_backend.py +++ b/awx/main/notifications/slack_backend.py @@ -2,7 +2,8 @@ # All Rights Reserved. import logging -from slackclient import SlackClient +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError from django.utils.encoding import smart_text from django.utils.translation import ugettext_lazy as _ @@ -28,7 +29,7 @@ class SlackBackend(AWXBaseEmailBackend, CustomNotificationBase): self.color = hex_color def send_messages(self, messages): - connection = SlackClient(self.token) + client = WebClient(self.token) sent_messages = 0 for m in messages: try: @@ -36,15 +37,15 @@ class SlackBackend(AWXBaseEmailBackend, CustomNotificationBase): if r.startswith('#'): r = r[1:] if self.color: - ret = connection.api_call("chat.postMessage", channel=r, as_user=True, attachments=[{"color": self.color, "text": m.subject}]) + response = client.chat_postMessage(channel=r, as_user=True, attachments=[{"color": self.color, "text": m.subject}]) else: - ret = connection.api_call("chat.postMessage", channel=r, as_user=True, text=m.subject) - logger.debug(ret) - if ret['ok']: + response = client.chat_postMessage(channel=r, as_user=True, text=m.subject) + logger.debug(response) + if response['ok']: sent_messages += 1 else: - raise RuntimeError("Slack Notification unable to send {}: {} ({})".format(r, m.subject, ret['error'])) - except Exception as e: + raise RuntimeError("Slack Notification unable to send {}: {} ({})".format(r, m.subject, response['error'])) + except SlackApiError as e: logger.error(smart_text(_("Exception sending messages: {}").format(e))) if not self.fail_silently: raise diff --git a/awx/main/tests/unit/notifications/test_slack.py b/awx/main/tests/unit/notifications/test_slack.py new file mode 100644 index 0000000000..e4bf3d66ac --- /dev/null +++ b/awx/main/tests/unit/notifications/test_slack.py @@ -0,0 +1,73 @@ +import pytest + +from unittest import mock +from django.core.mail.message import EmailMessage + +import awx.main.notifications.slack_backend as slack_backend + + +def test_send_messages(): + with mock.patch('awx.main.notifications.slack_backend.WebClient') as slack_sdk_mock: + WebClient_mock = slack_sdk_mock.return_value + WebClient_mock.chat_postMessage.return_value = {'ok': True} + backend = slack_backend.SlackBackend('slack_access_token') + message = EmailMessage( + 'test subject', + 'test body', + [], + [ + '#random', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + WebClient_mock.chat_postMessage.assert_called_once_with(channel='random', as_user=True, text='test subject') + assert sent_messages == 1 + + +def test_send_messages_with_color(): + with mock.patch('awx.main.notifications.slack_backend.WebClient') as slack_sdk_mock: + WebClient_mock = slack_sdk_mock.return_value + WebClient_mock.chat_postMessage.return_value = {'ok': True} + backend = slack_backend.SlackBackend('slack_access_token', hex_color='#006699') + message = EmailMessage( + 'test subject', + 'test body', + [], + [ + '#random', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + + WebClient_mock.chat_postMessage.assert_called_once_with(channel='random', as_user=True, attachments=[{'color': '#006699', 'text': 'test subject'}]) + assert sent_messages == 1 + + +def test_send_messages_fail(): + with mock.patch('awx.main.notifications.slack_backend.WebClient') as slack_sdk_mock, pytest.raises(RuntimeError, match=r'.*not_in_channel.*'): + WebClient_mock = slack_sdk_mock.return_value + WebClient_mock.chat_postMessage.return_value = {'ok': False, 'error': 'not_in_channel'} + backend = slack_backend.SlackBackend('slack_access_token') + message = EmailMessage( + 'test subject', + 'test body', + [], + [ + '#not_existing', + ], + ) + sent_messages = backend.send_messages( + [ + message, + ] + ) + WebClient_mock.chat_postMessage.assert_called_once_with(channel='not_existing', as_user=True, text='test subject') + assert sent_messages == 0 diff --git a/docs/licenses/slackclient.txt b/docs/licenses/slack-sdk.txt similarity index 94% rename from docs/licenses/slackclient.txt rename to docs/licenses/slack-sdk.txt index 73da6e9751..c4944b4f8e 100644 --- a/docs/licenses/slackclient.txt +++ b/docs/licenses/slack-sdk.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015-2016 Slack Technologies, Inc +Copyright (c) 2015- Slack Technologies, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +THE SOFTWARE. \ No newline at end of file diff --git a/requirements/README.md b/requirements/README.md index b026c20aa7..69ab2d4bcf 100644 --- a/requirements/README.md +++ b/requirements/README.md @@ -108,12 +108,6 @@ Upgrading to 4.0.0 causes error because imports changed. ImportError: cannot import name 'KeyVaultClient' ``` -### slackclient - -Imports as used in `awx/main/notifications/slack_backend.py` changed -in version 2.0. This plugin code will need to change and be re-tested -as the upgrade takes place. - ### django-jsonfield Instead of calling a `loads()` operation, the returned value is casted into diff --git a/requirements/requirements.in b/requirements/requirements.in index 6104cb3a59..79e76250a7 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -50,7 +50,7 @@ social-auth-core==3.3.1 # see UPGRADE BLOCKERs social-auth-app-django==3.1.0 # see UPGRADE BLOCKERs redis requests -slackclient==1.1.2 # see UPGRADE BLOCKERs +slack-sdk tacacs_plus==1.0 # UPGRADE BLOCKER: auth does not work with later versions twilio twisted[tls]>=20.3.0 # CVE-2020-10108, CVE-2020-10109 diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 304b73f8a5..63b945ec2f 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -320,7 +320,6 @@ requests==2.23.0 # python-dsv-sdk # python-tss-sdk # requests-oauthlib - # slackclient # social-auth-core # twilio requests-oauthlib==1.3.0 @@ -358,13 +357,12 @@ six==1.14.0 # pyrad # pyrsistent # python-dateutil - # slackclient # social-auth-app-django # social-auth-core # tacacs-plus # twilio # websocket-client -slackclient==1.1.2 +slack-sdk==3.11.2 # via -r /awx_devel/requirements/requirements.in smmap==3.0.1 # via gitdb @@ -399,9 +397,7 @@ uwsgi==2.0.18 uwsgitop==0.11 # via -r /awx_devel/requirements/requirements.in websocket-client==0.57.0 - # via - # kubernetes - # slackclient + # via kubernetes wheel==0.36.2 # via -r /awx_devel/requirements/requirements.in xmlsec==1.3.3