Coverage for src/edwh_uptime_plugin/uptime_plugin.py: 0%

1 import json

2 from typing import Optional

3

4 import edwh

5 from invoke import Context, task

6

7 from .uptimerobot import MonitorType, UptimeRobotMonitor, uptime_robot

8

9

10@task()

11 def status(_: Context, url: str) -> None:

12 """

13 Show a specific monitor by (partial) url or label.

14

15 :param url: required positional argument of the URL to show the status for

16 """

17 monitors = uptime_robot.get_monitors(url)

18 if not monitors:

19 print("No monitor found!")

20 return

21

22 for monitor in monitors:

23 print(f"- {monitor['url']}:", uptime_robot.format_status(monitor["status"]))

24

25

26@task(name="monitors")

27 def monitors_verbose(_: Context, search: str = "") -> None:

28 """

29 Show all monitors full data as dict.

30 You can optionally add a search term, which will look in the URL and label.

31

32 :param search: (partial) URL or monitor name to filter by

33 """

34 print(json.dumps(uptime_robot.get_monitors(search), indent=2))

35

36

37@task(name="list")

38 def list_statuses(_: Context, search: str = "") -> None:

39 """

40 Show the status for each monitor.

41

42 :param search: (partial) URL or monitor name to filter by

43 """

44 for monitor in uptime_robot.get_monitors(search):

45 print(f"- {monitor['url']}:", uptime_robot.format_status(monitor["status"]))

46

47

48@task()

49 def up(_: Context, strict: bool = False) -> None:

50 """

51 List monitors that are up (probably).

52

53 :param strict: If strict is True, only status 2 is allowed

54 """

55 min_status = 2 if strict else 0

56 max_status = 3

57

58 monitors = uptime_robot.get_monitors()

59 monitors = [_ for _ in monitors if min_status <= _["status"] < max_status]

60

61 for monitor in monitors:

62 print(f"- {monitor['url']}:", uptime_robot.format_status(monitor["status"]))

63

64

65@task()

66 def down(_: Context, strict: bool = False) -> None:

67 """

68 List monitors that are down (probably).

69

70 :param strict: If strict is True, 'seems down' is ignored

71 """

72 min_status = 9 if strict else 8

73

74 monitors = uptime_robot.get_monitors()

75 monitors = [_ for _ in monitors if _["status"] >= min_status]

76

77 for monitor in monitors:

78 print(f"- {monitor['url']}:", uptime_robot.format_status(monitor["status"]))

79

80

81 def extract_friendly_name(url: str) -> str:

82 name = url.split("/")[2]

83

84 return name.removesuffix(".edwh.nl").removesuffix(".meteddie.nl").removeprefix("www.")

85

86

87 def normalize_url(url: str) -> tuple[str, str]:

88 if not url.startswith(("https://", "http://")):

89 if "://" in url:

90 protocol = url.split("://")[0]

91 raise ValueError(f"protocol {protocol} not supported, please use http(s)://")

92 url = f"https://{url}"

93

94 # search for existing and confirm:

95 domain = url.split("/")[2]

96

97 return url, domain

98

99

100@task(aliases=("create",))

101 def add(_: Context, url: str, friendly_name: str = "") -> None:

102 """

103 Create a new monitor.

104 Requires a positional argument 'url' and an optional --friendly-name label

105

106 :param url: Which domain name to add

107 :param friendly_name: Human-readable label (defaults to part of URL)

108 """

109 url, domain = normalize_url(url)

110

111 if existing := uptime_robot.get_monitors(domain):

112 print("A similar domain was already added:")

113 for monitor in existing:

114 print(monitor["friendly_name"], monitor["url"])

115 if not edwh.confirm("Are you sure you want to continue? [yN]", default=False):

116 return

117

118 friendly_name = friendly_name or extract_friendly_name(url)

119

120 monitor_id = uptime_robot.new_monitor(

121 friendly_name,

122 url,

123 )

124

125 if not monitor_id:

126 print("No monitor was added")

127 else:

128 print(f"Monitor '{friendly_name}' was added: {monitor_id}")

129

130

131 def select_monitor(url: str) -> UptimeRobotMonitor | None:

132 """

133 Interactively select a monitor by url.

134

135 :param url: Which domain name to select

136 :return: Selected monitor

137 """

138 monitors = uptime_robot.get_monitors(url)

139 if not monitors:

140 print(f"No such monitor could be found {url}")

141 return None

142 if len(monitors) > 1:

143 print(f"Ambiguous url {url} could mean:")

144 for idx, monitor in enumerate(monitors):

145 print(idx + 1, monitor["friendly_name"], monitor["url"])

146

147 print("0", "Exit")

148

149 _which_one = input("Which monitor would you like to select? ")

150 if not _which_one.isdigit():

151 print(f"Invalid number {_which_one}!")

152 return None

153

154 which_one = int(_which_one)

155 if which_one > len(monitors):

156 print(f"Invalid selection {which_one}!")

157 return None

158

159 elif which_one == 0:

160 return None

161 else:

162 # zero-index:

163 which_one -= 1

164

165 else:

166 which_one = 0

167

168 return monitors[which_one]

169

170

171@task(aliases=("delete",))

172 def remove(_: Context, url: str) -> None:

173 """

174 Remove a specific monitor by url.

175

176 :param url: Which domain name to remove

177 """

178 if not (monitor := select_monitor(url)):

179 return

180

181 monitor_id = monitor["id"]

182

183 if uptime_robot.delete_monitor(monitor_id):

184 print(f"Monitor {monitor['friendly_name']} removed!")

185 else:

186 print(f"Monitor {monitor['friendly_name']} could not be deleted.")

187

188

189@task(aliases=("update",))

190 def edit(_: Context, url: str, friendly_name: Optional[str] = None) -> None:

191 """

192 Edit a specific monitor by url.

193

194 :param url: Which domain name to edit

195 :param friendly_name: new human-readable label

196 """

197 monitor = select_monitor(url)

198 if monitor is None:

199 return

200

201 monitor_id = monitor["id"]

202

203 url, _domain = normalize_url(url)

204

205 # Here you can define the new data for the monitor

206 new_data = {

207 "url": url,

208 "friendly_name": friendly_name or monitor.get("friendly_name", ""),

209 "monitor_type": monitor.get("type", MonitorType.HTTP), # todo: support more?

210 # ...

211 }

212

213 if uptime_robot.edit_monitor(monitor_id, new_data):

214 print(f"Monitor {monitor['friendly_name']} updated!")

215 else:

216 print(f"Monitor {monitor['friendly_name']} could not be updated.")

217

218

219@task()

220 def reset(_: Context, url: str) -> None:

221 """

222 Reset a specific monitor by url.

223

224 :param url: Which domain name to reset

225 """

226 if not (monitor := select_monitor(url)):

227 return

228

229 monitor_id = monitor["id"]

230

231 if uptime_robot.reset_monitor(monitor_id):

232 print(f"Monitor {monitor['friendly_name']} reset!")

233 else:

234 print(f"Monitor {monitor['friendly_name']} could not be reset.")

235

236

237@task()

238 def account(_: Context) -> None:

239 """

240 Show information about the acccount related to the current API key.

241 """

242

243 print(json.dumps(uptime_robot.get_account_details(), indent=2))