Move to requests to handle TLS connections

Fixes #15
This commit is contained in:
Scott Wallace 2021-05-26 21:52:46 +01:00
parent 4d74be6b9f
commit a4688c4442
10 changed files with 68 additions and 53 deletions

View file

@ -17,8 +17,7 @@ The following environment variables will override any config or default:
* DISABLE_RESOLVED (default: False) * DISABLE_RESOLVED (default: False)
* GOTIFY_KEY_APP (default: None) * GOTIFY_KEY_APP (default: None)
* GOTIFY_KEY_CLIENT (default: None) * GOTIFY_KEY_CLIENT (default: None)
* GOTIFY_PORT (default: 80) * GOTIFY_URL_PREFIX (default: http://localhost)
* GOTIFY_SERVER (default: localhost)
* LISTEN_PORT (default: 8080) * LISTEN_PORT (default: 8080)
* VERBOSE (default: 0) * VERBOSE (default: 0)
``` ```
@ -48,7 +47,7 @@ docker build . -t 'alertify:latest'
e.g. e.g.
```bash ```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: ## Compose:
@ -76,7 +75,6 @@ services:
- DELETE_ONRESOLVE=true - DELETE_ONRESOLVE=true
- GOTIFY_KEY_APP=_APPKEY_ - GOTIFY_KEY_APP=_APPKEY_
- GOTIFY_KEY_CLIENT=_CLIENTKEY_ - GOTIFY_KEY_CLIENT=_CLIENTKEY_
- GOTIFY_SERVER=gotify - GOTIFY_URL_PREFIX=http://gotify
- GOTIFY_PORT=80
restart: unless-stopped restart: unless-stopped
``` ```

View file

@ -1,7 +1,6 @@
--- ---
disable_resolved: false disable_resolved: false
gotify_key: sOmEsEcReTkEy1 gotify_key: sOmEsEcReTkEy1
gotify_port: "80" gotify_url_prefix: http://gotifyserver.example.net
gotify_server: gotifyserver.example.net
listen_port: "8080" listen_port: "8080"
verbose: true verbose: true

View file

@ -1,3 +1,4 @@
flask flask
flask-classful flask-classful
pyyaml pyyaml
requests

View file

@ -50,8 +50,7 @@ class Alertify(FlaskView):
self.config = Config(configfile) self.config = Config(configfile)
self.gotify = Gotify( self.gotify = Gotify(
self.config.gotify_server, self.config.gotify_url_prefix,
self.config.gotify_port,
self.config.gotify_key_app, self.config.gotify_key_app,
self.config.gotify_key_client, self.config.gotify_key_client,
) )

View file

@ -19,8 +19,7 @@ class Config:
disable_resolved = bool(False) disable_resolved = bool(False)
gotify_key_app = str() gotify_key_app = str()
gotify_key_client = str() gotify_key_client = str()
gotify_port = int(80) gotify_url_prefix = str('http://localhost')
gotify_server = str('localhost')
listen_port = int(8080) listen_port = int(8080)
verbose = int(0) verbose = int(0)

View file

@ -1,20 +1,21 @@
""" """
Module to handle communication with the Gotify server Module to handle communication with the Gotify server
""" """
import http.client
import json import json
import logging import logging
import socket import socket
from typing import Optional from typing import Optional
from requests import request
class Gotify: class Gotify:
""" """
Class to handle Gotify communications Class to handle Gotify communications
""" """
def __init__(self, server: str, port: int, app_key: str, client_key: Optional[str] = None): def __init__(self, url_prefix: str, app_key: str, client_key: Optional[str] = None):
self.api = http.client.HTTPConnection(server, port) self.url_prefix = url_prefix
self.app_key = app_key self.app_key = app_key
self.client_key = client_key self.client_key = client_key
self.base_headers = { self.base_headers = {
@ -22,7 +23,7 @@ class Gotify:
'Accept': 'application/json', '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 Method to call Gotify with an app or client key as appropriate
""" """
@ -32,11 +33,15 @@ class Gotify:
else: else:
headers['X-Gotify-Key'] = self.app_key headers['X-Gotify-Key'] = self.app_key
logging.debug('Sending to Gotify:\n%s', body) logging.debug('Sending to Gotify:\n%s', data)
try: try:
self.api.request(method, url, body=body, headers=headers) response = request(
response = self.api.getresponse() method,
f'{self.url_prefix}{url}',
json=data,
headers=headers,
)
except (ConnectionRefusedError, socket.gaierror) as error: except (ConnectionRefusedError, socket.gaierror) as error:
logging.error('Connection error: %s', error) logging.error('Connection error: %s', error)
return { return {
@ -45,14 +50,13 @@ class Gotify:
} }
resp_obj = { resp_obj = {
'status': response.status, 'status': response.status_code,
'reason': response.reason, 'reason': response.reason,
'json': None, 'json': None,
} }
rawbody = response.read() if len(response.content) > 0:
if len(rawbody) > 0:
try: try:
resp_obj['json'] = json.loads(rawbody.decode()) resp_obj['json'] = response.json
except json.decoder.JSONDecodeError as error: except json.decoder.JSONDecodeError as error:
logging.error('Could not parse JSON: %s', 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 Method to send a message payload to a Gotify server
""" """
logging.debug('Sending message to Gotify') 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: def healthcheck(self) -> dict:
""" """

View file

@ -16,8 +16,7 @@ class ConfigTest(unittest.TestCase):
'disable_resolved': bool(False), 'disable_resolved': bool(False),
'gotify_key_app': str(), 'gotify_key_app': str(),
'gotify_key_client': str(), 'gotify_key_client': str(),
'gotify_port': int(80), 'gotify_url_prefix': str('http://localhost'),
'gotify_server': str('localhost'),
'listen_port': int(8080), 'listen_port': int(8080),
'verbose': int(0), 'verbose': int(0),
} }

View file

@ -14,7 +14,7 @@ class GotifyTest(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.gotify_client = gotify.Gotify('', 0, '', '') cls.gotify_client = gotify.Gotify('http://localhost', '')
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -26,15 +26,12 @@ class GotifyTest(unittest.TestCase):
def tearDown(self): def tearDown(self):
pass pass
@patch('http.client.HTTPConnection.request') @patch('requests.Session.request')
@patch('http.client.HTTPConnection.getresponse') def test_delete(self, mock_request):
@patch('http.client.HTTPResponse.read')
def test_delete(self, mock_request, mock_getresponse, mock_read):
"""Test""" """Test"""
mock_request.return_value.status = {} mock_request.return_value.status_code = 200
mock_getresponse.return_value.status = 200 mock_request.return_value.reason = 'OK'
mock_getresponse.return_value.reason = 'OK' mock_request.return_value.content = ''
mock_read.return_value = {}
self.assertDictEqual( self.assertDictEqual(
self.gotify_client.delete('123'), self.gotify_client.delete('123'),
@ -67,18 +64,15 @@ class GotifyTest(unittest.TestCase):
dict(), dict(),
) )
@patch('http.client.HTTPConnection.request') @patch('requests.Session.request')
@patch('http.client.HTTPConnection.getresponse') def test_send_alert_empty(self, mock_request):
@patch('http.client.HTTPResponse.read')
def test_send_alert(self, mock_request, mock_getresponse, mock_read):
"""Test""" """Test"""
mock_request.return_value.status = {} mock_request.return_value.status_code = 200
mock_getresponse.return_value.status = 200 mock_request.return_value.reason = 'OK'
mock_getresponse.return_value.reason = 'OK' mock_request.return_value.content = ''
mock_read.return_value = {}
self.assertDictEqual( self.assertDictEqual(
self.gotify_client.send_alert({}), self.gotify_client.send_alert(dict()),
{ {
'status': 200, 'status': 200,
'reason': 'OK', 'reason': 'OK',
@ -86,15 +80,35 @@ class GotifyTest(unittest.TestCase):
}, },
) )
@patch('http.client.HTTPConnection.request') @patch('requests.Session.request')
@patch('http.client.HTTPConnection.getresponse') def test_send_alert_dummy(self, mock_request):
@patch('http.client.HTTPResponse.read')
def test_healthcheck(self, mock_request, mock_getresponse, mock_read):
"""Test""" """Test"""
mock_request.return_value.status = {} mock_request.return_value.status_code = 200
mock_getresponse.return_value.status = 200 mock_request.return_value.reason = 'OK'
mock_getresponse.return_value.reason = 'OK' mock_request.return_value.content = ''
mock_read.return_value = {}
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.assertDictEqual(
self.gotify_client.healthcheck(), self.gotify_client.healthcheck(),

View file

@ -12,7 +12,7 @@ class HealthcheckTest(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.healthcheck = health.Healthcheck(gotify.Gotify('', 0, '', '')) cls.healthcheck = health.Healthcheck(gotify.Gotify('http://localhost', '', ''))
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):

View file

@ -12,7 +12,9 @@ class MessageHandlerTest(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
cls.messaging = messaging.MessageHandler(gotify.Gotify('', 0, '', '')) cls.messaging = messaging.MessageHandler(
gotify.Gotify('http://localhost', '', '')
)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):