Metadata-Version: 2.4
Name: qualis-roku
Version: 0.6.8
Summary: Qualis Desk Roku HTTP client: connect (tuple result, no raise on failure), validate, clear_txn, optional process singleton, is_connected flag.
Author: Qualis Desk
License: MIT
Keywords: roku,qualis,developer-portal,automation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: pytest-httpx>=0.30; extra == "dev"

# qualis-roku

Python **3.9+** helper for automating **Roku Developer Portal** workflows when your organization exposes a compatible **Qualis Desk–style HTTP API** (portal sign-in, test-user validation, clearing channel transactions from CI or tooling).

## Install

```bash
pip install qualis-roku
```

**From source** (development):

```bash
pip install -e "./packages/qualis_roku"
```

## Use cases

- **Pre-flight checks** — Confirm a QA email resolves to a portal test user for a given **product** (slug from your deployment) before a test run.
- **Clean slate** — Void test-user purchases / entitlements on a channel before functional or billing-related tests.
- **Pipelines** — Drive the same flows from Jenkins, GitHub Actions, or internal runners without opening the browser UI.
- **Batch operations** — Loop over emails or products using the simple `(success, message)` API.

## Design

- **`connect()`** — Start (or queue) server-side portal sign-in; no required arguments if your deployment defines default apps via configuration. For normal connection or API failures it **returns** **`(False, message)`** and does **not** raise (only **`KeyboardInterrupt`** / **`SystemExit`** are passed through).
- **`validate(email, product)`** — Resolve the email in the current portal context for that product slug.
- **`clear_txn(email, product)`** — Clear transactions for that email and product.

Each of the three simple methods returns **`(True, message)`** or **`(False, message)`** so callers can branch without exceptions for normal failures.

**Connection state (singleton-friendly):** **`client.is_connected`** is **`True`** when a non-empty **`session_id`** is stored locally (no extra HTTP call). For a **live** portal check, call the low-level **`session_active(session_id)`** method: it returns a **JSON envelope `dict`**, not a bare boolean—inspect **`response.get("roku", {}).get("session_active")`**.

Lower-level methods (`validate_users`, `clear_transactions`, `session_active`, `connect_and_wait`, …) return full JSON envelopes when you need structured details; they **raise** **`QualisRokuError`** on HTTP/API errors when you need full exception details.

## Quick start

**Credentials and host** can come from **environment variables** (recommended—nothing secret in code) or be passed **explicitly** to the constructor. The client reads **`os.environ` only**: a **`.env` file is not loaded automatically**—use `export`, your process manager, or `python-dotenv` in your app before constructing **`QualisRokuClient`**. **`QUALIS_DESK_BASE_URL`** is the canonical name; **`QUALIS_BASE`** is also accepted as an alias.

**From environment** (set vars in the shell, CI secrets, or `.env` loaded by your app—do not commit secrets). **`connect()`** with no arguments uses default product slugs built into the library unless you override with **`QUALIS_ROKU_BRANDS`** / **`ROKU_BRANDS`**.

```python
from qualis_roku import QualisRokuClient

# Uses deployment-specific env vars when base_url / api_token are omitted.
with QualisRokuClient() as client:
    ok, msg = client.connect()
    if not ok:
        raise SystemExit(msg)
    ok_v, msg_v = client.validate("user@example.com", "your-product-slug")
    if not ok_v:
        raise SystemExit(msg_v)
    ok_c, msg_c = client.clear_txn("user@example.com", "your-product-slug")
    if not ok_c:
        raise SystemExit(msg_c)
```

**Explicit arguments** (useful for tests or when config is not on the process environment):

```python
with QualisRokuClient(
    base_url="https://your-qualis-host.example/qualis-desk",
    api_token="your-credential",
) as client:
    ok, msg = client.connect()
    ...
```

Minimal usage only needs base URL, API token, and checking **`(ok, message)`**. Optional: **`QualisRokuClient.try_create()`** returns **`(client, err)`** so the constructor never raises; **`connect`** / **`validate`** / **`clear_txn`** turn failures into **`(False, message)`** (not exceptions) for normal errors.

Optional **process singleton:** set **`QUALIS_ROKU_SINGLETON=1`** so every **`QualisRokuClient()`** shares one instance, or call **`connect(singleton=True)`** once. Use **`force_new=True`** or **`QualisRokuClient.reset_singleton()`** to bypass / reset (e.g. tests).

**Session state after `connect`:** use **`if client.is_connected:`** for a fast local check. For a **server** check, call **`session_active(client.session_id)`** (returns a **`dict`**; see **Design** above) inside **`try`/`except QualisRokuError`**.

| Surface | Returns | On failure |
| --- | --- | --- |
| **`connect()`**, **`validate()`**, **`clear_txn()`** | **`(bool, str)`** | **`(False, message)`** — no raise for normal errors |
| **`client.is_connected`** | **`bool`** | N/A (local **`session_id`**) |
| **`session_active(session_id)`** (and other low-level HTTP helpers) | **`dict`** (envelope) | **`QualisRokuError`** |

## Command line

After install, the **`qualis-roku`** executable (or **`python -m qualis_roku`**) exposes the same flows without writing Python. **Before parsing arguments**, the CLI loads the first **`.env`** found in the working directory or a parent folder (`KEY=value` lines; existing exported variables win).

```bash
qualis-roku --help
qualis-roku connect
qualis-roku validate user@example.com your-product-slug
qualis-roku clear user@example.com your-product-slug
qualis-roku run user@example.com your-product-slug
```

Pass **`--base-url`** and **`--api-token`** or configure the environment variables described in your **internal** runbook. **`connect`** uses built-in default brands unless **`QUALIS_ROKU_BRANDS`** / **`ROKU_BRANDS`** is set. **`connect`** can write a small session file (default `./.qualis_roku_session`) so **`validate`** / **`clear`** can reuse the session; use **`--print-session-id-only`** for scripting.

Transient HTTP errors (429, 502–504) and retryable network failures are retried automatically with backoff.

Set **`QUALIS_ROKU_DEBUG=1`** (or pass **`debug=True`** to **`QualisRokuClient`**) for verbose debug logs on stderr.

## Requirements

- **Network**: HTTPS access from the machine running the client to your Qualis deployment.
- **`httpx`** (declared as a dependency).

## Documentation

- **Full reference** (operators + integrators): after install from source or a checkout, open **[`docs/DOCUMENTATION.md`](docs/DOCUMENTATION.md)** — complete env var list, API surface, CLI flags, retries, debug logging, and troubleshooting.
- Your organization may also maintain **internal runbooks** and HTTP samples for the server API; the PyPI page shows only this README.

## Security (general)

- Prefer **TLS** end-to-end.
- Keep credentials out of source control and logs; use your platform’s secret management.

## License

MIT.
