From a4688c444284420037ca809df485f570354be1e0 Mon Sep 17 00:00:00 2001 From: Scott Wallace Date: Wed, 26 May 2021 21:52:46 +0100 Subject: [PATCH] Move to `requests` to handle TLS connections Fixes #15 --- README.md | 8 ++-- example.yaml | 3 +- requirements.txt | 1 + src/Alertify/__init__.py | 3 +- src/Alertify/config.py | 3 +- src/Alertify/gotify.py | 28 +++++++----- src/tests/Alertify/test_config.py | 3 +- src/tests/Alertify/test_gotify.py | 66 +++++++++++++++++----------- src/tests/Alertify/test_health.py | 2 +- src/tests/Alertify/test_messaging.py | 4 +- 10 files changed, 68 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index c091c49..605e1a7 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ The following environment variables will override any config or default: * DISABLE_RESOLVED (default: False) * GOTIFY_KEY_APP (default: None) * GOTIFY_KEY_CLIENT (default: None) - * GOTIFY_PORT (default: 80) - * GOTIFY_SERVER (default: localhost) + * GOTIFY_URL_PREFIX (default: http://localhost) * LISTEN_PORT (default: 8080) * VERBOSE (default: 0) ``` @@ -48,7 +47,7 @@ docker build . -t 'alertify:latest' e.g. ```bash -docker run --name alertify -p 8080:8080 -e TZ=Europe/London -e GOTIFY_KEY_APP=_APPKEY_ -e GOTIFY_SERVER=gotify -e GOTIFY_PORT=80 alertify:latest +docker run --name alertify -p 8080:8080 -e TZ=Europe/London -e GOTIFY_KEY_APP=_APPKEY_ -e GOTIFY_URL_PREFIX=http://gotify alertify:latest ``` ## Compose: @@ -76,7 +75,6 @@ services: - DELETE_ONRESOLVE=true - GOTIFY_KEY_APP=_APPKEY_ - GOTIFY_KEY_CLIENT=_CLIENTKEY_ - - GOTIFY_SERVER=gotify - - GOTIFY_PORT=80 + - GOTIFY_URL_PREFIX=http://gotify restart: unless-stopped ``` diff --git a/example.yaml b/example.yaml index b406df7..9075a47 100644 --- a/example.yaml +++ b/example.yaml @@ -1,7 +1,6 @@ --- disable_resolved: false gotify_key: sOmEsEcReTkEy1 -gotify_port: "80" -gotify_server: gotifyserver.example.net +gotify_url_prefix: http://gotifyserver.example.net listen_port: "8080" verbose: true diff --git a/requirements.txt b/requirements.txt index c10a6fc..32bfe43 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ flask flask-classful pyyaml +requests diff --git a/src/Alertify/__init__.py b/src/Alertify/__init__.py index 1404da3..a648f27 100644 --- a/src/Alertify/__init__.py +++ b/src/Alertify/__init__.py @@ -50,8 +50,7 @@ class Alertify(FlaskView): self.config = Config(configfile) self.gotify = Gotify( - self.config.gotify_server, - self.config.gotify_port, + self.config.gotify_url_prefix, self.config.gotify_key_app, self.config.gotify_key_client, ) diff --git a/src/Alertify/config.py b/src/Alertify/config.py index e5269da..8af303b 100644 --- a/src/Alertify/config.py +++ b/src/Alertify/config.py @@ -19,8 +19,7 @@ class Config: disable_resolved = bool(False) gotify_key_app = str() gotify_key_client = str() - gotify_port = int(80) - gotify_server = str('localhost') + gotify_url_prefix = str('http://localhost') listen_port = int(8080) verbose = int(0) diff --git a/src/Alertify/gotify.py b/src/Alertify/gotify.py index f1aac5b..9f98d76 100644 --- a/src/Alertify/gotify.py +++ b/src/Alertify/gotify.py @@ -1,20 +1,21 @@ """ Module to handle communication with the Gotify server """ -import http.client import json import logging import socket from typing import Optional +from requests import request + class Gotify: """ Class to handle Gotify communications """ - def __init__(self, server: str, port: int, app_key: str, client_key: Optional[str] = None): - self.api = http.client.HTTPConnection(server, port) + def __init__(self, url_prefix: str, app_key: str, client_key: Optional[str] = None): + self.url_prefix = url_prefix self.app_key = app_key self.client_key = client_key self.base_headers = { @@ -22,7 +23,7 @@ class Gotify: 'Accept': 'application/json', } - def _call(self, method: str, url: str, body: Optional[str] = None) -> dict: + def _call(self, method: str, url: str, data: Optional[object] = None) -> dict: """ Method to call Gotify with an app or client key as appropriate """ @@ -32,11 +33,15 @@ class Gotify: else: headers['X-Gotify-Key'] = self.app_key - logging.debug('Sending to Gotify:\n%s', body) + logging.debug('Sending to Gotify:\n%s', data) try: - self.api.request(method, url, body=body, headers=headers) - response = self.api.getresponse() + response = request( + method, + f'{self.url_prefix}{url}', + json=data, + headers=headers, + ) except (ConnectionRefusedError, socket.gaierror) as error: logging.error('Connection error: %s', error) return { @@ -45,14 +50,13 @@ class Gotify: } resp_obj = { - 'status': response.status, + 'status': response.status_code, 'reason': response.reason, 'json': None, } - rawbody = response.read() - if len(rawbody) > 0: + if len(response.content) > 0: try: - resp_obj['json'] = json.loads(rawbody.decode()) + resp_obj['json'] = response.json except json.decoder.JSONDecodeError as error: logging.error('Could not parse JSON: %s', error) @@ -109,7 +113,7 @@ class Gotify: Method to send a message payload to a Gotify server """ logging.debug('Sending message to Gotify') - return self._call('POST', '/message', body=json.dumps(payload, indent=2)) + return self._call('POST', '/message', data=payload) def healthcheck(self) -> dict: """ diff --git a/src/tests/Alertify/test_config.py b/src/tests/Alertify/test_config.py index 297b7ae..13bc547 100644 --- a/src/tests/Alertify/test_config.py +++ b/src/tests/Alertify/test_config.py @@ -16,8 +16,7 @@ class ConfigTest(unittest.TestCase): 'disable_resolved': bool(False), 'gotify_key_app': str(), 'gotify_key_client': str(), - 'gotify_port': int(80), - 'gotify_server': str('localhost'), + 'gotify_url_prefix': str('http://localhost'), 'listen_port': int(8080), 'verbose': int(0), } diff --git a/src/tests/Alertify/test_gotify.py b/src/tests/Alertify/test_gotify.py index 27be280..5d4f8d6 100644 --- a/src/tests/Alertify/test_gotify.py +++ b/src/tests/Alertify/test_gotify.py @@ -14,7 +14,7 @@ class GotifyTest(unittest.TestCase): @classmethod def setUpClass(cls): - cls.gotify_client = gotify.Gotify('', 0, '', '') + cls.gotify_client = gotify.Gotify('http://localhost', '') @classmethod def tearDownClass(cls): @@ -26,15 +26,12 @@ class GotifyTest(unittest.TestCase): def tearDown(self): pass - @patch('http.client.HTTPConnection.request') - @patch('http.client.HTTPConnection.getresponse') - @patch('http.client.HTTPResponse.read') - def test_delete(self, mock_request, mock_getresponse, mock_read): + @patch('requests.Session.request') + def test_delete(self, mock_request): """Test""" - mock_request.return_value.status = {} - mock_getresponse.return_value.status = 200 - mock_getresponse.return_value.reason = 'OK' - mock_read.return_value = {} + mock_request.return_value.status_code = 200 + mock_request.return_value.reason = 'OK' + mock_request.return_value.content = '' self.assertDictEqual( self.gotify_client.delete('123'), @@ -67,18 +64,15 @@ class GotifyTest(unittest.TestCase): dict(), ) - @patch('http.client.HTTPConnection.request') - @patch('http.client.HTTPConnection.getresponse') - @patch('http.client.HTTPResponse.read') - def test_send_alert(self, mock_request, mock_getresponse, mock_read): + @patch('requests.Session.request') + def test_send_alert_empty(self, mock_request): """Test""" - mock_request.return_value.status = {} - mock_getresponse.return_value.status = 200 - mock_getresponse.return_value.reason = 'OK' - mock_read.return_value = {} + mock_request.return_value.status_code = 200 + mock_request.return_value.reason = 'OK' + mock_request.return_value.content = '' self.assertDictEqual( - self.gotify_client.send_alert({}), + self.gotify_client.send_alert(dict()), { 'status': 200, 'reason': 'OK', @@ -86,15 +80,35 @@ class GotifyTest(unittest.TestCase): }, ) - @patch('http.client.HTTPConnection.request') - @patch('http.client.HTTPConnection.getresponse') - @patch('http.client.HTTPResponse.read') - def test_healthcheck(self, mock_request, mock_getresponse, mock_read): + @patch('requests.Session.request') + def test_send_alert_dummy(self, mock_request): """Test""" - mock_request.return_value.status = {} - mock_getresponse.return_value.status = 200 - mock_getresponse.return_value.reason = 'OK' - mock_read.return_value = {} + mock_request.return_value.status_code = 200 + mock_request.return_value.reason = 'OK' + mock_request.return_value.content = '' + + self.assertDictEqual( + self.gotify_client.send_alert( + { + 'title': 'TITLE', + 'message': 'MESSAGE', + 'priority': 0, + 'extras': dict(), + } + ), + { + 'status': 200, + 'reason': 'OK', + 'json': None, + }, + ) + + @patch('requests.Session.request') + def test_healthcheck(self, mock_request): + """Test""" + mock_request.return_value.status_code = 200 + mock_request.return_value.reason = 'OK' + mock_request.return_value.content = '' self.assertDictEqual( self.gotify_client.healthcheck(), diff --git a/src/tests/Alertify/test_health.py b/src/tests/Alertify/test_health.py index 13424ab..573107b 100644 --- a/src/tests/Alertify/test_health.py +++ b/src/tests/Alertify/test_health.py @@ -12,7 +12,7 @@ class HealthcheckTest(unittest.TestCase): @classmethod def setUpClass(cls): - cls.healthcheck = health.Healthcheck(gotify.Gotify('', 0, '', '')) + cls.healthcheck = health.Healthcheck(gotify.Gotify('http://localhost', '', '')) @classmethod def tearDownClass(cls): diff --git a/src/tests/Alertify/test_messaging.py b/src/tests/Alertify/test_messaging.py index 4a14248..7c424bb 100644 --- a/src/tests/Alertify/test_messaging.py +++ b/src/tests/Alertify/test_messaging.py @@ -12,7 +12,9 @@ class MessageHandlerTest(unittest.TestCase): @classmethod def setUpClass(cls): - cls.messaging = messaging.MessageHandler(gotify.Gotify('', 0, '', '')) + cls.messaging = messaging.MessageHandler( + gotify.Gotify('http://localhost', '', '') + ) @classmethod def tearDownClass(cls):