devhelm 1.2.0__tar.gz → 1.4.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.
- {devhelm-1.2.0 → devhelm-1.4.0}/PKG-INFO +2 -1
- {devhelm-1.2.0 → devhelm-1.4.0}/README.md +1 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/docs/openapi/monitoring-api.json +52 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/pyproject.toml +1 -1
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/__init__.py +34 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_errors.py +37 -1
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_http.py +1 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_pagination.py +11 -2
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/client.py +3 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/api_keys.py +9 -1
- devhelm-1.4.0/src/devhelm/resources/dependencies.py +100 -0
- devhelm-1.4.0/src/devhelm/resources/services.py +322 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/types.py +32 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/run_sdk.py +32 -0
- devhelm-1.4.0/tests/test_api_keys.py +76 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_client.py +19 -0
- devhelm-1.4.0/tests/test_dependencies.py +142 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_errors.py +27 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_http.py +46 -2
- devhelm-1.4.0/tests/test_services.py +281 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_spec_parity.py +4 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/uv.lock +1 -1
- devhelm-1.2.0/src/devhelm/resources/dependencies.py +0 -51
- {devhelm-1.2.0 → devhelm-1.4.0}/.github/workflows/ci.yml +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/.github/workflows/release.yml +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/.github/workflows/spec-check.yml +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/.gitignore +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/LICENSE +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/Makefile +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/scripts/emit_response_enums.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/scripts/inject_strict_config.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/scripts/regen-from.sh +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/scripts/release.sh +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/scripts/typegen.sh +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_enums.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_generated.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/_validation.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/py.typed +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/__init__.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/alert_channels.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/deploy_lock.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/environments.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/forensics.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/incidents.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/maintenance_windows.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/monitors.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/notification_policies.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/resource_groups.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/secrets.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/status.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/status_pages.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/tags.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/src/devhelm/resources/webhooks.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/__init__.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_maintenance_windows.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_negative_validation.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_schemas.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_typing.py +0 -0
- {devhelm-1.2.0 → devhelm-1.4.0}/tests/test_validation_helpers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devhelm
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: DevHelm SDK for Python — typed client for monitors, incidents, alerting, and more
|
|
5
5
|
Project-URL: Homepage, https://github.com/devhelmhq/sdk-python
|
|
6
6
|
Project-URL: Repository, https://github.com/devhelmhq/sdk-python.git
|
|
@@ -108,6 +108,7 @@ The client exposes the following resource modules:
|
|
|
108
108
|
| `client.api_keys` | API key management |
|
|
109
109
|
| `client.dependencies` | Service dependency tracking |
|
|
110
110
|
| `client.deploy_lock` | Deploy lock for safe deployments |
|
|
111
|
+
| `client.services` | Status Data catalog: vendor services, components, incidents, uptime |
|
|
111
112
|
| `client.status` | Dashboard overview |
|
|
112
113
|
|
|
113
114
|
## Pagination
|
|
@@ -84,6 +84,7 @@ The client exposes the following resource modules:
|
|
|
84
84
|
| `client.api_keys` | API key management |
|
|
85
85
|
| `client.dependencies` | Service dependency tracking |
|
|
86
86
|
| `client.deploy_lock` | Deploy lock for safe deployments |
|
|
87
|
+
| `client.services` | Status Data catalog: vendor services, components, incidents, uptime |
|
|
87
88
|
| `client.status` | Dashboard overview |
|
|
88
89
|
|
|
89
90
|
## Pagination
|
|
@@ -2274,6 +2274,18 @@
|
|
|
2274
2274
|
],
|
|
2275
2275
|
"summary": "List categories with service counts",
|
|
2276
2276
|
"operationId": "listCategories",
|
|
2277
|
+
"parameters": [
|
|
2278
|
+
{
|
|
2279
|
+
"name": "publishedOnly",
|
|
2280
|
+
"in": "query",
|
|
2281
|
+
"description": "Count only published services (curated public pSEO set); default false",
|
|
2282
|
+
"required": false,
|
|
2283
|
+
"schema": {
|
|
2284
|
+
"type": "boolean",
|
|
2285
|
+
"default": false
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
],
|
|
2277
2289
|
"responses": {
|
|
2278
2290
|
"200": {
|
|
2279
2291
|
"description": "OK",
|
|
@@ -13520,6 +13532,24 @@
|
|
|
13520
13532
|
"type": "boolean"
|
|
13521
13533
|
}
|
|
13522
13534
|
},
|
|
13535
|
+
{
|
|
13536
|
+
"name": "search",
|
|
13537
|
+
"in": "query",
|
|
13538
|
+
"description": "Case-insensitive substring match on service name or slug",
|
|
13539
|
+
"required": false,
|
|
13540
|
+
"schema": {
|
|
13541
|
+
"type": "string"
|
|
13542
|
+
}
|
|
13543
|
+
},
|
|
13544
|
+
{
|
|
13545
|
+
"name": "sort",
|
|
13546
|
+
"in": "query",
|
|
13547
|
+
"description": "Result ordering: 'recent' (default, newest first) or 'curated' (curated/recognizable first)",
|
|
13548
|
+
"required": false,
|
|
13549
|
+
"schema": {
|
|
13550
|
+
"type": "string"
|
|
13551
|
+
}
|
|
13552
|
+
},
|
|
13523
13553
|
{
|
|
13524
13554
|
"name": "cursor",
|
|
13525
13555
|
"in": "query",
|
|
@@ -13661,6 +13691,16 @@
|
|
|
13661
13691
|
"type": "boolean",
|
|
13662
13692
|
"default": false
|
|
13663
13693
|
}
|
|
13694
|
+
},
|
|
13695
|
+
{
|
|
13696
|
+
"name": "publishedOnly",
|
|
13697
|
+
"in": "query",
|
|
13698
|
+
"description": "Resolve only published services (curated public pSEO set); 404 otherwise. Default false",
|
|
13699
|
+
"required": false,
|
|
13700
|
+
"schema": {
|
|
13701
|
+
"type": "boolean",
|
|
13702
|
+
"default": false
|
|
13703
|
+
}
|
|
13664
13704
|
}
|
|
13665
13705
|
],
|
|
13666
13706
|
"responses": {
|
|
@@ -15358,6 +15398,18 @@
|
|
|
15358
15398
|
"summary": "Global status summary across all services",
|
|
15359
15399
|
"description": "Returns aggregate counts of services by status and a list of services currently experiencing issues.",
|
|
15360
15400
|
"operationId": "getGlobalStatusSummary",
|
|
15401
|
+
"parameters": [
|
|
15402
|
+
{
|
|
15403
|
+
"name": "publishedOnly",
|
|
15404
|
+
"in": "query",
|
|
15405
|
+
"description": "Aggregate only published services (curated public pSEO set); default false",
|
|
15406
|
+
"required": false,
|
|
15407
|
+
"schema": {
|
|
15408
|
+
"type": "boolean",
|
|
15409
|
+
"default": false
|
|
15410
|
+
}
|
|
15411
|
+
}
|
|
15412
|
+
],
|
|
15361
15413
|
"responses": {
|
|
15362
15414
|
"200": {
|
|
15363
15415
|
"description": "OK",
|
|
@@ -29,6 +29,7 @@ from devhelm.resources.monitors import Monitors
|
|
|
29
29
|
from devhelm.resources.notification_policies import NotificationPolicies
|
|
30
30
|
from devhelm.resources.resource_groups import ResourceGroups
|
|
31
31
|
from devhelm.resources.secrets import Secrets
|
|
32
|
+
from devhelm.resources.services import Services
|
|
32
33
|
from devhelm.resources.status import Status
|
|
33
34
|
from devhelm.resources.status_pages import StatusPages
|
|
34
35
|
from devhelm.resources.tags import Tags
|
|
@@ -46,8 +47,11 @@ from devhelm.types import (
|
|
|
46
47
|
ApiKeyDto,
|
|
47
48
|
AssertionSeverity,
|
|
48
49
|
AssertionTestResultDto,
|
|
50
|
+
BatchComponentUptimeDto,
|
|
51
|
+
CategoryDto,
|
|
49
52
|
CheckResultDto,
|
|
50
53
|
CheckTraceDto,
|
|
54
|
+
ComponentUptimeDayDto,
|
|
51
55
|
ConfirmationPolicyType,
|
|
52
56
|
CreateAlertChannelRequest,
|
|
53
57
|
CreateApiKeyRequest,
|
|
@@ -69,6 +73,7 @@ from devhelm.types import (
|
|
|
69
73
|
DashboardOverviewDto,
|
|
70
74
|
DeployLockDto,
|
|
71
75
|
EnvironmentDto,
|
|
76
|
+
GlobalStatusSummaryDto,
|
|
72
77
|
IncidentDetailDto,
|
|
73
78
|
IncidentDto,
|
|
74
79
|
IncidentNewStatus,
|
|
@@ -78,6 +83,7 @@ from devhelm.types import (
|
|
|
78
83
|
IncidentStatus,
|
|
79
84
|
IncidentTimelineDto,
|
|
80
85
|
IncidentUpdateCreatedBy,
|
|
86
|
+
LifecycleStatus,
|
|
81
87
|
LinkedIncidentStatus,
|
|
82
88
|
MaintenanceWindowDto,
|
|
83
89
|
MembershipStatus,
|
|
@@ -99,8 +105,18 @@ from devhelm.types import (
|
|
|
99
105
|
ResourceGroupHealthStatus,
|
|
100
106
|
ResourceGroupMemberDto,
|
|
101
107
|
RuleEvaluationDto,
|
|
108
|
+
ScheduledMaintenanceDto,
|
|
102
109
|
SecretDto,
|
|
110
|
+
ServiceCatalogDto,
|
|
111
|
+
ServiceComponentDto,
|
|
112
|
+
ServiceDayDetailDto,
|
|
113
|
+
ServiceDetailDto,
|
|
114
|
+
ServiceIncidentDetailDto,
|
|
115
|
+
ServiceIncidentDto,
|
|
116
|
+
ServiceLiveStatusDto,
|
|
117
|
+
ServiceSubscribeRequest,
|
|
103
118
|
ServiceSubscriptionDto,
|
|
119
|
+
ServiceUptimeResponse,
|
|
104
120
|
StatusPageBranding,
|
|
105
121
|
StatusPageComponentCurrentStatus,
|
|
106
122
|
StatusPageComponentDto,
|
|
@@ -124,6 +140,7 @@ from devhelm.types import (
|
|
|
124
140
|
TriggerRuleSeverity,
|
|
125
141
|
TriggerRuleType,
|
|
126
142
|
UpdateAlertChannelRequest,
|
|
143
|
+
UpdateAlertSensitivityRequest,
|
|
127
144
|
UpdateAssertionSeverity,
|
|
128
145
|
UpdateEnvironmentRequest,
|
|
129
146
|
UpdateMaintenanceWindowRequest,
|
|
@@ -184,6 +201,7 @@ __all__ = [
|
|
|
184
201
|
"Dependencies",
|
|
185
202
|
"DeployLock",
|
|
186
203
|
"MaintenanceWindows",
|
|
204
|
+
"Services",
|
|
187
205
|
"Status",
|
|
188
206
|
"StatusPages",
|
|
189
207
|
# Response DTOs
|
|
@@ -216,6 +234,19 @@ __all__ = [
|
|
|
216
234
|
"ApiKeyDto",
|
|
217
235
|
"ApiKeyCreateResponse",
|
|
218
236
|
"ServiceSubscriptionDto",
|
|
237
|
+
"ServiceCatalogDto",
|
|
238
|
+
"ServiceDetailDto",
|
|
239
|
+
"ServiceLiveStatusDto",
|
|
240
|
+
"ServiceComponentDto",
|
|
241
|
+
"ServiceIncidentDto",
|
|
242
|
+
"ServiceIncidentDetailDto",
|
|
243
|
+
"ServiceUptimeResponse",
|
|
244
|
+
"ServiceDayDetailDto",
|
|
245
|
+
"ScheduledMaintenanceDto",
|
|
246
|
+
"CategoryDto",
|
|
247
|
+
"GlobalStatusSummaryDto",
|
|
248
|
+
"BatchComponentUptimeDto",
|
|
249
|
+
"ComponentUptimeDayDto",
|
|
219
250
|
"MonitorVersionDto",
|
|
220
251
|
"CheckResultDto",
|
|
221
252
|
"DashboardOverviewDto",
|
|
@@ -261,6 +292,8 @@ __all__ = [
|
|
|
261
292
|
"UpdateWebhookEndpointRequest",
|
|
262
293
|
"CreateApiKeyRequest",
|
|
263
294
|
"AcquireDeployLockRequest",
|
|
295
|
+
"ServiceSubscribeRequest",
|
|
296
|
+
"UpdateAlertSensitivityRequest",
|
|
264
297
|
# Enum aliases (descriptive names for codegen-numbered enums)
|
|
265
298
|
"AffectedComponentStatus",
|
|
266
299
|
"AlertDeliveryStatus",
|
|
@@ -272,6 +305,7 @@ __all__ = [
|
|
|
272
305
|
"IncidentSeverity",
|
|
273
306
|
"IncidentStatus",
|
|
274
307
|
"IncidentUpdateCreatedBy",
|
|
308
|
+
"LifecycleStatus",
|
|
275
309
|
"LinkedIncidentStatus",
|
|
276
310
|
"MemberStatus",
|
|
277
311
|
"MembershipStatus",
|
|
@@ -82,6 +82,11 @@ class DevhelmApiError(DevhelmError):
|
|
|
82
82
|
The optional `request_id` field is the per-request id emitted by the
|
|
83
83
|
API as the `X-Request-Id` response header and embedded in the JSON
|
|
84
84
|
error body. Always include it in support tickets.
|
|
85
|
+
|
|
86
|
+
The optional `retry_after` field is the parsed value of the
|
|
87
|
+
`Retry-After` response header in whole seconds. It's populated on
|
|
88
|
+
rate-limit (429) responses that include the header so callers can back
|
|
89
|
+
off for exactly as long as the server asked; ``None`` otherwise.
|
|
85
90
|
"""
|
|
86
91
|
|
|
87
92
|
status: int
|
|
@@ -93,6 +98,7 @@ class DevhelmApiError(DevhelmError):
|
|
|
93
98
|
# narrowing. (Subclasses still inherit the same `str` type.)
|
|
94
99
|
code: str
|
|
95
100
|
request_id: str | None
|
|
101
|
+
retry_after: int | None
|
|
96
102
|
|
|
97
103
|
def __init__(
|
|
98
104
|
self,
|
|
@@ -103,6 +109,7 @@ class DevhelmApiError(DevhelmError):
|
|
|
103
109
|
body: dict[str, Any] | str | None = None,
|
|
104
110
|
code: str | None = None,
|
|
105
111
|
request_id: str | None = None,
|
|
112
|
+
retry_after: int | None = None,
|
|
106
113
|
) -> None:
|
|
107
114
|
super().__init__(message)
|
|
108
115
|
self.status = status
|
|
@@ -113,6 +120,9 @@ class DevhelmApiError(DevhelmError):
|
|
|
113
120
|
# `err.code` is never ``None`` for callers switching on category.
|
|
114
121
|
self.code = code or "API_ERROR"
|
|
115
122
|
self.request_id = request_id
|
|
123
|
+
# Parsed from the `Retry-After` response header (seconds). Populated
|
|
124
|
+
# on 429 / 503 responses that include it; ``None`` otherwise.
|
|
125
|
+
self.retry_after = retry_after
|
|
116
126
|
|
|
117
127
|
|
|
118
128
|
class DevhelmAuthError(DevhelmApiError):
|
|
@@ -152,8 +162,28 @@ class DevhelmTransportError(DevhelmError):
|
|
|
152
162
|
self.__cause__ = cause
|
|
153
163
|
|
|
154
164
|
|
|
165
|
+
def _parse_retry_after(value: str | None) -> int | None:
|
|
166
|
+
"""Parse a ``Retry-After`` header value into whole seconds.
|
|
167
|
+
|
|
168
|
+
The API emits ``Retry-After`` as an integer number of seconds. We parse
|
|
169
|
+
defensively: any non-integer value (an HTTP-date form, or garbage from a
|
|
170
|
+
misbehaving proxy) yields ``None`` rather than raising, so a malformed
|
|
171
|
+
header can never break error construction.
|
|
172
|
+
"""
|
|
173
|
+
if value is None:
|
|
174
|
+
return None
|
|
175
|
+
try:
|
|
176
|
+
return int(value)
|
|
177
|
+
except (TypeError, ValueError):
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
|
|
155
181
|
def error_from_response(
|
|
156
|
-
status: int,
|
|
182
|
+
status: int,
|
|
183
|
+
body: str,
|
|
184
|
+
*,
|
|
185
|
+
request_id: str | None = None,
|
|
186
|
+
retry_after: str | None = None,
|
|
157
187
|
) -> DevhelmApiError:
|
|
158
188
|
"""Map an HTTP error response to a typed DevhelmApiError subclass.
|
|
159
189
|
|
|
@@ -161,6 +191,11 @@ def error_from_response(
|
|
|
161
191
|
pulled out at the call site (rather than re-parsed from the body) so the
|
|
162
192
|
SDK still surfaces the id even when the server returns a non-JSON body
|
|
163
193
|
(e.g. an HTML error page from a misconfigured proxy).
|
|
194
|
+
|
|
195
|
+
`retry_after` is the raw value of the `Retry-After` response header,
|
|
196
|
+
pulled out at the call site for the same reason. It's parsed into whole
|
|
197
|
+
seconds and surfaced as ``err.retry_after`` (e.g. on 429 responses) so
|
|
198
|
+
callers can back off for exactly as long as the server asked.
|
|
164
199
|
"""
|
|
165
200
|
message = f"HTTP {status}"
|
|
166
201
|
detail: str | None = None
|
|
@@ -195,6 +230,7 @@ def error_from_response(
|
|
|
195
230
|
"body": parsed_body,
|
|
196
231
|
"code": code,
|
|
197
232
|
"request_id": resolved_request_id,
|
|
233
|
+
"retry_after": _parse_retry_after(retry_after),
|
|
198
234
|
}
|
|
199
235
|
|
|
200
236
|
if status in (401, 403):
|
|
@@ -151,9 +151,18 @@ def fetch_cursor_page(
|
|
|
151
151
|
model_class: type[M],
|
|
152
152
|
cursor: str | None = None,
|
|
153
153
|
limit: int | None = None,
|
|
154
|
+
*,
|
|
155
|
+
extra_params: dict[str, Any] | None = None,
|
|
154
156
|
) -> CursorPage[M]:
|
|
155
|
-
"""Fetch a single page from a cursor-paginated endpoint with validation.
|
|
156
|
-
|
|
157
|
+
"""Fetch a single page from a cursor-paginated endpoint with validation.
|
|
158
|
+
|
|
159
|
+
``extra_params`` is merged into the request so callers can forward
|
|
160
|
+
server-side filter kwargs (``category``, ``search``, …) alongside the
|
|
161
|
+
cursor controls. Pagination keys (``cursor``, ``limit``) always win
|
|
162
|
+
over user-supplied ``extra_params`` to keep the iterator's invariants
|
|
163
|
+
intact.
|
|
164
|
+
"""
|
|
165
|
+
params: dict[str, Any] = dict(extra_params) if extra_params else {}
|
|
157
166
|
if cursor:
|
|
158
167
|
params["cursor"] = cursor
|
|
159
168
|
if limit:
|
|
@@ -15,6 +15,7 @@ from devhelm.resources.monitors import Monitors
|
|
|
15
15
|
from devhelm.resources.notification_policies import NotificationPolicies
|
|
16
16
|
from devhelm.resources.resource_groups import ResourceGroups
|
|
17
17
|
from devhelm.resources.secrets import Secrets
|
|
18
|
+
from devhelm.resources.services import Services
|
|
18
19
|
from devhelm.resources.status import Status
|
|
19
20
|
from devhelm.resources.status_pages import StatusPages
|
|
20
21
|
from devhelm.resources.tags import Tags
|
|
@@ -53,6 +54,7 @@ class Devhelm:
|
|
|
53
54
|
dependencies: Dependencies
|
|
54
55
|
deploy_lock: DeployLock
|
|
55
56
|
maintenance_windows: MaintenanceWindows
|
|
57
|
+
services: Services
|
|
56
58
|
status: Status
|
|
57
59
|
status_pages: StatusPages
|
|
58
60
|
|
|
@@ -99,5 +101,6 @@ class Devhelm:
|
|
|
99
101
|
self.dependencies = Dependencies(client)
|
|
100
102
|
self.deploy_lock = DeployLock(client)
|
|
101
103
|
self.maintenance_windows = MaintenanceWindows(client)
|
|
104
|
+
self.services = Services(client)
|
|
102
105
|
self.status = Status(client)
|
|
103
106
|
self.status_pages = StatusPages(client)
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import httpx
|
|
4
4
|
|
|
5
5
|
from devhelm._generated import ApiKeyCreateResponse, ApiKeyDto, CreateApiKeyRequest
|
|
6
|
-
from devhelm._http import api_delete, api_post, path_param
|
|
6
|
+
from devhelm._http import api_delete, api_get, api_post, path_param
|
|
7
7
|
from devhelm._pagination import Page, fetch_all_pages, fetch_page
|
|
8
8
|
from devhelm._validation import RequestBody, parse_single, validate_request
|
|
9
9
|
|
|
@@ -22,6 +22,14 @@ class ApiKeys:
|
|
|
22
22
|
"""List API keys with manual page control."""
|
|
23
23
|
return fetch_page(self._client, "/api/v1/api-keys", ApiKeyDto, page, size)
|
|
24
24
|
|
|
25
|
+
def get(self, id: int | str) -> ApiKeyDto:
|
|
26
|
+
"""Get a single API key by ID."""
|
|
27
|
+
return parse_single(
|
|
28
|
+
ApiKeyDto,
|
|
29
|
+
api_get(self._client, f"/api/v1/api-keys/{path_param(id)}"),
|
|
30
|
+
f"GET /api/v1/api-keys/{id}",
|
|
31
|
+
)
|
|
32
|
+
|
|
25
33
|
def create(self, body: RequestBody[CreateApiKeyRequest]) -> ApiKeyCreateResponse:
|
|
26
34
|
"""Create an API key. Returns the key value (shown only once)."""
|
|
27
35
|
body = validate_request(CreateApiKeyRequest, body, "apiKeys.create")
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from devhelm._generated import (
|
|
6
|
+
ServiceSubscribeRequest,
|
|
7
|
+
ServiceSubscriptionDto,
|
|
8
|
+
UpdateAlertSensitivityRequest,
|
|
9
|
+
)
|
|
10
|
+
from devhelm._http import api_delete, api_get, api_patch, api_post, path_param
|
|
11
|
+
from devhelm._pagination import Page, fetch_all_pages, fetch_page
|
|
12
|
+
from devhelm._validation import parse_single, validate_request
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Dependencies:
|
|
16
|
+
"""Service dependency tracking (service subscriptions)."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, client: httpx.Client) -> None:
|
|
19
|
+
self._client = client
|
|
20
|
+
|
|
21
|
+
def list(self) -> list[ServiceSubscriptionDto]:
|
|
22
|
+
"""List all tracked service dependencies."""
|
|
23
|
+
return fetch_all_pages(
|
|
24
|
+
self._client, "/api/v1/service-subscriptions", ServiceSubscriptionDto
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def list_page(self, page: int, size: int) -> Page[ServiceSubscriptionDto]:
|
|
28
|
+
"""List tracked dependencies with manual page control."""
|
|
29
|
+
return fetch_page(
|
|
30
|
+
self._client,
|
|
31
|
+
"/api/v1/service-subscriptions",
|
|
32
|
+
ServiceSubscriptionDto,
|
|
33
|
+
page,
|
|
34
|
+
size,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def get(self, id: int | str) -> ServiceSubscriptionDto:
|
|
38
|
+
"""Get a tracked dependency by ID."""
|
|
39
|
+
return parse_single(
|
|
40
|
+
ServiceSubscriptionDto,
|
|
41
|
+
api_get(self._client, f"/api/v1/service-subscriptions/{path_param(id)}"),
|
|
42
|
+
f"GET /api/v1/service-subscriptions/{id}",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def track(
|
|
46
|
+
self,
|
|
47
|
+
slug: str,
|
|
48
|
+
*,
|
|
49
|
+
component_id: str | None = None,
|
|
50
|
+
alert_sensitivity: str | None = None,
|
|
51
|
+
) -> ServiceSubscriptionDto:
|
|
52
|
+
"""Track a new service dependency by slug.
|
|
53
|
+
|
|
54
|
+
``component_id`` subscribes to one component instead of the whole
|
|
55
|
+
service. ``alert_sensitivity`` is one of ``ALL``,
|
|
56
|
+
``INCIDENTS_ONLY``, ``MAJOR_ONLY``, or ``AWARENESS`` (the API
|
|
57
|
+
default — silent tracking with no alert fan-out). The request body
|
|
58
|
+
is omitted entirely when neither kwarg is provided.
|
|
59
|
+
"""
|
|
60
|
+
body: ServiceSubscribeRequest | None = None
|
|
61
|
+
if component_id is not None or alert_sensitivity is not None:
|
|
62
|
+
fields: dict[str, str] = {}
|
|
63
|
+
if component_id is not None:
|
|
64
|
+
fields["componentId"] = component_id
|
|
65
|
+
if alert_sensitivity is not None:
|
|
66
|
+
fields["alertSensitivity"] = alert_sensitivity
|
|
67
|
+
body = validate_request(
|
|
68
|
+
ServiceSubscribeRequest, fields, "dependencies.track"
|
|
69
|
+
)
|
|
70
|
+
return parse_single(
|
|
71
|
+
ServiceSubscriptionDto,
|
|
72
|
+
api_post(
|
|
73
|
+
self._client, f"/api/v1/service-subscriptions/{path_param(slug)}", body
|
|
74
|
+
),
|
|
75
|
+
f"POST /api/v1/service-subscriptions/{slug}",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def update_alert_sensitivity(
|
|
79
|
+
self, subscription_id: int | str, alert_sensitivity: str
|
|
80
|
+
) -> ServiceSubscriptionDto:
|
|
81
|
+
"""Update the alert sensitivity on a tracked dependency.
|
|
82
|
+
|
|
83
|
+
``alert_sensitivity`` is one of ``ALL``, ``INCIDENTS_ONLY``,
|
|
84
|
+
``MAJOR_ONLY``, or ``AWARENESS``.
|
|
85
|
+
"""
|
|
86
|
+
body = validate_request(
|
|
87
|
+
UpdateAlertSensitivityRequest,
|
|
88
|
+
{"alertSensitivity": alert_sensitivity},
|
|
89
|
+
"dependencies.update_alert_sensitivity",
|
|
90
|
+
)
|
|
91
|
+
path = f"/api/v1/service-subscriptions/{path_param(subscription_id)}"
|
|
92
|
+
return parse_single(
|
|
93
|
+
ServiceSubscriptionDto,
|
|
94
|
+
api_patch(self._client, f"{path}/alert-sensitivity", body),
|
|
95
|
+
f"PATCH /api/v1/service-subscriptions/{subscription_id}/alert-sensitivity",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def delete(self, id: int | str) -> None:
|
|
99
|
+
"""Remove a tracked dependency."""
|
|
100
|
+
api_delete(self._client, f"/api/v1/service-subscriptions/{path_param(id)}")
|