From 5849700dad4706215ea62fed249cda959d3ff335 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Tue, 24 May 2022 21:23:49 +0200 Subject: [PATCH 1/9] modified version to upload to pypi --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 920b2ae..8e3fee7 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="gotify_message", - version="0.1.5", + version="0.1.0", author="Przemek Bubasr", author_email="bubasenator@gmail.com", description="Python module to push messages to gotify server", From 7a7a107eaaa3dcfbb20d900a595444cb22c8b704 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 1 Jun 2022 14:50:55 +0200 Subject: [PATCH 2/9] added unittest --- tests/test_notification.py | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/test_notification.py diff --git a/tests/test_notification.py b/tests/test_notification.py new file mode 100644 index 0000000..1db4bb8 --- /dev/null +++ b/tests/test_notification.py @@ -0,0 +1,52 @@ +from unittest import TestCase, mock +from gotify_message import GotifyNotification +from gotify_message.gotify_notification import requests + + +class TestGotifyNotification(TestCase): + URL = "http://10.0.0.7:8090/message" + HEADERS = { + "X-Gotify-Key": "AiOLxxDxYOCc7bY", + "Content-type": 'application/json' + } + PAYLOAD = { + "title": "test_title", + "priority": 8, + "message": "test_message", + "extras": { + "client::display": { + "contentType": "text/plain" + } + } + } + + @mock.patch("requests.post") + def test_notification(self, m__request): + message = GotifyNotification( + "http://10.0.0.7:8090", + "AiOLxxDxYOCc7bY", + "test_title", + "test_message", 8 + ) + assert message.url == self.URL + assert message.headers == self.HEADERS + assert message.payload == self.PAYLOAD + assert message.delivered is False + + m__request().ok = True + message.send() + requests.post.assert_called_with( + self.URL, + headers=self.HEADERS, + json=self.PAYLOAD + ) + assert requests.post().ok is True + assert message.delivered is True + + def test_arguments_number(self): + with self.assertRaises(TypeError): + GotifyNotification(12345) + + +if __name__ == '__main__': + unittest.main() From 3ad903180bc05c286e3f652b780491c906d71acc Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 1 Jun 2022 14:51:48 +0200 Subject: [PATCH 3/9] added to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0d6a243..520d160 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode dist -*.egg-info \ No newline at end of file +*.egg-info +__pycache__ \ No newline at end of file From e34e1578687d2d64910e17eb52fc159ed69c8dac Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Thu, 2 Jun 2022 21:31:53 +0200 Subject: [PATCH 4/9] added connector --- .gitignore | 3 +- gotify_message/gotify_connector.py | 177 ++++++++++++++++++++++++++ gotify_message/gotify_notification.py | 48 +++---- 3 files changed, 200 insertions(+), 28 deletions(-) create mode 100644 gotify_message/gotify_connector.py diff --git a/.gitignore b/.gitignore index 0d6a243..520d160 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode dist -*.egg-info \ No newline at end of file +*.egg-info +__pycache__ \ No newline at end of file diff --git a/gotify_message/gotify_connector.py b/gotify_message/gotify_connector.py new file mode 100644 index 0000000..5284401 --- /dev/null +++ b/gotify_message/gotify_connector.py @@ -0,0 +1,177 @@ + +import logging +import requests +import json + +LOG = logging.getLogger(__name__) + + +class GotifyConnector: + URL = None + TOKEN = None + + def __init__( + self, + url: str = None, + token: str = None + ): + + self.url = (url or self.URL) + self.token = (token or self.TOKEN) + self._check_status() + + def _get(self, resource, timeout: int = None) -> dict: + headers = {"X-Gotify-Key": self.token} + url = self.url + resource + response = requests.get(url, headers=headers, timeout=timeout) + + if response.ok: + return response.json() + else: + error = ( + f"'{url}' response error:\n" + + json.dumps(response.json(), indent=3) + ) + LOG.error(error) + return None + + def _post( + self, + resource: dict = None, + payload: dict = None, + headers: dict = None, + ): + + _headers = { + "X-Gotify-Key": self.token, + "Content-type": "application/json" + } + if headers: + _headers |= headers + url = self.url + resource + response = requests.post(url, headers=_headers, json=payload) + + if response.ok: + return response.json() + else: + error = ( + f"'{url}' response error:\n" + + json.dumps(response.json(), indent=3) + ) + LOG.error(error) + return None + + @property + def health(self) -> str: + response = self._get("/health", timeout=10) + status = response.get("health") + return status + + @property + def database(self) -> str: + response = self._get("/health", timeout=10) + status = response.get("database") + return status + + def _check_status(self): + if self.health != "green": + log_msg = ( + f"Gotify service {self.url} health probblem: {self.health}" + ) + LOG.warning(log_msg) + else: + log_msg = ( + f"Gotify service {self.url} health status: {self.health}" + ) + LOG.debug(log_msg) + + if self.database != "green": + log_msg = ( + f"Gotify service {self.url} database probblem: {self.database}" + ) + LOG.warning(log_msg) + else: + log_msg = ( + f"Gotify service {self.url} database status: {self.database}" + ) + LOG.debug(log_msg) + + +class GotifyNotification(GotifyConnector): + """Basic gotify notification class + + :param url: gotify server url + :type url: str + :param token: token to which application send a message + :type token: str + :param title: title of the message + :type title: str + :param message: message + :type message: str + :param priority: message priority, defaults to 5 + :type priority: int, optional + """ + CONTENT_TYPE = 'plain' + + def __init__( + self, + url: str = None, + token: str = None, + title: str = None, + message: str = None, + priority: int = 5 + ): + """Constructor method""" + + super().__init__(url, token) + + self.payload = { + "title": title, + "priority": priority, + "message": message, + "extras": { + "client::display": { + "contentType": "text/"+self.CONTENT_TYPE + } + } + } + self.delivered = False + + def send( + self, + message: str = None, + title: str = None, + priority: int = None + ) -> bool: + """sends message to gotify server + + :param message: message + :type message: str + :param title: title of the message + :type title: str + :param priority: message priority, defaults to 5 + :type priority: int, optional + :return: True is message delivered + :rtype: bool + """ + + if message: + self.payload['message'] = message + if title: + self.payload['title'] = title + if priority: + self.payload['priority'] = priority + + if self._post("/message", self.payload): + self.delivered = True + + return self.delivered + + @property + def json(self) -> str: + """property shows constructed object in string json format` + + :return: constructed object in string json format + :rtype: str + """ + return json.dumps(self.__dict__, indent=4) diff --git a/gotify_message/gotify_notification.py b/gotify_message/gotify_notification.py index eccfe90..e8a2946 100644 --- a/gotify_message/gotify_notification.py +++ b/gotify_message/gotify_notification.py @@ -1,14 +1,14 @@ -import requests import json +from gotify_connector import GotifyConnector -class GotifyNotification: +class GotifyNotification(GotifyConnector): """Basic gotify notification class :param url: gotify server url :type url: str - :param app_token: token to which application send a message - :type app_token: str + :param token: token to which application send a message + :type token: str :param title: title of the message :type title: str :param message: message @@ -16,24 +16,20 @@ class GotifyNotification: :param priority: message priority, defaults to 5 :type priority: int, optional """ - CONTENT_TYPE = 'plain' def __init__( self, - url, - app_token: str, + url: str = None, + token: str = None, title: str = None, message: str = None, priority: int = 5 ): """Constructor method""" - self.url = url + '/message' - self.headers = { - "X-Gotify-Key": app_token, - "Content-type": 'application/json' - } + super().__init__(url, token) + self.payload = { "title": title, "priority": priority, @@ -46,20 +42,22 @@ def __init__( } self.delivered = False - def send(self, - message: str = None, - title: str = None, - priority: int = None) -> requests.models.Response: + def send( + self, + message: str = None, + title: str = None, + priority: int = None + ) -> bool: """sends message to gotify server - :param title: title of the message - :type title: str :param message: message :type message: str + :param title: title of the message + :type title: str :param priority: message priority, defaults to 5 :type priority: int, optional - :return: _description_ - :rtype: response.Request + :return: True is message delivered + :rtype: bool """ if message: @@ -69,14 +67,10 @@ def send(self, if priority: self.payload['priority'] = priority - response = requests.post( - self.url, - headers=self.headers, - json=self.payload - ) - if response.ok: + if self._post("/message", self.payload): self.delivered = True - return response + + return self.delivered @property def json(self) -> str: From 6fce0eeda34354fa69920e3a4869dfe26a3b5fb4 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 8 Jun 2022 16:01:08 +0200 Subject: [PATCH 5/9] removed not needed class --- gotify_message/gotify_connector.py | 83 +----------------------------- 1 file changed, 1 insertion(+), 82 deletions(-) diff --git a/gotify_message/gotify_connector.py b/gotify_message/gotify_connector.py index 5284401..6f7a5cb 100644 --- a/gotify_message/gotify_connector.py +++ b/gotify_message/gotify_connector.py @@ -1,4 +1,3 @@ - import logging import requests import json @@ -40,7 +39,7 @@ def _post( resource: dict = None, payload: dict = None, headers: dict = None, - ): + ) --> dict: _headers = { "X-Gotify-Key": self.token, @@ -95,83 +94,3 @@ def _check_status(self): f"Gotify service {self.url} database status: {self.database}" ) LOG.debug(log_msg) - - -class GotifyNotification(GotifyConnector): - """Basic gotify notification class - - :param url: gotify server url - :type url: str - :param token: token to which application send a message - :type token: str - :param title: title of the message - :type title: str - :param message: message - :type message: str - :param priority: message priority, defaults to 5 - :type priority: int, optional - """ - CONTENT_TYPE = 'plain' - - def __init__( - self, - url: str = None, - token: str = None, - title: str = None, - message: str = None, - priority: int = 5 - ): - """Constructor method""" - - super().__init__(url, token) - - self.payload = { - "title": title, - "priority": priority, - "message": message, - "extras": { - "client::display": { - "contentType": "text/"+self.CONTENT_TYPE - } - } - } - self.delivered = False - - def send( - self, - message: str = None, - title: str = None, - priority: int = None - ) -> bool: - """sends message to gotify server - - :param message: message - :type message: str - :param title: title of the message - :type title: str - :param priority: message priority, defaults to 5 - :type priority: int, optional - :return: True is message delivered - :rtype: bool - """ - - if message: - self.payload['message'] = message - if title: - self.payload['title'] = title - if priority: - self.payload['priority'] = priority - - if self._post("/message", self.payload): - self.delivered = True - - return self.delivered - - @property - def json(self) -> str: - """property shows constructed object in string json format` - - :return: constructed object in string json format - :rtype: str - """ - return json.dumps(self.__dict__, indent=4) From 8171c2018aac773ff585ff1ecd71579345463706 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 8 Jun 2022 16:05:44 +0200 Subject: [PATCH 6/9] syntax fix --- gotify_message/gotify_connector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gotify_message/gotify_connector.py b/gotify_message/gotify_connector.py index 6f7a5cb..c38055d 100644 --- a/gotify_message/gotify_connector.py +++ b/gotify_message/gotify_connector.py @@ -39,7 +39,7 @@ def _post( resource: dict = None, payload: dict = None, headers: dict = None, - ) --> dict: + ) -> dict: _headers = { "X-Gotify-Key": self.token, From bd9f269877070e6e21965f3be74447abaad583fc Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 8 Jun 2022 16:08:12 +0200 Subject: [PATCH 7/9] added import --- gotify_message/gotify_notification.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gotify_message/gotify_notification.py b/gotify_message/gotify_notification.py index e8a2946..1aadd38 100644 --- a/gotify_message/gotify_notification.py +++ b/gotify_message/gotify_notification.py @@ -1,5 +1,5 @@ import json -from gotify_connector import GotifyConnector +from .gotify_connector import GotifyConnector class GotifyNotification(GotifyConnector): From 3ee70f13fc26c6c0185c787580258691e8472c5e Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Wed, 6 Jul 2022 16:45:13 +0200 Subject: [PATCH 8/9] unittests --- .gitignore | 3 +- tests/pytest1.py | 103 ++++++++++++++++++++ tests/test_connector.py | 44 +++++++++ tests/{test_notification.py => unittest.py} | 32 ++++-- 4 files changed, 175 insertions(+), 7 deletions(-) create mode 100644 tests/pytest1.py create mode 100644 tests/test_connector.py rename tests/{test_notification.py => unittest.py} (58%) diff --git a/.gitignore b/.gitignore index 520d160..2eb7a83 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode dist *.egg-info -__pycache__ \ No newline at end of file +__pycache__ +venv \ No newline at end of file diff --git a/tests/pytest1.py b/tests/pytest1.py new file mode 100644 index 0000000..5edbd8e --- /dev/null +++ b/tests/pytest1.py @@ -0,0 +1,103 @@ +import unittest +import pytest +from unittest import mock +from gotify_message.gotify_connector import requests +from gotify_message.gotify_connector import GotifyConnector + +URL = "http://10.0.0.7:8090" +TOKEN = "tokenASDASDA" + + +@pytest.fixture +def url(): + return URL + +@pytest.fixture +def token(): + return TOKEN + +@pytest.fixture +def health_response(): + return {'health': 'green', 'database': 'green'} + +def mock_response(fail = None): + def request(method, **kwargs): + response = requests.Response() + request = requests.Request(method) + response.status_code = 200 + for attr in kwargs: + if hasattr(request, attr): + setattr(request, attr, kwargs[attr]) + response.request = request + if fail: + response._content = b'{"health":"red","database":"red"}' + else: + response._content = b'{"health":"green","database":"green"}' + return response + return request + + +@pytest.fixture +def gotify_connector(url, token, health_response): + with mock.patch("requests.get", new=mock.MagicMock()): + requests.get().json.return_value = health_response + requests.get().ok = True + yield GotifyConnector(url, token) + +@pytest.fixture +def gotify_connector_mock_response(url, token): + with mock.patch("requests.get", new=mock_response()): + yield GotifyConnector(url, token) + +@pytest.fixture +def gotify_connector_mock_response_fail(url, token): + with mock.patch("requests.get", side_effect=mock_response(fail=True)): + yield GotifyConnector(url, token) + +class TestConnector: + + def test_constructor(self, gotify_connector): + assert gotify_connector.url == "http://10.0.0.7:8090" + assert gotify_connector.token == "tokenASDASDA" + assert gotify_connector.health == "green" + assert gotify_connector.database == "green" + + def test_constructor_2(self): + with mock.patch("requests.get"): + requests.get().json.return_value = {'health': 'green', 'database': 'green'} + requests.get().ok = True + gotify_connector = GotifyConnector("http://10.0.0.7:8090","tokenASDASDA") + assert gotify_connector.url == "http://10.0.0.7:8090" + assert gotify_connector.token == "tokenASDASDA" + assert gotify_connector.health == "green" + assert gotify_connector.database == "green" + + def test_constructor_3(self): + with mock.patch("requests.get"): + requests.get.side_effect = requests.exceptions.HTTPError + with pytest.raises(requests.exceptions.HTTPError): + gotify_connector = GotifyConnector("http://10.0.0.7:8090","tokenASDASDA") + + + def test_constructor_mock_response(self, gotify_connector_mock_response): + assert gotify_connector_mock_response.url == "http://10.0.0.7:8090" + assert gotify_connector_mock_response.token == "tokenASDASDA" + assert gotify_connector_mock_response.health == "green" + assert gotify_connector_mock_response.database == "green" + + def test_constructor_mock_response_fail(self, gotify_connector_mock_response_fail): + assert gotify_connector_mock_response_fail.url == "http://10.0.0.7:8090" + assert gotify_connector_mock_response_fail.token == "tokenASDASDA" + assert gotify_connector_mock_response_fail.health == "red" + assert gotify_connector_mock_response_fail.database == "red" + + @mock.patch("requests.get", side_effect=mock_response(fail=True)) + def test_constructor_1(self, m__get): + client = GotifyConnector("http://10.0.0.7:8090", "daskjdsakjn") + assert client.url == "http://10.0.0.7:8090" + assert client.token == "daskjdsakjn" + assert client.health == "red" + assert client.database == "red" + + + diff --git a/tests/test_connector.py b/tests/test_connector.py new file mode 100644 index 0000000..e0d2339 --- /dev/null +++ b/tests/test_connector.py @@ -0,0 +1,44 @@ +from pytest import fixture,raises +from mock import patch +from gotify_message.gotify_connector import requests +from gotify_message.gotify_connector import GotifyConnector + + +URL="http://1.2.3.4:8888" +TOKEN="lskd12314sadf1" + + + + +@fixture +@patch("requests.get") +def gotify_connector2(m__get): + m__get().json.return_value = {'health': 'green', 'database': 'green'} + m__get().ok = True + yield GotifyConnector(URL, TOKEN) + + +def mock_get(**kwargs): + request = requests.Request(**kwargs) + response = requests.Response() + response.status_code = 200 + response._content = {'health': 'green', 'database': 'green'} + return request + +@fixture +def gotify_connector(): + with patch("requests.get"): + requests.get().json.return_value = {'health': 'green', 'database': 'green'} + requests.get().ok= True + yield GotifyConnector(URL, TOKEN) + +def test_connector(gotify_connector): + assert gotify_connector.health == "green" + assert gotify_connector.database == "green" + + +def test_1(): + with patch("requests.get", new=mock_get): + requests.get(method="GET") + + diff --git a/tests/test_notification.py b/tests/unittest.py similarity index 58% rename from tests/test_notification.py rename to tests/unittest.py index 1db4bb8..1bce47b 100644 --- a/tests/test_notification.py +++ b/tests/unittest.py @@ -1,10 +1,12 @@ from unittest import TestCase, mock from gotify_message import GotifyNotification -from gotify_message.gotify_notification import requests +from gotify_message.gotify_connector import GotifyConnector +from gotify_message.gotify_connector import requests class TestGotifyNotification(TestCase): - URL = "http://10.0.0.7:8090/message" + URL = "http://10.0.0.7:8090" + RESOURCE = "/message" HEADERS = { "X-Gotify-Key": "AiOLxxDxYOCc7bY", "Content-type": 'application/json' @@ -21,7 +23,16 @@ class TestGotifyNotification(TestCase): } @mock.patch("requests.post") - def test_notification(self, m__request): + @mock.patch("requests.get") + def test_notification(self, m__post, m__get): + + m__get().ok = True + m__get().json.return_value = {'health': 'green', 'database': 'green'} + + m__post().ok = True + m__post().json.return_value = {'health': 'green', 'database': 'green'} + + message = GotifyNotification( "http://10.0.0.7:8090", "AiOLxxDxYOCc7bY", @@ -29,14 +40,13 @@ def test_notification(self, m__request): "test_message", 8 ) assert message.url == self.URL - assert message.headers == self.HEADERS assert message.payload == self.PAYLOAD assert message.delivered is False - m__request().ok = True + message.send() requests.post.assert_called_with( - self.URL, + self.URL + self.RESOURCE, headers=self.HEADERS, json=self.PAYLOAD ) @@ -48,5 +58,15 @@ def test_arguments_number(self): GotifyNotification(12345) +class TestGotifyConnector(TestCase): + + @mock.patch("requests.get") + def test_constructor(self, m__get): + m__get().ok = True + m__get().json.return_value = {'health': 'green', 'database': 'green'} + client = GotifyConnector("http://1.1.1.1:8888", "tokenASDASDA") + assert client.token == "tokenASDASDA" + + if __name__ == '__main__': unittest.main() From 326df2d1947e82584afaa784873bea41fc761650 Mon Sep 17 00:00:00 2001 From: Przemyslaw Bubas Date: Sun, 10 Jul 2022 17:33:28 +0200 Subject: [PATCH 9/9] git ignore --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2eb7a83..a5a03cc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ dist *.egg-info __pycache__ -venv \ No newline at end of file +venv +.tox +tox.ini \ No newline at end of file