parent
4d74be6b9f
commit
a4688c4442
|
@ -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
|
||||||
```
|
```
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
flask
|
flask
|
||||||
flask-classful
|
flask-classful
|
||||||
pyyaml
|
pyyaml
|
||||||
|
requests
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in a new issue