From 514e3b84becd8c014c7e2be35dabe84e1a148fd0 Mon Sep 17 00:00:00 2001 From: Moasib-Arif Date: Mon, 22 Jan 2024 13:07:09 +0000 Subject: [PATCH 1/3] Salck Notification Working --- dpytools/slack/slack.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/dpytools/slack/slack.py b/dpytools/slack/slack.py index dc826c5..ab56432 100644 --- a/dpytools/slack/slack.py +++ b/dpytools/slack/slack.py @@ -1,14 +1,25 @@ +import os +import logging +from dpytools.http_clients.base import BaseHttpClient class SlackNotifier: def __init__(self): - # Set a webhok via an env var, ask Mike for a - #web hook url. - ... - - def notify(self, msg: str): - # Check formatting options for messages to slack. - # From memory you style it via sending a dictionary. - # It's a post request so do use the http client - # we've developing elsewhere in this library. - ... \ No newline at end of file + self.webhook_url = os.getenv("SLACK_WEBHOOK_URL") + if not self.webhook_url: + raise ValueError('SLACK_WEBHOOK_URL is not set') + self.http_client = BaseHttpClient() + self.validate_webhook_url() + + def validate_webhook_url(self): + response = self.http_client.get(self.webhook_url) + if response.status_code != 200: + logging.error(f'Invalid SLACK_WEBHOOK_URL: {response.status_code}') + raise ValueError('Invalid SLACK_WEBHOOK_URL') + + def notify(self, msg: dict): + try: + response = self.http_client.post(self.webhook_url, json=msg) + response.raise_for_status() + except Exception as e: + logging.error(f'Failed to send notification: {e}') From 2813ec6f1c0f3b2a04a5c75963e8efae9913b3d2 Mon Sep 17 00:00:00 2001 From: Moasib-Arif Date: Mon, 22 Jan 2024 13:07:41 +0000 Subject: [PATCH 2/3] Added tests --- tests/test_slack.py | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_slack.py diff --git a/tests/test_slack.py b/tests/test_slack.py new file mode 100644 index 0000000..376e2bc --- /dev/null +++ b/tests/test_slack.py @@ -0,0 +1,51 @@ +import pytest +from unittest.mock import patch, MagicMock +from requests import HTTPError, Response +from dpytools.http_clients.base import BaseHttpClient +from dpytools.slack.slack import SlackNotifier + +@patch('os.getenv') +@patch.object(BaseHttpClient, 'get') +def test_validate_webhook_url(mock_get, mock_getenv): + """ + Test that the validate_webhook_url method raises an exception for invalid URLs + """ + mock_getenv.return_value = 'http://example.com' + mock_response = MagicMock(Response) + mock_response.status_code = 404 + mock_get.return_value = mock_response + + with pytest.raises(ValueError): + notifier = SlackNotifier() + +@patch('os.getenv') +@patch.object(BaseHttpClient, 'get') +def test_validate_webhook_url_success(mock_get, mock_getenv): + """ + Test that the validate_webhook_url method does not raise an exception for valid URLs + """ + mock_getenv.return_value = 'http://example.com' + mock_response = MagicMock(Response) + mock_response.status_code = 200 + mock_get.return_value = mock_response + + try: + notifier = SlackNotifier() + except ValueError: + pytest.fail("Unexpected ValueError ..") + +@patch('os.getenv') +@patch.object(BaseHttpClient, 'post') +def test_notify(mock_post, mock_getenv): + """ + Test that the notify method sends a POST request + """ + mock_getenv.return_value = 'http://example.com' + mock_response = MagicMock(Response) + mock_response.status_code = 200 + mock_post.return_value = mock_response + + notifier = SlackNotifier() + notifier.notify({'text': 'Test message'}) + + mock_post.assert_called_once_with('http://example.com', json={'text': 'Test message'}) \ No newline at end of file From 5c8fe551fca3226e9f3f59755fcd92296daa9914 Mon Sep 17 00:00:00 2001 From: Moasib-Arif Date: Tue, 23 Jan 2024 12:44:24 +0000 Subject: [PATCH 3/3] Made the requested changes --- dpytools/slack/slack.py | 32 ++++++++++++++++------------ tests/test_slack.py | 46 ++++++++++++++--------------------------- 2 files changed, 34 insertions(+), 44 deletions(-) diff --git a/dpytools/slack/slack.py b/dpytools/slack/slack.py index ab56432..13eb617 100644 --- a/dpytools/slack/slack.py +++ b/dpytools/slack/slack.py @@ -1,25 +1,31 @@ -import os import logging from dpytools.http_clients.base import BaseHttpClient class SlackNotifier: - def __init__(self): - self.webhook_url = os.getenv("SLACK_WEBHOOK_URL") - if not self.webhook_url: - raise ValueError('SLACK_WEBHOOK_URL is not set') + def __init__(self, webhook_url): + if not webhook_url: + raise ValueError('webhook_url is not set') + self.webhook_url = webhook_url self.http_client = BaseHttpClient() - self.validate_webhook_url() - def validate_webhook_url(self): - response = self.http_client.get(self.webhook_url) - if response.status_code != 200: - logging.error(f'Invalid SLACK_WEBHOOK_URL: {response.status_code}') - raise ValueError('Invalid SLACK_WEBHOOK_URL') + def notify(self, msg_dict: dict): + """ + Send a message to the Slack webhook. - def notify(self, msg: dict): + The msg_dict parameter should be a dictionary that matches the + structure documented at https://api.slack.com/messaging/webhooks + """ try: - response = self.http_client.post(self.webhook_url, json=msg) + response = self.http_client.post(self.webhook_url, json=msg_dict) response.raise_for_status() except Exception as e: logging.error(f'Failed to send notification: {e}') + + def msg_str(self, msg: str): + """ + Send a string message to the Slack webhook. + + The msg parameter is wrapped into a dictionary before being sent. + """ + self.notify({'text': msg}) \ No newline at end of file diff --git a/tests/test_slack.py b/tests/test_slack.py index 376e2bc..32b85db 100644 --- a/tests/test_slack.py +++ b/tests/test_slack.py @@ -4,48 +4,32 @@ from dpytools.http_clients.base import BaseHttpClient from dpytools.slack.slack import SlackNotifier -@patch('os.getenv') -@patch.object(BaseHttpClient, 'get') -def test_validate_webhook_url(mock_get, mock_getenv): - """ - Test that the validate_webhook_url method raises an exception for invalid URLs - """ - mock_getenv.return_value = 'http://example.com' - mock_response = MagicMock(Response) - mock_response.status_code = 404 - mock_get.return_value = mock_response - - with pytest.raises(ValueError): - notifier = SlackNotifier() - -@patch('os.getenv') -@patch.object(BaseHttpClient, 'get') -def test_validate_webhook_url_success(mock_get, mock_getenv): +@patch.object(BaseHttpClient, 'post') +def test_notify(mock_post): """ - Test that the validate_webhook_url method does not raise an exception for valid URLs + Test that the notify method sends a POST request """ - mock_getenv.return_value = 'http://example.com' + webhook_url = 'http://example.com' mock_response = MagicMock(Response) mock_response.status_code = 200 - mock_get.return_value = mock_response + mock_post.return_value = mock_response + + notifier = SlackNotifier(webhook_url) + notifier.notify({'text': 'Test message'}) - try: - notifier = SlackNotifier() - except ValueError: - pytest.fail("Unexpected ValueError ..") + mock_post.assert_called_once_with(webhook_url, json={'text': 'Test message'}) -@patch('os.getenv') @patch.object(BaseHttpClient, 'post') -def test_notify(mock_post, mock_getenv): +def test_msg_str(mock_post): """ - Test that the notify method sends a POST request + Test that the msg_str method sends a POST request with a string message """ - mock_getenv.return_value = 'http://example.com' + webhook_url = 'http://example.com' mock_response = MagicMock(Response) mock_response.status_code = 200 mock_post.return_value = mock_response - notifier = SlackNotifier() - notifier.notify({'text': 'Test message'}) + notifier = SlackNotifier(webhook_url) + notifier.msg_str('Test message') - mock_post.assert_called_once_with('http://example.com', json={'text': 'Test message'}) \ No newline at end of file + mock_post.assert_called_once_with(webhook_url, json={'text': 'Test message'}) \ No newline at end of file