feat: add support for uptime kuma 1.22.0 and 1.22.1

This commit is contained in:
lucasheld 2023-07-07 22:28:20 +02:00
parent 934ab15457
commit 06f1173569
8 changed files with 136 additions and 61 deletions

View file

@ -19,6 +19,9 @@ Enums
.. autoclass:: MonitorType .. autoclass:: MonitorType
:members: :members:
.. autoclass:: MonitorStatus
:members:
.. autoclass:: NotificationType .. autoclass:: NotificationType
:members: :members:

View file

@ -5,7 +5,7 @@ if [ $version ]
then then
versions=("$version") versions=("$version")
else else
versions=(1.21.3) versions=(1.22.1 1.22.0 1.21.3)
fi fi
for version in ${versions[*]} for version in ${versions[*]}

View file

@ -89,6 +89,12 @@ data = {
"twilioToNumber": "To Number.", "twilioToNumber": "To Number.",
"twilioFromNumber": "From Number.", "twilioFromNumber": "From Number.",
"pushoverttl": "Message TTL (Seconds).",
"ntfyaccesstoken": "Access Token.",
"ntfyAuthenticationMethod": "Authentication Method.",
} }
for provider in notification_provider_options: for provider in notification_provider_options:

View file

@ -1,4 +1,5 @@
import unittest import unittest
from packaging.version import parse as parse_version
from uptime_kuma_api import UptimeKumaException, MonitorType, AuthMethod, MonitorStatus from uptime_kuma_api import UptimeKumaException, MonitorType, AuthMethod, MonitorStatus
from uptime_kuma_test_case import UptimeKumaTestCase from uptime_kuma_test_case import UptimeKumaTestCase
@ -303,6 +304,26 @@ class TestMonitor(UptimeKumaTestCase):
} }
self.do_test_monitor_type(expected_monitor) 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): def test_delete_not_existing_monitor(self):
with self.assertRaises(UptimeKumaException): with self.assertRaises(UptimeKumaException):
self.api.delete_monitor(42) self.api.delete_monitor(42)

View file

