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