Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,21 +108,38 @@ print(results.has_more)

## Error Handling

The SDK raises three top-level error types (see
[`040-codegen-policies.md`](https://github.com/devhelmhq/mono/blob/main/cowork/design/040-codegen-policies.md)):

- `DevhelmValidationError` — local request/response shape validation failed.
- `DevhelmApiError` — the API returned a non-2xx status. Subclassed by HTTP
class for ergonomics: `DevhelmAuthError` (401/403), `DevhelmNotFoundError`
(404), `DevhelmConflictError` (409), `DevhelmRateLimitError` (429),
`DevhelmServerError` (5xx).
- `DevhelmTransportError` — the request never reached a server response
(connection refused, timeout, TLS failure, etc.).

Every `DevhelmApiError` carries:

- `status` — the HTTP status code
- `code` — coarse machine-readable category (e.g. `NOT_FOUND`,
`RATE_LIMITED`); switch on this, not the human-readable `message`
- `request_id` — the per-request id from the `X-Request-Id` response header;
always include this in support tickets

```python
from devhelm import Devhelm, DevhelmError, AuthError
from devhelm import Devhelm, DevhelmAuthError, DevhelmError

client = Devhelm(token="bad-token", org_id="1", workspace_id="1")

try:
client.monitors.list()
except AuthError as e:
print(f"Auth failed: {e.message} (HTTP {e.status})")
except DevhelmAuthError as e:
print(f"Auth failed: {e.message} (HTTP {e.status}, request_id={e.request_id})")
except DevhelmError as e:
print(f"API error [{e.code}]: {e.message}")
```

Error codes: `AUTH`, `NOT_FOUND`, `CONFLICT`, `VALIDATION`, `API`.

## Development

```bash
Expand Down
127 changes: 117 additions & 10 deletions docs/openapi/monitoring-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -10128,8 +10128,7 @@
"$ref": "#/components/schemas/TestNotificationPolicyRequest"
}
}
},
"required": true
}
},
"responses": {
"200": {
Expand Down Expand Up @@ -12921,6 +12920,7 @@
"Status Data"
],
"summary": "Get a single service by slug or UUID with current status, components, and recent incidents",
"description": "When ``summary=true``, the inline ``components`` list is trimmed to groups + showcase leaves + currently-impacted leaves + ungrouped leaves, and a ``componentsSummary`` block is added with the trimmed counts. Powers SSR for vendors with hundreds of components (Snowflake, Cloudflare, DigitalOcean) without OOM-ing the renderer. Default false for full back-compat.",
"operationId": "getService",
"parameters": [
{
Expand All @@ -12930,6 +12930,16 @@
"schema": {
"type": "string"
}
},
{
"name": "summary",
"in": "query",
"description": "Return a curated subset of components (groups + showcase + impacted + ungrouped) and a componentsSummary block; default false",
"required": false,
"schema": {
"type": "boolean",
"default": false
}
}
],
"responses": {
Expand Down Expand Up @@ -13032,6 +13042,7 @@
"Status Data"
],
"summary": "List active components for a service with current status and inline uptime",
"description": "When ``groupId`` is supplied, only direct children of that group are returned — used by the pSEO renderer to lazy-load the leaves under a group that summary mode trimmed. Without ``groupId`` the response includes every active component for the service.",
"operationId": "getComponents",
"parameters": [
{
Expand All @@ -13041,6 +13052,16 @@
"schema": {
"type": "string"
}
},
{
"name": "groupId",
"in": "query",
"description": "Restrict result to direct children of this group component id",
"required": false,
"schema": {
"type": "string",
"format": "uuid"
}
}
],
"responses": {
Expand Down Expand Up @@ -21894,6 +21915,35 @@
},
"description": "A single component position"
},
"ComponentsSummaryDto": {
"required": [
"groupComponentCounts",
"includedCount",
"totalCount"
],
"type": "object",
"properties": {
"totalCount": {
"type": "integer",
"description": "Total active components for this service across all groups",
"format": "int32"
},
"includedCount": {
"type": "integer",
"description": "Number of components actually returned in the inline ``components`` list",
"format": "int32"
},
"groupComponentCounts": {
"type": "object",
"additionalProperties": {
"type": "integer",
"description": "Per-group active leaf count, keyed by group component id (UUID stringified). Empty when the service has no groups; lets the UI render \"show all N\" affordances without a second round trip",
"format": "int32"
},
"description": "Per-group active leaf count, keyed by group component id (UUID stringified). Empty when the service has no groups; lets the UI render \"show all N\" affordances without a second round trip"
}
}
},
"ComponentStatusDto": {
"required": [
"id",
Expand Down Expand Up @@ -22239,8 +22289,7 @@
"CreateEnvironmentRequest": {
"required": [
"name",
"slug",
"isDefault"
"slug"
],
"type": "object",
"properties": {
Expand Down Expand Up @@ -22269,7 +22318,8 @@
},
"isDefault": {
"type": "boolean",
"description": "Whether this is the default environment for new monitors"
"description": "Whether this is the default environment for new monitors (default: false)",
"nullable": true
}
}
},
Expand Down Expand Up @@ -22992,11 +23042,23 @@
"subscribedEvents": {
"minItems": 1,
"type": "array",
"description": "Event types to deliver, e.g. monitor.created, incident.resolved",
"description": "Event types to deliver",
"items": {
"minLength": 1,
"type": "string",
"description": "Event types to deliver, e.g. monitor.created, incident.resolved"
"enum": [
"monitor.created",
"monitor.updated",
"monitor.deleted",
"incident.created",
"incident.resolved",
"incident.reopened",
"service.status_changed",
"service.component_changed",
"service.incident_created",
"service.incident_updated",
"service.incident_resolved"
]
}
}
}
Expand Down Expand Up @@ -23882,6 +23944,7 @@
},
"ErrorResponse": {
"required": [
"code",
"message",
"status",
"timestamp"
Expand All @@ -23894,6 +23957,11 @@
"format": "int32",
"example": 404
},
"code": {
"type": "string",
"description": "Coarse machine-readable error category (e.g. NOT_FOUND, RATE_LIMITED); stable per status",
"example": "NOT_FOUND"
},
"message": {
"type": "string",
"description": "Human-readable error message; safe to surface to end users",
Expand All @@ -23904,13 +23972,21 @@
"description": "Server time when the error was produced (epoch milliseconds)",
"format": "int64",
"example": 1737302400000
},
"requestId": {
"type": "string",
"description": "Opaque per-request id; same value as the X-Request-Id response header. Use in support tickets.",
"nullable": true,
"example": "5b6f7a8c-1234-4d5e-9f0a-1b2c3d4e5f6a"
}
},
"description": "Uniform error envelope returned for every non-2xx response",
"example": {
"status": 404,
"code": "NOT_FOUND",
"message": "Monitor not found",
"timestamp": 1737302400000
"timestamp": 1737302400000,
"requestId": "5b6f7a8c-1234-4d5e-9f0a-1b2c3d4e5f6a"
}
},
"EscalationChain": {
Expand Down Expand Up @@ -25430,7 +25506,18 @@
"properties": {
"type": {
"type": "string",
"description": "Rule type, e.g. severity_gte, monitor_id_in, region_in"
"description": "Rule type used to evaluate incidents and status events",
"enum": [
"severity_gte",
"monitor_id_in",
"region_in",
"incident_status",
"monitor_type_in",
"service_id_in",
"resource_group_id_in",
"component_name_in",
"monitor_tag_in"
]
},
"value": {
"type": "string",
Expand Down Expand Up @@ -28149,6 +28236,14 @@
"$ref": "#/components/schemas/ServiceComponentDto"
}
},
"componentsSummary": {
"nullable": true,
"allOf": [
{
"$ref": "#/components/schemas/ComponentsSummaryDto"
}
]
},
"uptime": {
"nullable": true,
"allOf": [
Expand Down Expand Up @@ -32518,7 +32613,19 @@
"nullable": true,
"items": {
"type": "string",
"description": "Replace subscribed events; null preserves current"
"enum": [
"monitor.created",
"monitor.updated",
"monitor.deleted",
"incident.created",
"incident.resolved",
"incident.reopened",
"service.status_changed",
"service.component_changed",
"service.incident_created",
"service.incident_updated",
"service.incident_resolved"
]
}
},
"enabled": {
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "devhelm"
version = "0.1.2"
version = "0.1.3"
description = "DevHelm SDK for Python — typed client for monitors, incidents, alerting, and more"
authors = [{ name = "DevHelm", email = "hello@devhelm.io" }]
license = "MIT"
Expand Down
2 changes: 0 additions & 2 deletions src/devhelm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""DevHelm SDK for Python — typed client for monitors, incidents, alerting, and more."""

from devhelm._errors import (
AuthError,
DevhelmApiError,
DevhelmAuthError,
DevhelmConflictError,
Expand Down Expand Up @@ -140,7 +139,6 @@
"DevhelmRateLimitError",
"DevhelmServerError",
"DevhelmTransportError",
"AuthError",
# Pagination
"Page",
"CursorPage",
Expand Down
Loading
Loading