diff --git a/docs/api.rst b/docs/api.rst index 0ebf9eb..e515a3e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -19,6 +19,9 @@ Enums .. autoclass:: MonitorType :members: +.. autoclass:: MonitorStatus + :members: + .. autoclass:: NotificationType :members: diff --git a/run_tests.sh b/run_tests.sh index ff942c7..e50dea1 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -5,7 +5,7 @@ if [ $version ] then versions=("$version") else - versions=(1.21.3) + versions=(1.22.1 1.22.0 1.21.3) fi for version in ${versions[*]} diff --git a/scripts/build_notification_docstring.py b/scripts/build_notification_docstring.py index ce389ab..1efbdfb 100644 --- a/scripts/build_notification_docstring.py +++ b/scripts/build_notification_docstring.py @@ -89,6 +89,12 @@ data = { "twilioToNumber": "To Number.", "twilioFromNumber": "From Number.", + + "pushoverttl": "Message TTL (Seconds).", + + "ntfyaccesstoken": "Access Token.", + + "ntfyAuthenticationMethod": "Authentication Method.", } for provider in notification_provider_options: diff --git a/tests/test_monitor.py b/tests/test_monitor.py index 74abda9..4a26481 100644 --- a/tests/test_monitor.py +++ b/tests/test_monitor.py @@ -1,4 +1,5 @@ import unittest +from packaging.version import parse as parse_version from uptime_kuma_api import UptimeKumaException, MonitorType, AuthMethod, MonitorStatus from uptime_kuma_test_case import UptimeKumaTestCase @@ -303,6 +304,26 @@ class TestMonitor(UptimeKumaTestCase): } self.do_test_monitor_type(expected_monitor) + def test_monitor_type_group(self): + if parse_version(self.api.version) < parse_version("1.22"): + self.skipTest("Unsupported in this Uptime Kuma version") + + # create monitor group + expected_monitor = { + "type": MonitorType.GROUP, + "name": "monitor 1" + } + group_monitor = self.do_test_monitor_type(expected_monitor) + group_monitor_id = group_monitor["id"] + + # use monitor group as parent for another monitor + expected_monitor = { + "type": MonitorType.PUSH, + "name": "monitor 1", + "parent": group_monitor_id + } + self.do_test_monitor_type(expected_monitor) + def test_delete_not_existing_monitor(self): with self.assertRaises(UptimeKumaException): self.api.delete_monitor(42) diff --git a/uptime_kuma_api/api.py b/uptime_kuma_api/api.py index a440ab3..9912dd3 100644 --- a/uptime_kuma_api/api.py +++ b/uptime_kuma_api/api.py @@ -11,6 +11,7 @@ from typing import Any import requests import socketio +from packaging.version import parse as parse_version from . import ( AuthMethod, @@ -58,7 +59,11 @@ def parse_value(data, key, type_, default=None) -> None: else: if key in data: if data[key] is not None: - data[key] = type_(data[key]) + try: + data[key] = type_(data[key]) + except ValueError: + # todo: add warning to logs + pass elif default is not None: data[key] = default @@ -128,10 +133,10 @@ def _convert_monitor_input(kwargs) -> None: kwargs["databaseConnectionString"] = "postgres://username:password@host:port/database" elif kwargs["type"] == MonitorType.MYSQL: kwargs["databaseConnectionString"] = "mysql://username:password@host:port/database" - elif kwargs["type"] == MonitorType.MONGODB: - kwargs["databaseConnectionString"] = "mongodb://username:password@host:port/database" elif kwargs["type"] == MonitorType.REDIS: kwargs["databaseConnectionString"] = "redis://user:password@host:port" + elif kwargs["type"] == MonitorType.MONGODB: + kwargs["databaseConnectionString"] = "mongodb://username:password@host:port/database" if kwargs["type"] == MonitorType.PUSH and not kwargs.get("pushToken"): kwargs["pushToken"] = gen_secret(10) @@ -187,49 +192,6 @@ def _build_proxy_data( return data -def _build_status_page_data( - slug: str, - - # config - id: int, - title: str, - description: str = None, - theme: str = "light", - published: bool = True, - showTags: bool = False, - domainNameList: list = None, - googleAnalyticsId: str = None, - customCSS: str = "", - footerText: str = None, - showPoweredBy: bool = True, - - icon: str = "/icon.svg", - publicGroupList: list = None -) -> tuple[str, dict, str, list]: - if theme not in ["light", "dark"]: - raise ValueError - if not domainNameList: - domainNameList = [] - if not publicGroupList: - publicGroupList = [] - config = { - "id": id, - "slug": slug, - "title": title, - "description": description, - "icon": icon, - "theme": theme, - "published": published, - "showTags": showTags, - "domainNameList": domainNameList, - "googleAnalyticsId": googleAnalyticsId, - "customCSS": customCSS, - "footerText": footerText, - "showPoweredBy": showPoweredBy - } - return slug, config, icon, publicGroupList - - def _convert_docker_host_input(kwargs) -> None: if not kwargs["dockerDaemon"]: if kwargs["dockerType"] == DockerType.SOCKET: @@ -315,7 +277,8 @@ def _check_arguments_monitor(kwargs) -> None: MonitorType.MYSQL: [], MonitorType.MONGODB: [], MonitorType.RADIUS: ["radiusUsername", "radiusPassword", "radiusSecret", "radiusCalledStationId", "radiusCallingStationId"], - MonitorType.REDIS: [] + MonitorType.REDIS: [], + MonitorType.GROUP: [] } type_ = kwargs["type"] required_args = required_args_by_type[type_] @@ -678,6 +641,7 @@ class UptimeKumaApi(object): self, type: MonitorType, name: str, + parent: int = None, description: str = None, interval: int = 60, retryInterval: int = 60, @@ -770,6 +734,11 @@ class UptimeKumaApi(object): "httpBodyEncoding": httpBodyEncoding } + if parse_version(self.version) >= parse_version("1.22"): + data.update({ + "parent": parent + }) + if type in [MonitorType.KEYWORD, MonitorType.GRPC_KEYWORD]: data.update({ "keyword": keyword, @@ -939,6 +908,54 @@ class UptimeKumaApi(object): } return data + def _build_status_page_data( + self, + slug: str, + + # config + id: int, + title: str, + description: str = None, + theme: str = None, + published: bool = True, + showTags: bool = False, + domainNameList: list = None, + googleAnalyticsId: str = None, + customCSS: str = "", + footerText: str = None, + showPoweredBy: bool = True, + + icon: str = "/icon.svg", + publicGroupList: list = None + ) -> tuple[str, dict, str, list]: + if not theme: + if parse_version(self.version) >= parse_version("1.22"): + theme = "auto" + else: + theme = "light" + if theme not in ["auto", "light", "dark"]: + raise ValueError + if not domainNameList: + domainNameList = [] + if not publicGroupList: + publicGroupList = [] + config = { + "id": id, + "slug": slug, + "title": title, + "description": description, + "icon": icon, + "theme": theme, + "published": published, + "showTags": showTags, + "domainNameList": domainNameList, + "googleAnalyticsId": googleAnalyticsId, + "customCSS": customCSS, + "footerText": footerText, + "showPoweredBy": showPoweredBy + } + return slug, config, icon, publicGroupList + # monitor def get_monitors(self) -> list[dict]: @@ -961,14 +978,17 @@ class UptimeKumaApi(object): 'basic_auth_pass': None, 'basic_auth_user': None, 'body': None, + 'childrenIDs': [], 'databaseConnectionString': None, 'databaseQuery': None, + 'description': None, 'dns_last_result': None, 'dns_resolve_server': '1.1.1.1', 'dns_resolve_type': 'A', 'docker_container': None, 'docker_host': None, 'expiryNotification': False, + 'forceInactive': False, 'game': None, 'grpcBody': None, 'grpcEnableTls': False, @@ -979,6 +999,7 @@ class UptimeKumaApi(object): 'grpcUrl': None, 'headers': None, 'hostname': None, + 'httpBodyEncoding': 'json', 'id': 1, 'ignoreTls': False, 'includeSensitiveData': True, @@ -986,7 +1007,7 @@ class UptimeKumaApi(object): 'keyword': None, 'maintenance': False, 'maxredirects': 10, - 'maxretries': 1, + 'maxretries': 0, 'method': 'GET', 'mqttPassword': None, 'mqttSuccessMessage': None, @@ -995,6 +1016,8 @@ class UptimeKumaApi(object): 'name': 'monitor 1', 'notificationIDList': [1, 2], 'packetSize': 56, + 'parent': None, + 'pathName': 'monitor 1', 'port': None, 'proxyId': None, 'pushToken': None, @@ -1006,7 +1029,10 @@ class UptimeKumaApi(object): 'resendInterval': 0, 'retryInterval': 60, 'tags': [], - 'type': + 'tlsCa': None, + 'tlsCert': None, + 'tlsKey': None, + 'type': , 'upsideDown': False, 'url': 'http://127.0.0.1', 'weight': 2000 @@ -1045,14 +1071,17 @@ class UptimeKumaApi(object): 'basic_auth_pass': None, 'basic_auth_user': None, 'body': None, + 'childrenIDs': [], 'databaseConnectionString': None, 'databaseQuery': None, + 'description': None, 'dns_last_result': None, 'dns_resolve_server': '1.1.1.1', 'dns_resolve_type': 'A', 'docker_container': None, 'docker_host': None, 'expiryNotification': False, + 'forceInactive': False, 'game': None, 'grpcBody': None, 'grpcEnableTls': False, @@ -1063,6 +1092,7 @@ class UptimeKumaApi(object): 'grpcUrl': None, 'headers': None, 'hostname': None, + 'httpBodyEncoding': 'json', 'id': 1, 'ignoreTls': False, 'includeSensitiveData': True, @@ -1070,7 +1100,7 @@ class UptimeKumaApi(object): 'keyword': None, 'maintenance': False, 'maxredirects': 10, - 'maxretries': 1, + 'maxretries': 0, 'method': 'GET', 'mqttPassword': None, 'mqttSuccessMessage': None, @@ -1079,6 +1109,8 @@ class UptimeKumaApi(object): 'name': 'monitor 1', 'notificationIDList': [1, 2], 'packetSize': 56, + 'parent': None, + 'pathName': 'monitor 1', 'port': None, 'proxyId': None, 'pushToken': None, @@ -1090,7 +1122,10 @@ class UptimeKumaApi(object): 'resendInterval': 0, 'retryInterval': 60, 'tags': [], - 'type': + 'tlsCa': None, + 'tlsCert': None, + 'tlsKey': None, + 'type': , 'upsideDown': False, 'url': 'http://127.0.0.1', 'weight': 2000 @@ -1781,7 +1816,6 @@ class UptimeKumaApi(object): 'monitorList': [ { 'id': 1, - 'maintenance': False, 'name': 'monitor 1', 'sendUrl': 0 } @@ -1871,7 +1905,7 @@ class UptimeKumaApi(object): :param int id: Id of the status page to save :param str title: Title :param str, optional description: Description, defaults to None - :param str, optional theme: Switch Theme, defaults to "light" + :param str, optional theme: Switch Theme, defaults to "auto" :param bool, optional published: Published, defaults to True :param bool, optional showTags: Show Tags, defaults to False :param list, optional domainNameList: Domain Names, defaults to None @@ -1923,7 +1957,7 @@ class UptimeKumaApi(object): status_page.pop("incident") status_page.pop("maintenanceList") status_page.update(kwargs) - data = _build_status_page_data(**status_page) + data = self._build_status_page_data(**status_page) r = self._call('saveStatusPage', data) # uptime kuma does not send the status page list event when a status page is saved @@ -3460,12 +3494,10 @@ class UptimeKumaApi(object): >>> api.get_monitor_maintenance(1) [ { - "id": 1, - "name": "monitor 1" + "id": 1 }, { - "id": 2, - "name": "monitor 2" + "id": 2 } ] """ diff --git a/uptime_kuma_api/docstrings.py b/uptime_kuma_api/docstrings.py index 946a7d9..0d1e2fe 100644 --- a/uptime_kuma_api/docstrings.py +++ b/uptime_kuma_api/docstrings.py @@ -18,6 +18,7 @@ def monitor_docstring(mode) -> str: return f""" :param MonitorType{", optional" if mode == "edit" else ""} type: Monitor Type :param str{", optional" if mode == "edit" else ""} name: Friendly Name + :param str, optional parent: Id of the monitor group, defaults to None :param str, optional description: Description, defaults to None :param int, optional interval: Heartbeat Interval, defaults to 60 :param int, optional retryInterval: Retry every X seconds, defaults to 60 @@ -133,12 +134,14 @@ def notification_docstring(mode) -> str: :param str mattermostchannel: Notification option for ``type`` :attr:`~.NotificationType.MATTERMOST`. :param str mattermosticonemo: Notification option for ``type`` :attr:`~.NotificationType.MATTERMOST`. :param str mattermosticonurl: Notification option for ``type`` :attr:`~.NotificationType.MATTERMOST`. + :param str ntfyAuthenticationMethod: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. Authentication Method. :param str ntfyusername: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. :param str ntfypassword: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. + :param str ntfyaccesstoken: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. Access Token. :param str, optional ntfytopic: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. :param int, optional ntfyPriority: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. - :param str ntfyIcon: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. :param str, optional ntfyserverurl: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. + :param str ntfyIcon: Notification option for ``type`` :attr:`~.NotificationType.NTFY`. :param str octopushVersion: Notification option for ``type`` :attr:`~.NotificationType.OCTOPUSH`. :param str, optional octopushAPIKey: Notification option for ``type`` :attr:`~.NotificationType.OCTOPUSH`. :param str, optional octopushLogin: Notification option for ``type`` :attr:`~.NotificationType.OCTOPUSH`. @@ -196,6 +199,7 @@ def notification_docstring(mode) -> str: :param str pushoverpriority: Notification option for ``type`` :attr:`~.NotificationType.PUSHOVER`. :param str pushovertitle: Notification option for ``type`` :attr:`~.NotificationType.PUSHOVER`. :param str pushoverdevice: Notification option for ``type`` :attr:`~.NotificationType.PUSHOVER`. + :param int pushoverttl: Notification option for ``type`` :attr:`~.NotificationType.PUSHOVER`. Message TTL (Seconds). :param str, optional pushyAPIKey: Notification option for ``type`` :attr:`~.NotificationType.PUSHY`. :param str, optional pushyToken: Notification option for ``type`` :attr:`~.NotificationType.PUSHY`. :param str rocketchannel: Notification option for ``type`` :attr:`~.NotificationType.ROCKET_CHAT`. diff --git a/uptime_kuma_api/monitor_type.py b/uptime_kuma_api/monitor_type.py index 975c839..380c994 100644 --- a/uptime_kuma_api/monitor_type.py +++ b/uptime_kuma_api/monitor_type.py @@ -54,3 +54,6 @@ class MonitorType(str, Enum): REDIS = "redis" """Redis""" + + GROUP = "group" + """Group""" diff --git a/uptime_kuma_api/notification_providers.py b/uptime_kuma_api/notification_providers.py index 7a48edf..ccb239c 100644 --- a/uptime_kuma_api/notification_providers.py +++ b/uptime_kuma_api/notification_providers.py @@ -259,12 +259,14 @@ notification_provider_options = { mattermosticonurl=dict(type="str", required=False), ), NotificationType.NTFY: dict( + ntfyAuthenticationMethod=dict(type="str", required=False), ntfyusername=dict(type="str", required=False), ntfypassword=dict(type="str", required=False), + ntfyaccesstoken=dict(type="str", required=False), ntfytopic=dict(type="str", required=True), ntfyPriority=dict(type="int", required=True), - ntfyIcon=dict(type="str", required=False), ntfyserverurl=dict(type="str", required=True), + ntfyIcon=dict(type="str", required=False), ), NotificationType.OCTOPUSH: dict( octopushVersion=dict(type="str", required=False), @@ -317,6 +319,7 @@ notification_provider_options = { pushoverpriority=dict(type="str", required=False), pushovertitle=dict(type="str", required=False), pushoverdevice=dict(type="str", required=False), + pushoverttl=dict(type="int", required=False), ), NotificationType.PUSHY: dict( pushyAPIKey=dict(type="str", required=True), @@ -436,6 +439,9 @@ notification_provider_conditions = dict( min=1, max=5, ), + pushoverttl=dict( + min=0, + ), smseaglePriority=dict( min=0, max=9,