Skip to content

Commit 9c60b40

Browse files
authored
Merge pull request #37 from devhelmhq/feat/services-catalog-resource
feat: add services catalog resource and complete dependencies coverage
2 parents 4a2f578 + 2637ae5 commit 9c60b40

13 files changed

Lines changed: 914 additions & 9 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ The client exposes the following resource modules:
8484
| `client.api_keys` | API key management |
8585
| `client.dependencies` | Service dependency tracking |
8686
| `client.deploy_lock` | Deploy lock for safe deployments |
87+
| `client.services` | Status Data catalog: vendor services, components, incidents, uptime |
8788
| `client.status` | Dashboard overview |
8889

8990
## Pagination

docs/openapi/monitoring-api.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13520,6 +13520,15 @@
1352013520
"type": "boolean"
1352113521
}
1352213522
},
13523+
{
13524+
"name": "search",
13525+
"in": "query",
13526+
"description": "Case-insensitive substring match on service name or slug",
13527+
"required": false,
13528+
"schema": {
13529+
"type": "string"
13530+
}
13531+
},
1352313532
{
1352413533
"name": "cursor",
1352513534
"in": "query",

src/devhelm/__init__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from devhelm.resources.notification_policies import NotificationPolicies
3030
from devhelm.resources.resource_groups import ResourceGroups
3131
from devhelm.resources.secrets import Secrets
32+
from devhelm.resources.services import Services
3233
from devhelm.resources.status import Status
3334
from devhelm.resources.status_pages import StatusPages
3435
from devhelm.resources.tags import Tags
@@ -46,8 +47,11 @@
4647
ApiKeyDto,
4748
AssertionSeverity,
4849
AssertionTestResultDto,
50+
BatchComponentUptimeDto,
51+
CategoryDto,
4952
CheckResultDto,
5053
CheckTraceDto,
54+
ComponentUptimeDayDto,
5155
ConfirmationPolicyType,
5256
CreateAlertChannelRequest,
5357
CreateApiKeyRequest,
@@ -69,6 +73,7 @@
6973
DashboardOverviewDto,
7074
DeployLockDto,
7175
EnvironmentDto,
76+
GlobalStatusSummaryDto,
7277
IncidentDetailDto,
7378
IncidentDto,
7479
IncidentNewStatus,
@@ -78,6 +83,7 @@
7883
IncidentStatus,
7984
IncidentTimelineDto,
8085
IncidentUpdateCreatedBy,
86+
LifecycleStatus,
8187
LinkedIncidentStatus,
8288
MaintenanceWindowDto,
8389
MembershipStatus,
@@ -99,8 +105,18 @@
99105
ResourceGroupHealthStatus,
100106
ResourceGroupMemberDto,
101107
RuleEvaluationDto,
108+
ScheduledMaintenanceDto,
102109
SecretDto,
110+
ServiceCatalogDto,
111+
ServiceComponentDto,
112+
ServiceDayDetailDto,
113+
ServiceDetailDto,
114+
ServiceIncidentDetailDto,
115+
ServiceIncidentDto,
116+
ServiceLiveStatusDto,
117+
ServiceSubscribeRequest,
103118
ServiceSubscriptionDto,
119+
ServiceUptimeResponse,
104120
StatusPageBranding,
105121
StatusPageComponentCurrentStatus,
106122
StatusPageComponentDto,
@@ -124,6 +140,7 @@
124140
TriggerRuleSeverity,
125141
TriggerRuleType,
126142
UpdateAlertChannelRequest,
143+
UpdateAlertSensitivityRequest,
127144
UpdateAssertionSeverity,
128145
UpdateEnvironmentRequest,
129146
UpdateMaintenanceWindowRequest,
@@ -184,6 +201,7 @@
184201
"Dependencies",
185202
"DeployLock",
186203
"MaintenanceWindows",
204+
"Services",
187205
"Status",
188206
"StatusPages",
189207
# Response DTOs
@@ -216,6 +234,19 @@
216234
"ApiKeyDto",
217235
"ApiKeyCreateResponse",
218236
"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",
219250
"MonitorVersionDto",
220251
"CheckResultDto",
221252
"DashboardOverviewDto",
@@ -261,6 +292,8 @@
261292
"UpdateWebhookEndpointRequest",
262293
"CreateApiKeyRequest",
263294
"AcquireDeployLockRequest",
295+
"ServiceSubscribeRequest",
296+
"UpdateAlertSensitivityRequest",
264297
# Enum aliases (descriptive names for codegen-numbered enums)
265298
"AffectedComponentStatus",
266299
"AlertDeliveryStatus",
@@ -272,6 +305,7 @@
272305
"IncidentSeverity",
273306
"IncidentStatus",
274307
"IncidentUpdateCreatedBy",
308+
"LifecycleStatus",
275309
"LinkedIncidentStatus",
276310
"MemberStatus",
277311
"MembershipStatus",

src/devhelm/_pagination.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,9 +151,18 @@ def fetch_cursor_page(
151151
model_class: type[M],
152152
cursor: str | None = None,
153153
limit: int | None = None,
154+
*,
155+
extra_params: dict[str, Any] | None = None,
154156
) -> CursorPage[M]:
155-
"""Fetch a single page from a cursor-paginated endpoint with validation."""
156-
params: dict[str, Any] = {}
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 {}
157166
if cursor:
158167
params["cursor"] = cursor
159168
if limit:

src/devhelm/client.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from devhelm.resources.notification_policies import NotificationPolicies
1616
from devhelm.resources.resource_groups import ResourceGroups
1717
from devhelm.resources.secrets import Secrets
18+
from devhelm.resources.services import Services
1819
from devhelm.resources.status import Status
1920
from devhelm.resources.status_pages import StatusPages
2021
from devhelm.resources.tags import Tags
@@ -53,6 +54,7 @@ class Devhelm:
5354
dependencies: Dependencies
5455
deploy_lock: DeployLock
5556
maintenance_windows: MaintenanceWindows
57+
services: Services
5658
status: Status
5759
status_pages: StatusPages
5860

@@ -99,5 +101,6 @@ def __init__(
99101
self.dependencies = Dependencies(client)
100102
self.deploy_lock = DeployLock(client)
101103
self.maintenance_windows = MaintenanceWindows(client)
104+
self.services = Services(client)
102105
self.status = Status(client)
103106
self.status_pages = StatusPages(client)

src/devhelm/resources/dependencies.py

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@
22

33
import httpx
44

5-
from devhelm._generated import ServiceSubscriptionDto
6-
from devhelm._http import api_delete, api_get, api_post, path_param
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
711
from devhelm._pagination import Page, fetch_all_pages, fetch_page
8-
from devhelm._validation import parse_single
12+
from devhelm._validation import parse_single, validate_request
913

1014

1115
class Dependencies:
@@ -38,14 +42,59 @@ def get(self, id: int | str) -> ServiceSubscriptionDto:
3842
f"GET /api/v1/service-subscriptions/{id}",
3943
)
4044

41-
def track(self, slug: str) -> ServiceSubscriptionDto:
42-
"""Track a new service dependency by slug."""
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+
)
4370
return parse_single(
4471
ServiceSubscriptionDto,
45-
api_post(self._client, f"/api/v1/service-subscriptions/{path_param(slug)}"),
72+
api_post(
73+
self._client, f"/api/v1/service-subscriptions/{path_param(slug)}", body
74+
),
4675
f"POST /api/v1/service-subscriptions/{slug}",
4776
)
4877

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+
4998
def delete(self, id: int | str) -> None:
5099
"""Remove a tracked dependency."""
51100
api_delete(self._client, f"/api/v1/service-subscriptions/{path_param(id)}")

0 commit comments

Comments
 (0)