@ -11,6 +11,7 @@ from typing import Any
import requests import requests
import socketio import socketio
from packaging.version import parse as parse_version
from . import ( from . import (
AuthMethod, AuthMethod,
@ -58,7 +59,11 @@ def parse_value(data, key, type_, default=None) -> None:
else: else:
if key in data: if key in data:
if data[key] is not None: if data[key] is not None:
try:
data[key] = type_(data[key]) data[key] = type_(data[key])
except ValueError:
# todo: add warning to logs
pass
elif default is not None: elif default is not None:
data[key] = default data[key] = default
@ -128,10 +133,10 @@ def _convert_monitor_input(kwargs) -> None:
kwargs["databaseConnectionString"] = "postgres://username:password@host:port/database" kwargs["databaseConnectionString"] = "postgres://username:password@host:port/database"
elif kwargs["type"] == MonitorType.MYSQL: elif kwargs["type"] == MonitorType.MYSQL:
kwargs["databaseConnectionString"] = "mysql://username:password@host:port/database" 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: elif kwargs["type"] == MonitorType.REDIS:
kwargs["databaseConnectionString"] = "redis://user:password@host:port" 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"): if kwargs["type"] == MonitorType.PUSH and not kwargs.get("pushToken"):
kwargs["pushToken"] = gen_secret(10) kwargs["pushToken"] = gen_secret(10)
@ -187,49 +192,6 @@ def _build_proxy_data(
return 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: def _convert_docker_host_input(kwargs) -> None:
if not kwargs["dockerDaemon"]: if not kwargs["dockerDaemon"]:
if kwargs["dockerType"] == DockerType.SOCKET: if kwargs["dockerType"] == DockerType.SOCKET:
@ -315,7 +277,8 @@ def _check_arguments_monitor(kwargs) -> None:
MonitorType.MYSQL: [], MonitorType.MYSQL: [],
MonitorType.MONGODB: [], MonitorType.MONGODB: [],
MonitorType.RADIUS: ["radiusUsername", "radiusPassword", "radiusSecret", "radiusCalledStationId", "radiusCallingStationId"], MonitorType.RADIUS: ["radiusUsername", "radiusPassword", "radiusSecret", "radiusCalledStationId", "radiusCallingStationId"],
MonitorType.REDIS: [] MonitorType.REDIS: [],
MonitorType.GROUP: []
} }
type_ = kwargs["type"] type_ = kwargs["type"]
required_args = required_args_by_type[type_] required_args = required_args_by_type[type_]
@ -678,6 +641,7 @@ class UptimeKumaApi(object):
self, self,
type: MonitorType, type: MonitorType,
name: str, name: str,
parent: int = None,
description: str = None, description: str = None,
interval: int = 60, interval: int = 60,
retryInterval: int = 60, retryInterval: int = 60,
@ -770,6 +734,11 @@ class UptimeKumaApi(object):
"httpBodyEncoding": httpBodyEncoding "httpBodyEncoding": httpBodyEncoding
} }
if parse_version(self.version) >= parse_version("1.22"):
data.update({
"parent": parent
})
if type in [MonitorType.KEYWORD, MonitorType.GRPC_KEYWORD]: if type in [MonitorType.KEYWORD, MonitorType.GRPC_KEYWORD]:
data.update({ data.update({
"keyword": keyword, "keyword": keyword,
@ -939,6 +908,54 @@ class UptimeKumaApi(object):
} }
return data 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 # monitor
def get_monitors(self) -> list[dict]: def get_monitors(self) -> list[dict]:
@ -961,14 +978,17 @@ class UptimeKumaApi(object):
'basic_auth_pass': None, 'basic_auth_pass': None,
'basic_auth_user': None, 'basic_auth_user': None,
'body': None, 'body': None,
'childrenIDs': [],
'databaseConnectionString': None, 'databaseConnectionString': None,
'databaseQuery': None, 'databaseQuery': None,
'description': None,
'dns_last_result': None, 'dns_last_result': None,
'dns_resolve_server': '1.1.1.1', 'dns_resolve_server': '1.1.1.1',
'dns_resolve_type': 'A', 'dns_resolve_type': 'A',
'docker_container': None, 'docker_container': None,
'docker_host': None, 'docker_host': None,
'expiryNotification': False, 'expiryNotification': False,
'forceInactive': False,
'game': None, 'game': None,
'grpcBody': None, 'grpcBody': None,
'grpcEnableTls': False, 'grpcEnableTls': False,
@ -979,6 +999,7 @@ class UptimeKumaApi(object):
'grpcUrl': None, 'grpcUrl': None,
'headers': None, 'headers': None,
'hostname': None, 'hostname': None,
'httpBodyEncoding': 'json',
'id': 1, 'id': 1,
'ignoreTls': False, 'ignoreTls': False,
'includeSensitiveData': True, 'includeSensitiveData': True,
@ -986,7 +1007,7 @@ class UptimeKumaApi(object):
'keyword': None, 'keyword': None,
'maintenance': False, 'maintenance': False,
'maxredirects': 10, 'maxredirects': 10,
'maxretries': 1, 'maxretries': 0,
'method': 'GET', 'method': 'GET',
'mqttPassword': None, 'mqttPassword': None,
'mqttSuccessMessage': None, 'mqttSuccessMessage': None,
@ -995,6 +1016,8 @@ class UptimeKumaApi(object):
'name': 'monitor 1', 'name': 'monitor 1',
'notificationIDList': [1, 2], 'notificationIDList': [1, 2],
'packetSize': 56, 'packetSize': 56,
'parent': None,
'pathName': 'monitor 1',
'port': None, 'port': None,
'proxyId': None, 'proxyId': None,
'pushToken': None, 'pushToken': None,
@ -1006,7 +1029,10 @@ class UptimeKumaApi(object):
'resendInterval': 0, 'resendInterval': 0,
'retryInterval': 60, 'retryInterval': 60,
'tags': [], 'tags': [],
'type': <MonitorType.HTTP: 'http'> 'tlsCa': None,
'tlsCert': None,
'tlsKey': None,
'type': <MonitorType.HTTP: 'http'>,
'upsideDown': False, 'upsideDown': False,
'url': 'http://127.0.0.1', 'url': 'http://127.0.0.1',
'weight': 2000 'weight': 2000
@ -1045,14 +1071,17 @@ class UptimeKumaApi(object):
'basic_auth_pass': None, 'basic_auth_pass': None,
'basic_auth_user': None, 'basic_auth_user': None,
'body': None, 'body': None,
'childrenIDs': [],
'databaseConnectionString': None, 'databaseConnectionString': None,
'databaseQuery': None, 'databaseQuery': None,
'description': None,
'dns_last_result': None, 'dns_last_result': None,
'dns_resolve_server': '1.1.1.1', 'dns_resolve_server': '1.1.1.1',
'dns_resolve_type': 'A', 'dns_resolve_type': 'A',
'docker_container': None, 'docker_container': None,
'docker_host': None, 'docker_host': None,
'expiryNotification': False, 'expiryNotification': False,
'forceInactive': False,
'game': None, 'game': None,
'grpcBody': None, 'grpcBody': None,
'grpcEnableTls': False, 'grpcEnableTls': False,
@ -1063,6 +1092,7 @@ class UptimeKumaApi(object):
'grpcUrl': None, 'grpcUrl': None,
'headers': None, 'headers': None,
'hostname': None, 'hostname': None,
'httpBodyEncoding': 'json',
'id': 1, 'id': 1,
'ignoreTls': False, 'ignoreTls': False,
'includeSensitiveData': True, 'includeSensitiveData': True,
@ -1070,7 +1100,7 @@ class UptimeKumaApi(object):
'keyword': None, 'keyword': None,
'maintenance': False, 'maintenance': False,
'maxredirects': 10, 'maxredirects': 10,
'maxretries': 1, 'maxretries': 0,
'method': 'GET', 'method': 'GET',
'mqttPassword': None, 'mqttPassword': None,
'mqttSuccessMessage': None, 'mqttSuccessMessage': None,
@ -1079,6 +1109,8 @@ class UptimeKumaApi(object):
'name': 'monitor 1', 'name': 'monitor 1',
'notificationIDList': [1, 2], 'notificationIDList': [1, 2],
'packetSize': 56, 'packetSize': 56,
'parent': None,
'pathName': 'monitor 1',
'port': None, 'port': None,
'proxyId': None, 'proxyId': None,
'pushToken': None, 'pushToken': None,
@ -1090,7 +1122,10 @@ class UptimeKumaApi(object):
'resendInterval': 0, 'resendInterval': 0,
'retryInterval': 60, 'retryInterval': 60,
'tags': [], 'tags': [],
'type': <MonitorType.HTTP: 'http'> 'tlsCa': None,
'tlsCert': None,
'tlsKey': None,
'type': <MonitorType.HTTP: 'http'>,
'upsideDown': False, 'upsideDown': False,
'url': 'http://127.0.0.1', 'url': 'http://127.0.0.1',
'weight': 2000 'weight': 2000
@ -1781,7 +1816,6 @@ class UptimeKumaApi(object):
'monitorList': [ 'monitorList': [
{ {
'id': 1, 'id': 1,
'maintenance': False,
'name': 'monitor 1', 'name': 'monitor 1',
'sendUrl': 0 'sendUrl': 0
} }
@ -1871,7 +1905,7 @@ class UptimeKumaApi(object):
:param int id: Id of the status page to save :param int id: Id of the status page to save
:param str title: Title :param str title: Title
:param str, optional description: Description, defaults to None :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 published: Published, defaults to True
:param bool, optional showTags: Show Tags, defaults to False :param bool, optional showTags: Show Tags, defaults to False
:param list, optional domainNameList: Domain Names, defaults to None :param list, optional domainNameList: Domain Names, defaults to None
@ -1923,7 +1957,7 @@ class UptimeKumaApi(object):
status_page.pop("incident") status_page.pop("incident")
status_page.pop("maintenanceList") status_page.pop("maintenanceList")
status_page.update(kwargs) status_page.update(kwargs)
data = _build_status_page_data(**status_page) data = self._build_status_page_data(**status_page)
r = self._call('saveStatusPage', data) r = self._call('saveStatusPage', data)
# uptime kuma does not send the status page list event when a status page is saved # 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) >>> api.get_monitor_maintenance(1)
[ [
{ {
"id": 1, "id": 1
"name": "monitor 1"
}, },
{ {
"id": 2, "id": 2
"name": "monitor 2"
} }
] ]
""" """

View file

@ -18,6 +18,7 @@ def monitor_docstring(mode) -> str:
return f""" return f"""
:param MonitorType{", optional" if mode == "edit" else ""} type: Monitor Type :param MonitorType{", optional" if mode == "edit" else ""} type: Monitor Type
:param str{", optional" if mode == "edit" else ""} name: Friendly Name :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 str, optional description: Description, defaults to None
:param int, optional interval: Heartbeat Interval, defaults to 60 :param int, optional interval: Heartbeat Interval, defaults to 60
:param int, optional retryInterval: Retry every X seconds, 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 mattermostchannel: Notification option for ``type`` :attr:`~.NotificationType.MATTERMOST`.
:param str mattermosticonemo: 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 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 ntfyusername: Notification option for ``type`` :attr:`~.NotificationType.NTFY`.
:param str ntfypassword: 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 str, optional ntfytopic: Notification option for ``type`` :attr:`~.NotificationType.NTFY`.
:param int, optional ntfyPriority: 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, 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 octopushVersion: Notification option for ``type`` :attr:`~.NotificationType.OCTOPUSH`.
:param str, optional octopushAPIKey: 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`. :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 pushoverpriority: Notification option for ``type`` :attr:`~.NotificationType.PUSHOVER`.
:param str pushovertitle: 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 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 pushyAPIKey: Notification option for ``type`` :attr:`~.NotificationType.PUSHY`.
:param str, optional pushyToken: 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`. :param str rocketchannel: Notification option for ``type`` :attr:`~.NotificationType.ROCKET_CHAT`.

View file

@ -54,3 +54,6 @@ class MonitorType(str, Enum):
REDIS = "redis" REDIS = "redis"
"""Redis""" """Redis"""
GROUP = "group"
"""Group"""

View file

@ -259,12 +259,14 @@ notification_provider_options = {
mattermosticonurl=dict(type="str", required=False), mattermosticonurl=dict(type="str", required=False),
), ),
NotificationType.NTFY: dict( NotificationType.NTFY: dict(
ntfyAuthenticationMethod=dict(type="str", required=False),
ntfyusername=dict(type="str", required=False), ntfyusername=dict(type="str", required=False),
ntfypassword=dict(type="str", required=False), ntfypassword=dict(type="str", required=False),
ntfyaccesstoken=dict(type="str", required=False),
ntfytopic=dict(type="str", required=True), ntfytopic=dict(type="str", required=True),
ntfyPriority=dict(type="int", required=True), ntfyPriority=dict(type="int", required=True),
ntfyIcon=dict(type="str", required=False),
ntfyserverurl=dict(type="str", required=True), ntfyserverurl=dict(type="str", required=True),
ntfyIcon=dict(type="str", required=False),
), ),
NotificationType.OCTOPUSH: dict( NotificationType.OCTOPUSH: dict(
octopushVersion=dict(type="str", required=False), octopushVersion=dict(type="str", required=False),
@ -317,6 +319,7 @@ notification_provider_options = {
pushoverpriority=dict(type="str", required=False), pushoverpriority=dict(type="str", required=False),
pushovertitle=dict(type="str", required=False), pushovertitle=dict(type="str", required=False),
pushoverdevice=dict(type="str", required=False), pushoverdevice=dict(type="str", required=False),
pushoverttl=dict(type="int", required=False),
), ),
NotificationType.PUSHY: dict( NotificationType.PUSHY: dict(
pushyAPIKey=dict(type="str", required=True), pushyAPIKey=dict(type="str", required=True),
@ -436,6 +439,9 @@ notification_provider_conditions = dict(
min=1, min=1,
max=5, max=5,
), ),
pushoverttl=dict(
min=0,
),
smseaglePriority=dict( smseaglePriority=dict(
min=0, min=0,
max=9, max=9,