From 8b584b6b2cdc6bb2d88c7bf4cc97dd24ca347c42 Mon Sep 17 00:00:00 2001 From: caballeto Date: Tue, 5 May 2026 22:04:26 +0200 Subject: [PATCH] docs(readme): mark org_id/workspace_id optional for single-org tokens PyPI README's Configuration table marked org_id / workspace_id as required, but single-org / single-workspace tokens (which is what every fresh user starts with) are auto-resolved server-side. The Required label scared fresh devs into looking up IDs they did not need. - README: drop org_id / workspace_id from Quick Start and error-handling examples; restructure Configuration table with Required + Notes columns explaining auto-resolution. - tests: add a regression test confirming Devhelm() can be constructed with just a token (no env vars, no extra args). The constructor itself already accepted None for both args (see DevhelmConfig in src/devhelm/_http.py); this PR aligns the public docs with the long-standing behaviour. No code-path change, no version bump. Co-authored-by: Cursor --- README.md | 24 ++++++++++-------------- tests/test_client.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index f8d5c69..47742da 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,7 @@ pip install devhelm ```python from devhelm import Devhelm -client = Devhelm( - token="your-api-token", - org_id="your-org-id", - workspace_id="your-workspace-id", -) +client = Devhelm(token="your-api-token") # List all monitors monitors = client.monitors.list() @@ -55,20 +51,20 @@ client.monitors.delete(monitor.id) from devhelm import Devhelm client = Devhelm( - token="your-api-token", # required (or DEVHELM_API_TOKEN env var) - org_id="1", # required (or DEVHELM_ORG_ID env var) - workspace_id="1", # required (or DEVHELM_WORKSPACE_ID env var) + token="your-api-token", # required (or DEVHELM_API_TOKEN env var) + org_id="1", # optional — see notes below + workspace_id="1", # optional — see notes below base_url="https://api.devhelm.io", # optional, defaults to production ) ``` Environment variables are used as fallbacks when constructor arguments are not provided: -| Parameter | Env Variable | -| -------------- | ----------------------- | -| `token` | `DEVHELM_API_TOKEN` | -| `org_id` | `DEVHELM_ORG_ID` | -| `workspace_id` | `DEVHELM_WORKSPACE_ID` | +| Parameter | Required | Env Variable | Notes | +| -------------- | -------- | ---------------------- | -------------------------------------------------------------------------------------------------------- | +| `token` | Yes | `DEVHELM_API_TOKEN` | Personal or workspace API token. | +| `org_id` | No | `DEVHELM_ORG_ID` | Auto-resolved if your token is scoped to one org. Required only when the token has access to multiple. | +| `workspace_id` | No | `DEVHELM_WORKSPACE_ID` | Auto-resolved if your token is scoped to one workspace. Required only when the token spans multiple. | ## Resources @@ -135,7 +131,7 @@ Every `DevhelmApiError` carries: ```python from devhelm import Devhelm, DevhelmAuthError, DevhelmError -client = Devhelm(token="bad-token", org_id="1", workspace_id="1") +client = Devhelm(token="bad-token") try: client.monitors.list() diff --git a/tests/test_client.py b/tests/test_client.py index 7e00a36..fb873a9 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -5,6 +5,7 @@ import pytest from devhelm import Devhelm +from devhelm._http import DevhelmConfig from devhelm.resources.alert_channels import AlertChannels from devhelm.resources.api_keys import ApiKeys from devhelm.resources.dependencies import Dependencies @@ -131,3 +132,31 @@ def test_domains_sub_resource(self, client: Devhelm) -> None: assert callable(d.add) assert callable(d.verify) assert callable(d.remove) + + +class TestClientOptionalTenantArgs: + """`org_id` / `workspace_id` are optional — single-tenant tokens + auto-resolve them server-side, so the README quickstart and the + constructor must work with just a token (the most common case). + """ + + def test_constructible_without_org_or_workspace( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + # Strip any env fallback so we prove the constructor itself accepts + # missing tenant args, not that the test environment leaks them in. + monkeypatch.delenv("DEVHELM_ORG_ID", raising=False) + monkeypatch.delenv("DEVHELM_WORKSPACE_ID", raising=False) + + client = Devhelm(token="test-token", base_url="http://localhost:8080") + + assert client.monitors is not None + assert client.incidents is not None + + def test_config_defaults_tenant_ids_to_none(self) -> None: + # Documents the API contract: leaving them unset on the config + # dataclass yields ``None``, which ``build_client`` then resolves + # via env var or the server-side default. + config = DevhelmConfig(token="test-token") + assert config.org_id is None + assert config.workspace_id is None