uptimer-python-sdk 0.3.0__tar.gz → 1.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uptimer_python_sdk-1.5.0/CHANGELOG.md +74 -0
- uptimer_python_sdk-1.5.0/PKG-INFO +287 -0
- uptimer_python_sdk-1.5.0/README.md +271 -0
- uptimer_python_sdk-1.5.0/Taskfile.yml +35 -0
- uptimer_python_sdk-1.5.0/examples/01_client_setup.py +21 -0
- uptimer_python_sdk-1.5.0/examples/02_list_workspaces.py +10 -0
- uptimer_python_sdk-1.5.0/examples/03_list_locations.py +13 -0
- uptimer_python_sdk-1.5.0/examples/04_create_website_monitor.py +43 -0
- uptimer_python_sdk-1.5.0/examples/05_open_incidents.py +26 -0
- uptimer_python_sdk-1.5.0/examples/README.md +33 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/pyproject.toml +8 -3
- uptimer_python_sdk-1.5.0/src/uptimer/__init__.py +12 -0
- uptimer_python_sdk-1.5.0/src/uptimer/client.py +69 -0
- uptimer_python_sdk-1.5.0/src/uptimer/compat.py +58 -0
- uptimer_python_sdk-1.5.0/src/uptimer/endpoints/incidents.py +37 -0
- uptimer_python_sdk-0.3.0/src/uptimer/endpoints/regions.py → uptimer_python_sdk-1.5.0/src/uptimer/endpoints/locations.py +7 -6
- uptimer_python_sdk-1.5.0/src/uptimer/endpoints/v2.py +38 -0
- uptimer_python_sdk-1.5.0/src/uptimer/endpoints/websites.py +116 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/workspaces.py +4 -3
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/errors.py +18 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/http.py +12 -1
- uptimer_python_sdk-1.5.0/src/uptimer/models/__init__.py +29 -0
- uptimer_python_sdk-1.5.0/src/uptimer/models/v2/__init__.py +70 -0
- uptimer_python_sdk-1.5.0/src/uptimer/models/v2/deserialize.py +119 -0
- uptimer_python_sdk-1.5.0/src/uptimer/models/v2/incident.py +39 -0
- uptimer_python_sdk-1.5.0/src/uptimer/models/v2/location.py +16 -0
- uptimer_python_sdk-1.5.0/src/uptimer/models/v2/monitor.py +88 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/conftest.py +2 -3
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/test_workspaces.py +4 -4
- uptimer_python_sdk-1.5.0/tests/test_client.py +114 -0
- uptimer_python_sdk-1.5.0/tests/test_integration_v2.py +274 -0
- uptimer_python_sdk-1.5.0/tests/test_model_namespace.py +118 -0
- uptimer_python_sdk-1.5.0/tests/test_v2_endpoints.py +134 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/test_workspaces_endpoint.py +2 -2
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/uv.lock +569 -567
- uptimer_python_sdk-0.3.0/CHANGELOG.md +0 -19
- uptimer_python_sdk-0.3.0/PKG-INFO +0 -203
- uptimer_python_sdk-0.3.0/README.md +0 -190
- uptimer_python_sdk-0.3.0/examples/01_client_setup.py +0 -14
- uptimer_python_sdk-0.3.0/examples/02_list_workspaces.py +0 -12
- uptimer_python_sdk-0.3.0/examples/03_list_regions.py +0 -12
- uptimer_python_sdk-0.3.0/examples/04_list_rules.py +0 -13
- uptimer_python_sdk-0.3.0/examples/05_get_rule.py +0 -13
- uptimer_python_sdk-0.3.0/examples/06_create_rule.py +0 -35
- uptimer_python_sdk-0.3.0/examples/07_update_rule.py +0 -37
- uptimer_python_sdk-0.3.0/examples/08_delete_rule.py +0 -13
- uptimer_python_sdk-0.3.0/examples/README.md +0 -40
- uptimer_python_sdk-0.3.0/src/uptimer/__init__.py +0 -3
- uptimer_python_sdk-0.3.0/src/uptimer/client.py +0 -26
- uptimer_python_sdk-0.3.0/src/uptimer/endpoints/rules.py +0 -75
- uptimer_python_sdk-0.3.0/src/uptimer/endpoints/v1.py +0 -17
- uptimer_python_sdk-0.3.0/src/uptimer/models/__init__.py +0 -47
- uptimer_python_sdk-0.3.0/src/uptimer/models/deserialize.py +0 -121
- uptimer_python_sdk-0.3.0/src/uptimer/models/region.py +0 -9
- uptimer_python_sdk-0.3.0/src/uptimer/models/rule.py +0 -57
- uptimer_python_sdk-0.3.0/tests/test_client.py +0 -37
- uptimer_python_sdk-0.3.0/tests/test_regions_endpoint.py +0 -46
- uptimer_python_sdk-0.3.0/tests/test_rules_endpoint.py +0 -535
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.gitignore +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.pre-commit-config.yaml +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/extensions.json +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/launch.json +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/settings.json +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/.vscode/tasks.json +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/LICENSE +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/NOTICE +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/examples/__init__.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/__init__.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/endpoints/endpoint.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/models/errors.py +0 -0
- {uptimer_python_sdk-0.3.0/src/uptimer/models → uptimer_python_sdk-1.5.0/src/uptimer/models/v2}/workspace.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/src/uptimer/py.typed +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/__init__.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/conftest.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/integrations/__init__.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tests/test_endpoint.py +0 -0
- {uptimer_python_sdk-0.3.0 → uptimer_python_sdk-1.5.0}/tools/mypy-diff.sh +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
## 1.5.0 (2026-08-20)
|
|
2
|
+
|
|
3
|
+
### BREAKING CHANGE
|
|
4
|
+
|
|
5
|
+
- Targets Uptimer **API v2 only**. The `client.v1` namespace, its models and its
|
|
6
|
+
kinds are gone. Code written against 0.4.x keeps working **against the
|
|
7
|
+
server** — API v1 is unchanged and supported — but must pin
|
|
8
|
+
`uptimer-python-sdk<1`.
|
|
9
|
+
- The version now tracks the uptimer release it targets, so this is 1.5.0 rather
|
|
10
|
+
than 1.0.0: 1.5.x speaks to uptimer 1.5.0 and later. Patch numbers stay
|
|
11
|
+
independent (product Decision 0013).
|
|
12
|
+
|
|
13
|
+
### Feat
|
|
14
|
+
|
|
15
|
+
- `client.v2.workspaces`, `client.v2.locations`, `client.v2.incidents` and
|
|
16
|
+
`client.v2.monitoring.websites` replace the v1 namespace. The API version stays
|
|
17
|
+
visible in the SDK, as it was in 0.4.x — there are no root-level aliases.
|
|
18
|
+
- **Types are versioned too:** import them from `uptimer.models.v2`
|
|
19
|
+
(`Location`, `Incident`, `Workspace`, the website-monitor classes, the
|
|
20
|
+
`AGREEMENT_*` / `STATUS_*` constants and the `from_api*` helpers). They are no
|
|
21
|
+
longer exported from `uptimer.models`, and there are no flat aliases. The
|
|
22
|
+
deserialization exceptions stay on `uptimer.models`, being
|
|
23
|
+
version-independent.
|
|
24
|
+
- `client.v2.incidents` reads **open** incidents, with the same five status words
|
|
25
|
+
the Uptimer screens use — `problem`, `pending`, `recovering`, `no_data`, `ok`.
|
|
26
|
+
`pending` means failing but inside the confirm hold: nobody has been notified
|
|
27
|
+
yet.
|
|
28
|
+
- Website monitors carry `agreement` (`any`, `majority`, `all`) — how many
|
|
29
|
+
locations must report a problem before the monitor does. Omitting it on update
|
|
30
|
+
keeps the stored value.
|
|
31
|
+
- `client.check_compatibility()` refuses a server that predates API v2 with a
|
|
32
|
+
message naming the fix, instead of a bare 404 on the first call.
|
|
33
|
+
|
|
34
|
+
### Migration
|
|
35
|
+
|
|
36
|
+
| 0.4.x | 1.5.0 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `client.v1.workspaces` | `client.v2.workspaces` |
|
|
39
|
+
| `client.v1.regions` | `client.v2.locations` |
|
|
40
|
+
| `client.v1.rules` | `client.v2.monitoring.websites` |
|
|
41
|
+
| `Region` | `Location` |
|
|
42
|
+
| `Rule` / `CreateRuleRequest` | `WebsiteMonitor` / `CreateWebsiteMonitorRequest` |
|
|
43
|
+
| `regions=[...]` | `locations=[...]` |
|
|
44
|
+
| — | `agreement=...`, `client.v2.incidents` |
|
|
45
|
+
| `from uptimer.models import …` | `from uptimer.models.v2 import …` |
|
|
46
|
+
|
|
47
|
+
`client.version()` and the compatibility helpers are unchanged and stay on the
|
|
48
|
+
client itself: `/version` is a shared global endpoint, not a versioned one.
|
|
49
|
+
|
|
50
|
+
## 0.4.0 (2026-07-15)
|
|
51
|
+
|
|
52
|
+
### Feat
|
|
53
|
+
|
|
54
|
+
- assign regions (by name) when creating or updating a rule via `rules.create`/`rules.update`; rules now expose a `regions` field
|
|
55
|
+
|
|
56
|
+
## 0.3.0 (2025-08-19)
|
|
57
|
+
|
|
58
|
+
### Feat
|
|
59
|
+
|
|
60
|
+
- self-hosted client now requires a base_url, added cloud client, updated docs
|
|
61
|
+
|
|
62
|
+
## 0.2.0 (2025-08-10)
|
|
63
|
+
|
|
64
|
+
### Feat
|
|
65
|
+
|
|
66
|
+
- create/update/delete rule methods.
|
|
67
|
+
- added method to get all rules
|
|
68
|
+
- added regions and get rule API
|
|
69
|
+
- added getting workspace list
|
|
70
|
+
- add Cursor IDE configuration for testing and development
|
|
71
|
+
|
|
72
|
+
### Fix
|
|
73
|
+
|
|
74
|
+
- fixed dependencies in pyproject.toml
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: uptimer-python-sdk
|
|
3
|
+
Version: 1.5.0
|
|
4
|
+
Summary: A Python SDK for hosted and self-hosted Uptimer
|
|
5
|
+
Project-URL: Homepage, https://myuptime.info
|
|
6
|
+
Project-URL: Documentation, https://uptimer.myuptime.info/latest/reference/python-sdk/
|
|
7
|
+
Project-URL: Repository, https://github.com/myuptime-info/uptimer-python-sdk
|
|
8
|
+
Project-URL: Changelog, https://myuptime.info/product-updates
|
|
9
|
+
Author-email: Roman Zadoev <zadoev@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: NOTICE
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Requires-Dist: httpx>=0.28.1
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Uptimer Python SDK
|
|
18
|
+
|
|
19
|
+
A Python SDK for hosted and self-hosted Uptimer.
|
|
20
|
+
|
|
21
|
+
* [Hosted Uptimer](https://myuptime.info)
|
|
22
|
+
* [Self-hosted documentation](https://uptimer.myuptime.info)
|
|
23
|
+
* [PyPI package](https://pypi.org/project/uptimer-python-sdk/)
|
|
24
|
+
* [Uptimer resources](https://myuptime.info/resources)
|
|
25
|
+
* [Product updates](https://myuptime.info/product-updates)
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
30
|
+
|
|
31
|
+
For third-party license information, see the [NOTICE](NOTICE) file.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```shell
|
|
36
|
+
pip install uptimer-python-sdk
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
or
|
|
40
|
+
```shell
|
|
41
|
+
uv add uptimer-python-sdk
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Create client
|
|
47
|
+
|
|
48
|
+
#### self-hosted
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from uptimer.client import UptimerClient
|
|
52
|
+
client = UptimerClient(
|
|
53
|
+
api_key="your-api-key-here",
|
|
54
|
+
base_url="http://127.0.0.1:2517/api", # or your custom base URL
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
#### cloud
|
|
59
|
+
```python
|
|
60
|
+
from uptimer.client import UptimerCloudClient
|
|
61
|
+
client = UptimerCloudClient(
|
|
62
|
+
api_key="your-api-key-here",
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Basic example
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from uptimer.client import UptimerClient
|
|
70
|
+
from uptimer.errors import (
|
|
71
|
+
DefaultUptimerApiError,
|
|
72
|
+
IncompatibleServerError,
|
|
73
|
+
UptimerError,
|
|
74
|
+
UptimerInvalidHttpCodeError,
|
|
75
|
+
)
|
|
76
|
+
from uptimer.models.v2 import (
|
|
77
|
+
AGREEMENT_MAJORITY,
|
|
78
|
+
CreateWebsiteMonitorRequest,
|
|
79
|
+
UpdateWebsiteMonitorRequest,
|
|
80
|
+
WebsiteMonitorRequest,
|
|
81
|
+
WebsiteMonitorResponse,
|
|
82
|
+
WebsiteMonitorResponseBody,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
client = UptimerClient(
|
|
86
|
+
api_key="your-api-key-here",
|
|
87
|
+
base_url="http://127.0.0.1:2517/api", # or your custom base URL
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Optional: fail fast with a message that names the fix, rather than a 404 on
|
|
91
|
+
# the first real call.
|
|
92
|
+
print("server:", client.check_compatibility())
|
|
93
|
+
|
|
94
|
+
workspace = client.v2.workspaces.all()[0]
|
|
95
|
+
locations = [location.name for location in client.v2.locations.all()]
|
|
96
|
+
|
|
97
|
+
monitor = client.v2.monitoring.websites.create(
|
|
98
|
+
CreateWebsiteMonitorRequest(
|
|
99
|
+
name="Checkout API",
|
|
100
|
+
interval=60, # seconds between probes
|
|
101
|
+
workspace_id=workspace.id,
|
|
102
|
+
request=WebsiteMonitorRequest(
|
|
103
|
+
url="https://checkout.example/health",
|
|
104
|
+
method="GET", # one of GET, POST, PATCH, OPTIONS
|
|
105
|
+
content_type="application/json",
|
|
106
|
+
data="",
|
|
107
|
+
),
|
|
108
|
+
response=WebsiteMonitorResponse(
|
|
109
|
+
statuses=[200, 201], # any of these means the site is up
|
|
110
|
+
body=WebsiteMonitorResponseBody(content="ok"), # expected substring
|
|
111
|
+
),
|
|
112
|
+
locations=locations,
|
|
113
|
+
# How many locations must report a problem before this monitor does:
|
|
114
|
+
# "any", "majority" or "all". Omit to keep the server default.
|
|
115
|
+
agreement=AGREEMENT_MAJORITY,
|
|
116
|
+
),
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
monitor = client.v2.monitoring.websites.update(
|
|
120
|
+
monitor.id,
|
|
121
|
+
UpdateWebsiteMonitorRequest(
|
|
122
|
+
name="Checkout API",
|
|
123
|
+
interval=120,
|
|
124
|
+
request=WebsiteMonitorRequest(url="https://checkout.example/health", method="GET"),
|
|
125
|
+
response=WebsiteMonitorResponse(statuses=[200]),
|
|
126
|
+
locations=locations,
|
|
127
|
+
# Omitting agreement here keeps the stored one.
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# What is wrong right now. Only open incidents come back.
|
|
132
|
+
for incident in client.v2.incidents.all(workspace.id):
|
|
133
|
+
print(incident.monitor_name, incident.status, incident.locations.failing)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
client.v2.monitoring.websites.delete(monitor.id)
|
|
137
|
+
except DefaultUptimerApiError as e:
|
|
138
|
+
# error responses from the uptimer server
|
|
139
|
+
print(
|
|
140
|
+
e.message, # user message
|
|
141
|
+
e.code, # error id
|
|
142
|
+
e.error_type, # class of error
|
|
143
|
+
e.details, # detailed message for a developer
|
|
144
|
+
)
|
|
145
|
+
except IncompatibleServerError as e:
|
|
146
|
+
# the server does not provide API v2 — see Migrating from 0.4.x below
|
|
147
|
+
print(e)
|
|
148
|
+
except UptimerInvalidHttpCodeError as e:
|
|
149
|
+
# the uptimer api always returns 200; anything else is a transport error.
|
|
150
|
+
# a 404 really is "no such URL", not "no object with that id".
|
|
151
|
+
print(e.url, e.status_code)
|
|
152
|
+
except UptimerError: # base error, if you need one
|
|
153
|
+
raise
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Incident status
|
|
157
|
+
|
|
158
|
+
`client.v2.incidents.all()` returns only **open** incidents. `status` carries the
|
|
159
|
+
same words the Uptimer screens show, so a client and the UI cannot disagree:
|
|
160
|
+
|
|
161
|
+
| status | meaning |
|
|
162
|
+
|---|---|
|
|
163
|
+
| `problem` | confirmed, and notifications have gone out |
|
|
164
|
+
| `pending` | failing, but inside the confirm hold — **nobody has been notified yet** |
|
|
165
|
+
| `recovering` | reporting ok again while the incident is still open |
|
|
166
|
+
| `no_data` | nothing usable arrived; a silent location counts toward the agreement |
|
|
167
|
+
| `ok` | healthy |
|
|
168
|
+
|
|
169
|
+
`locations.failing` / `.unknown` / `.ok` is the evidence the verdict was taken
|
|
170
|
+
from. A location that has never reported stays in `unknown` — that is a real
|
|
171
|
+
state, not a missing one.
|
|
172
|
+
|
|
173
|
+
### Migrating from 0.4.x
|
|
174
|
+
|
|
175
|
+
**1.5.0 targets API v2 only.** Your existing 0.4.x code keeps working against
|
|
176
|
+
the server — API v1 is unchanged and supported — but it must stay on the 0.4.x
|
|
177
|
+
SDK. Pin `uptimer-python-sdk<1` if you are not ready to move.
|
|
178
|
+
|
|
179
|
+
What changed:
|
|
180
|
+
|
|
181
|
+
| 0.4.x (API v1) | 1.5.0 (API v2) |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `client.v1.workspaces` | `client.v2.workspaces` |
|
|
184
|
+
| `client.v1.regions` | `client.v2.locations` |
|
|
185
|
+
| `client.v1.rules` | `client.v2.monitoring.websites` |
|
|
186
|
+
| `Region` | `Location` |
|
|
187
|
+
| `Rule`, `CreateRuleRequest` | `WebsiteMonitor`, `CreateWebsiteMonitorRequest` |
|
|
188
|
+
| `regions=[...]` | `locations=[...]` |
|
|
189
|
+
| — | `agreement="any"|"majority"|"all"` |
|
|
190
|
+
| — | `client.v2.incidents` |
|
|
191
|
+
| `from uptimer.models import …` | `from uptimer.models.v2 import …` |
|
|
192
|
+
|
|
193
|
+
**The version namespace stays, and now covers the types too.** As in 0.4.x,
|
|
194
|
+
resources sit under the API version that serves them — `client.v1.*` becomes
|
|
195
|
+
`client.v2.*`, not a bare `client.*` — and the models follow: import them from
|
|
196
|
+
`uptimer.models.v2`, not from `uptimer.models`. The HTTP API is versioned by
|
|
197
|
+
path, so the SDK shows the same thing rather than hiding it. There are no
|
|
198
|
+
root-level aliases for either surface, so a stale flat import fails loudly
|
|
199
|
+
instead of silently binding to the wrong thing.
|
|
200
|
+
|
|
201
|
+
The deserialization exceptions (`ModelError`, `TypeMismatchError`, …) stay on
|
|
202
|
+
`uptimer.models`: the same error is raised whichever API version produced the
|
|
203
|
+
payload, so versioning them would say something untrue.
|
|
204
|
+
|
|
205
|
+
Why `monitoring.websites` rather than `monitors`: website monitoring is a
|
|
206
|
+
built-in template, not the general model. Keeping the bare name free lets other
|
|
207
|
+
monitor types arrive later without renaming this one.
|
|
208
|
+
|
|
209
|
+
`client.version()`, `client.check_compatibility()` and
|
|
210
|
+
`client.ensure_compatible()` are unchanged and stay on the client itself —
|
|
211
|
+
`/version` is a shared global endpoint, not a versioned one, so it works against
|
|
212
|
+
any server, including one too old for the rest of this SDK.
|
|
213
|
+
|
|
214
|
+
**Why 1.5.0 and not 1.0.0:** the SDK's major.minor tracks the uptimer release it
|
|
215
|
+
targets, so the version is the compatibility statement — 1.5.x speaks to uptimer
|
|
216
|
+
1.5.0 and later. Patch numbers are independent, so an SDK fix can ship without a
|
|
217
|
+
server release.
|
|
218
|
+
|
|
219
|
+
Also, check out the [examples directory](https://github.com/myuptime-info/uptimer-python-sdk/tree/main/examples).
|
|
220
|
+
|
|
221
|
+
### Development Setup
|
|
222
|
+
|
|
223
|
+
1. Clone the repository:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
git clone <repository-url>
|
|
227
|
+
cd uptimer-python-sdk
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
2. Install dependencies:
|
|
231
|
+
|
|
232
|
+
```bash
|
|
233
|
+
uv sync --dev
|
|
234
|
+
# for integration tests
|
|
235
|
+
uv run playwright install chromium
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
3. Run tests:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
uv run pytest
|
|
242
|
+
# integration
|
|
243
|
+
docker pull ghcr.io/myuptime-info/uptimer:1.3.0
|
|
244
|
+
docker run -p 2517:2517 ghcr.io/myuptime-info/uptimer:1.3.0
|
|
245
|
+
UPTIMER_URL=http://localhost:2517 uv run --integration
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
4. Run linting:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
uv run ruff check .
|
|
252
|
+
uv run mypy src
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
5. Format code:
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
uv run ruff format .
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
6. Run pre-commit hooks:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv run pre-commit run --all-files
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Third-Party Licenses
|
|
268
|
+
|
|
269
|
+
This project uses the following third-party libraries:
|
|
270
|
+
|
|
271
|
+
### Production Dependencies
|
|
272
|
+
|
|
273
|
+
- **httpx** (BSD 3-Clause License) - HTTP client for Python
|
|
274
|
+
|
|
275
|
+
### Development Dependencies
|
|
276
|
+
|
|
277
|
+
- **mypy** (Apache 2.0 License) - Static type checker
|
|
278
|
+
- **playwright** (Apache 2.0 License) - Browser automation
|
|
279
|
+
- **pre-commit** (MIT License) - Git hooks framework
|
|
280
|
+
- **pytest** (MIT License) - Testing framework
|
|
281
|
+
- **pytest-cov** (MIT License) - Coverage plugin for pytest
|
|
282
|
+
- **pytest-httpx** (MIT License) - HTTPX plugin for pytest
|
|
283
|
+
- **pytest-playwright** (MIT License) - Playwright plugin for pytest
|
|
284
|
+
- **responses** (Apache 2.0 License) - Mock library for requests
|
|
285
|
+
- **ruff** (MIT License) - Fast Python linter and formatter
|
|
286
|
+
|
|
287
|
+
All third-party licenses are compatible with the MIT License used by this project. Note that the BSD 3-Clause License (used by httpx) includes an additional restriction prohibiting the use of the copyright holder's name for endorsement without permission.
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# Uptimer Python SDK
|
|
2
|
+
|
|
3
|
+
A Python SDK for hosted and self-hosted Uptimer.
|
|
4
|
+
|
|
5
|
+
* [Hosted Uptimer](https://myuptime.info)
|
|
6
|
+
* [Self-hosted documentation](https://uptimer.myuptime.info)
|
|
7
|
+
* [PyPI package](https://pypi.org/project/uptimer-python-sdk/)
|
|
8
|
+
* [Uptimer resources](https://myuptime.info/resources)
|
|
9
|
+
* [Product updates](https://myuptime.info/product-updates)
|
|
10
|
+
|
|
11
|
+
## License
|
|
12
|
+
|
|
13
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
14
|
+
|
|
15
|
+
For third-party license information, see the [NOTICE](NOTICE) file.
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
pip install uptimer-python-sdk
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
or
|
|
24
|
+
```shell
|
|
25
|
+
uv add uptimer-python-sdk
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Create client
|
|
31
|
+
|
|
32
|
+
#### self-hosted
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from uptimer.client import UptimerClient
|
|
36
|
+
client = UptimerClient(
|
|
37
|
+
api_key="your-api-key-here",
|
|
38
|
+
base_url="http://127.0.0.1:2517/api", # or your custom base URL
|
|
39
|
+
)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
#### cloud
|
|
43
|
+
```python
|
|
44
|
+
from uptimer.client import UptimerCloudClient
|
|
45
|
+
client = UptimerCloudClient(
|
|
46
|
+
api_key="your-api-key-here",
|
|
47
|
+
)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Basic example
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from uptimer.client import UptimerClient
|
|
54
|
+
from uptimer.errors import (
|
|
55
|
+
DefaultUptimerApiError,
|
|
56
|
+
IncompatibleServerError,
|
|
57
|
+
UptimerError,
|
|
58
|
+
UptimerInvalidHttpCodeError,
|
|
59
|
+
)
|
|
60
|
+
from uptimer.models.v2 import (
|
|
61
|
+
AGREEMENT_MAJORITY,
|
|
62
|
+
CreateWebsiteMonitorRequest,
|
|
63
|
+
UpdateWebsiteMonitorRequest,
|
|
64
|
+
WebsiteMonitorRequest,
|
|
65
|
+
WebsiteMonitorResponse,
|
|
66
|
+
WebsiteMonitorResponseBody,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
client = UptimerClient(
|
|
70
|
+
api_key="your-api-key-here",
|
|
71
|
+
base_url="http://127.0.0.1:2517/api", # or your custom base URL
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Optional: fail fast with a message that names the fix, rather than a 404 on
|
|
75
|
+
# the first real call.
|
|
76
|
+
print("server:", client.check_compatibility())
|
|
77
|
+
|
|
78
|
+
workspace = client.v2.workspaces.all()[0]
|
|
79
|
+
locations = [location.name for location in client.v2.locations.all()]
|
|
80
|
+
|
|
81
|
+
monitor = client.v2.monitoring.websites.create(
|
|
82
|
+
CreateWebsiteMonitorRequest(
|
|
83
|
+
name="Checkout API",
|
|
84
|
+
interval=60, # seconds between probes
|
|
85
|
+
workspace_id=workspace.id,
|
|
86
|
+
request=WebsiteMonitorRequest(
|
|
87
|
+
url="https://checkout.example/health",
|
|
88
|
+
method="GET", # one of GET, POST, PATCH, OPTIONS
|
|
89
|
+
content_type="application/json",
|
|
90
|
+
data="",
|
|
91
|
+
),
|
|
92
|
+
response=WebsiteMonitorResponse(
|
|
93
|
+
statuses=[200, 201], # any of these means the site is up
|
|
94
|
+
body=WebsiteMonitorResponseBody(content="ok"), # expected substring
|
|
95
|
+
),
|
|
96
|
+
locations=locations,
|
|
97
|
+
# How many locations must report a problem before this monitor does:
|
|
98
|
+
# "any", "majority" or "all". Omit to keep the server default.
|
|
99
|
+
agreement=AGREEMENT_MAJORITY,
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
monitor = client.v2.monitoring.websites.update(
|
|
104
|
+
monitor.id,
|
|
105
|
+
UpdateWebsiteMonitorRequest(
|
|
106
|
+
name="Checkout API",
|
|
107
|
+
interval=120,
|
|
108
|
+
request=WebsiteMonitorRequest(url="https://checkout.example/health", method="GET"),
|
|
109
|
+
response=WebsiteMonitorResponse(statuses=[200]),
|
|
110
|
+
locations=locations,
|
|
111
|
+
# Omitting agreement here keeps the stored one.
|
|
112
|
+
),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# What is wrong right now. Only open incidents come back.
|
|
116
|
+
for incident in client.v2.incidents.all(workspace.id):
|
|
117
|
+
print(incident.monitor_name, incident.status, incident.locations.failing)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
client.v2.monitoring.websites.delete(monitor.id)
|
|
121
|
+
except DefaultUptimerApiError as e:
|
|
122
|
+
# error responses from the uptimer server
|
|
123
|
+
print(
|
|
124
|
+
e.message, # user message
|
|
125
|
+
e.code, # error id
|
|
126
|
+
e.error_type, # class of error
|
|
127
|
+
e.details, # detailed message for a developer
|
|
128
|
+
)
|
|
129
|
+
except IncompatibleServerError as e:
|
|
130
|
+
# the server does not provide API v2 — see Migrating from 0.4.x below
|
|
131
|
+
print(e)
|
|
132
|
+
except UptimerInvalidHttpCodeError as e:
|
|
133
|
+
# the uptimer api always returns 200; anything else is a transport error.
|
|
134
|
+
# a 404 really is "no such URL", not "no object with that id".
|
|
135
|
+
print(e.url, e.status_code)
|
|
136
|
+
except UptimerError: # base error, if you need one
|
|
137
|
+
raise
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Incident status
|
|
141
|
+
|
|
142
|
+
`client.v2.incidents.all()` returns only **open** incidents. `status` carries the
|
|
143
|
+
same words the Uptimer screens show, so a client and the UI cannot disagree:
|
|
144
|
+
|
|
145
|
+
| status | meaning |
|
|
146
|
+
|---|---|
|
|
147
|
+
| `problem` | confirmed, and notifications have gone out |
|
|
148
|
+
| `pending` | failing, but inside the confirm hold — **nobody has been notified yet** |
|
|
149
|
+
| `recovering` | reporting ok again while the incident is still open |
|
|
150
|
+
| `no_data` | nothing usable arrived; a silent location counts toward the agreement |
|
|
151
|
+
| `ok` | healthy |
|
|
152
|
+
|
|
153
|
+
`locations.failing` / `.unknown` / `.ok` is the evidence the verdict was taken
|
|
154
|
+
from. A location that has never reported stays in `unknown` — that is a real
|
|
155
|
+
state, not a missing one.
|
|
156
|
+
|
|
157
|
+
### Migrating from 0.4.x
|
|
158
|
+
|
|
159
|
+
**1.5.0 targets API v2 only.** Your existing 0.4.x code keeps working against
|
|
160
|
+
the server — API v1 is unchanged and supported — but it must stay on the 0.4.x
|
|
161
|
+
SDK. Pin `uptimer-python-sdk<1` if you are not ready to move.
|
|
162
|
+
|
|
163
|
+
What changed:
|
|
164
|
+
|
|
165
|
+
| 0.4.x (API v1) | 1.5.0 (API v2) |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `client.v1.workspaces` | `client.v2.workspaces` |
|
|
168
|
+
| `client.v1.regions` | `client.v2.locations` |
|
|
169
|
+
| `client.v1.rules` | `client.v2.monitoring.websites` |
|
|
170
|
+
| `Region` | `Location` |
|
|
171
|
+
| `Rule`, `CreateRuleRequest` | `WebsiteMonitor`, `CreateWebsiteMonitorRequest` |
|
|
172
|
+
| `regions=[...]` | `locations=[...]` |
|
|
173
|
+
| — | `agreement="any"|"majority"|"all"` |
|
|
174
|
+
| — | `client.v2.incidents` |
|
|
175
|
+
| `from uptimer.models import …` | `from uptimer.models.v2 import …` |
|
|
176
|
+
|
|
177
|
+
**The version namespace stays, and now covers the types too.** As in 0.4.x,
|
|
178
|
+
resources sit under the API version that serves them — `client.v1.*` becomes
|
|
179
|
+
`client.v2.*`, not a bare `client.*` — and the models follow: import them from
|
|
180
|
+
`uptimer.models.v2`, not from `uptimer.models`. The HTTP API is versioned by
|
|
181
|
+
path, so the SDK shows the same thing rather than hiding it. There are no
|
|
182
|
+
root-level aliases for either surface, so a stale flat import fails loudly
|
|
183
|
+
instead of silently binding to the wrong thing.
|
|
184
|
+
|
|
185
|
+
The deserialization exceptions (`ModelError`, `TypeMismatchError`, …) stay on
|
|
186
|
+
`uptimer.models`: the same error is raised whichever API version produced the
|
|
187
|
+
payload, so versioning them would say something untrue.
|
|
188
|
+
|
|
189
|
+
Why `monitoring.websites` rather than `monitors`: website monitoring is a
|
|
190
|
+
built-in template, not the general model. Keeping the bare name free lets other
|
|
191
|
+
monitor types arrive later without renaming this one.
|
|
192
|
+
|
|
193
|
+
`client.version()`, `client.check_compatibility()` and
|
|
194
|
+
`client.ensure_compatible()` are unchanged and stay on the client itself —
|
|
195
|
+
`/version` is a shared global endpoint, not a versioned one, so it works against
|
|
196
|
+
any server, including one too old for the rest of this SDK.
|
|
197
|
+
|
|
198
|
+
**Why 1.5.0 and not 1.0.0:** the SDK's major.minor tracks the uptimer release it
|
|
199
|
+
targets, so the version is the compatibility statement — 1.5.x speaks to uptimer
|
|
200
|
+
1.5.0 and later. Patch numbers are independent, so an SDK fix can ship without a
|
|
201
|
+
server release.
|
|
202
|
+
|
|
203
|
+
Also, check out the [examples directory](https://github.com/myuptime-info/uptimer-python-sdk/tree/main/examples).
|
|
204
|
+
|
|
205
|
+
### Development Setup
|
|
206
|
+
|
|
207
|
+
1. Clone the repository:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
git clone <repository-url>
|
|
211
|
+
cd uptimer-python-sdk
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
2. Install dependencies:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
uv sync --dev
|
|
218
|
+
# for integration tests
|
|
219
|
+
uv run playwright install chromium
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
3. Run tests:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
uv run pytest
|
|
226
|
+
# integration
|
|
227
|
+
docker pull ghcr.io/myuptime-info/uptimer:1.3.0
|
|
228
|
+
docker run -p 2517:2517 ghcr.io/myuptime-info/uptimer:1.3.0
|
|
229
|
+
UPTIMER_URL=http://localhost:2517 uv run --integration
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
4. Run linting:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
uv run ruff check .
|
|
236
|
+
uv run mypy src
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
5. Format code:
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
uv run ruff format .
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
6. Run pre-commit hooks:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
uv run pre-commit run --all-files
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Third-Party Licenses
|
|
252
|
+
|
|
253
|
+
This project uses the following third-party libraries:
|
|
254
|
+
|
|
255
|
+
### Production Dependencies
|
|
256
|
+
|
|
257
|
+
- **httpx** (BSD 3-Clause License) - HTTP client for Python
|
|
258
|
+
|
|
259
|
+
### Development Dependencies
|
|
260
|
+
|
|
261
|
+
- **mypy** (Apache 2.0 License) - Static type checker
|
|
262
|
+
- **playwright** (Apache 2.0 License) - Browser automation
|
|
263
|
+
- **pre-commit** (MIT License) - Git hooks framework
|
|
264
|
+
- **pytest** (MIT License) - Testing framework
|
|
265
|
+
- **pytest-cov** (MIT License) - Coverage plugin for pytest
|
|
266
|
+
- **pytest-httpx** (MIT License) - HTTPX plugin for pytest
|
|
267
|
+
- **pytest-playwright** (MIT License) - Playwright plugin for pytest
|
|
268
|
+
- **responses** (Apache 2.0 License) - Mock library for requests
|
|
269
|
+
- **ruff** (MIT License) - Fast Python linter and formatter
|
|
270
|
+
|
|
271
|
+
All third-party licenses are compatible with the MIT License used by this project. Note that the BSD 3-Clause License (used by httpx) includes an additional restriction prohibiting the use of the copyright holder's name for endorsement without permission.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Dev + git release tasks for the uptimer Python SDK. All tooling via `uv` (uv.lock).
|
|
2
|
+
# PyPI publish lives in the private product repo: /work → task sdk:publish*
|
|
3
|
+
version: '3'
|
|
4
|
+
|
|
5
|
+
tasks:
|
|
6
|
+
build:
|
|
7
|
+
desc: Build the sdist + wheel into dist/ (uv build).
|
|
8
|
+
cmds:
|
|
9
|
+
- rm -rf dist
|
|
10
|
+
- uv build
|
|
11
|
+
|
|
12
|
+
test:
|
|
13
|
+
desc: Run the test suite (uv run pytest).
|
|
14
|
+
cmds:
|
|
15
|
+
- uv run pytest
|
|
16
|
+
|
|
17
|
+
lint:
|
|
18
|
+
desc: Lint + type-check (ruff + mypy).
|
|
19
|
+
cmds:
|
|
20
|
+
- uv run ruff check .
|
|
21
|
+
- uv run mypy src
|
|
22
|
+
|
|
23
|
+
cq:
|
|
24
|
+
desc: 'Code quality gate: lint + test.'
|
|
25
|
+
cmds:
|
|
26
|
+
- task: lint
|
|
27
|
+
- task: test
|
|
28
|
+
|
|
29
|
+
release:
|
|
30
|
+
desc: 'Cut a release: cq, then `cz bump` (version + CHANGELOG + tag) + push. Commit first. Prefer manual tag when version is already set.'
|
|
31
|
+
cmds:
|
|
32
|
+
- task: cq
|
|
33
|
+
- cz bump --yes
|
|
34
|
+
- git push --follow-tags
|
|
35
|
+
- 'echo ">> released v$(cz version --project) — publish from /work: task sdk:publish:test, then task sdk:publish"'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Set up a client and confirm the server speaks API v2."""
|
|
2
|
+
|
|
3
|
+
from uptimer.client import UptimerClient
|
|
4
|
+
from uptimer.errors import IncompatibleServerError
|
|
5
|
+
|
|
6
|
+
client = UptimerClient(
|
|
7
|
+
api_key="your-api-key-here",
|
|
8
|
+
base_url="https://myuptime.info/api", # or your self-hosted URL + /api
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# Optional but recommended: fail fast with a message that names the fix, rather
|
|
12
|
+
# than a 404 on the first real call.
|
|
13
|
+
try:
|
|
14
|
+
print("Server version:", client.check_compatibility())
|
|
15
|
+
except IncompatibleServerError as exc:
|
|
16
|
+
raise SystemExit(str(exc)) from exc
|
|
17
|
+
|
|
18
|
+
# API v2 lives under client.v2 — the SDK keeps the API version visible, because
|
|
19
|
+
# the API itself is versioned by path.
|
|
20
|
+
print("Available: client.v2.workspaces, client.v2.locations,")
|
|
21
|
+
print(" client.v2.incidents, client.v2.monitoring.websites")
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"""List the workspaces an API key can reach."""
|
|
2
|
+
|
|
3
|
+
from uptimer.client import UptimerClient
|
|
4
|
+
|
|
5
|
+
client = UptimerClient(
|
|
6
|
+
api_key="your-api-key-here", base_url="https://myuptime.info/api"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
for workspace in client.v2.workspaces.all():
|
|
10
|
+
print(f"{workspace.id} {workspace.name} ({workspace.role})")
|