# turbine-client

Typed HTTP client for the [Turbine](https://pypi.org/project/turbine-data/) management API. Sync and async siblings; both speak the `/api/v1/manage/...` surface and accept any `httpx.Auth` strategy.

Use this when you want to call a running Turbine server from your own code — outside of a `dagster-turbine` or `airflow-turbine` integration.

## Install

```bash
uv add turbine-client
```

Python 3.12 or newer.

## Minimal example

```python
from turbine_client import TurbineClient

client = TurbineClient("http://turbine:8000")
contracts = client.list_contracts()           # list every registered contract
sessions = client.start_runs(["orders"])      # fan out one Check Run per contract id
result = sessions[0].wait()                   # block until the run is terminal
print(result.status, [r.outcome for r in result.results])
```

## Contract selection

`start_runs` accepts three shapes:

- `start_runs("orders")` — single contract, one run.
- `start_runs(["orders", "customers"])` — explicit list, one run per id.
- `start_runs(None)` (the default) — fan out across every registered contract.

`start_run(contract_id)` is the atomic primitive — one contract, one run handle.

## Check Window and run options

`start_runs` (and `start_run`) accept four optional kwargs:

| kwarg | type | purpose |
|---|---|---|
| `since` | `datetime \| str \| None` | lower bound of the Check Window |
| `until` | `datetime \| str \| None` | upper bound of the Check Window |
| `incremental` | `bool` | scope to rows newer than the last run's watermark — mutually exclusive with `since` / `until` server-side |
| `flag_rows` | `bool` | persist failing-row primary keys to the flag matrix |

## Auth

Pass any `httpx.Auth` subclass via the `auth=` kwarg. Two helpers ship with the package:

```python
from turbine_client import TurbineClient, BearerAuth, AzureADClientCredentials

client = TurbineClient("http://turbine:8000", auth=BearerAuth(token="..."))

# OAuth client-credentials (e.g. behind the PAX ingress gateway):
client = TurbineClient(
    "https://turbine.example.com",
    auth=AzureADClientCredentials(
        tenant_id="...", client_id="...", client_secret="...",
        scope="api://turbine/.default",
    ),
)
```

Leave `auth=None` for in-cluster, unauthenticated deployments.

## Async client

`AsyncTurbineClient` mirrors `TurbineClient`; every network method returns a coroutine. Use it from any `asyncio` event loop — including the Airflow triggerer process.

```python
from turbine_client import AsyncTurbineClient

async def main() -> None:
    client = AsyncTurbineClient("http://turbine:8000")
    sessions = await client.start_runs(["orders"])
    result = await sessions[0].wait()
```

## Compatibility

`turbine-client >= 0.5.12` requires `turbine-data >= 0.5.12` on the server (Contract Selection + Check Severity round-trip require both the `POST /checks/runs` endpoint and the `severity` field on the registry response). The client carries no runtime version negotiation; older clients keep working against newer servers because API additions are additive on `/api/v1`.
