Files
analytics/documentation/overview.md
T
JiriUhlir 6934f22253 first
2026-06-18 11:58:23 +02:00

72 lines
3.1 KiB
Markdown

# analytics — overview
A stateless multi-tenant API proxy exposing two upstream services under one
FastAPI app:
1. **Google Analytics 4** — Data API (reporting) + Admin API (read).
2. **Sklik** (Seznam) — Drak JSON-RPC API.
The structure mirrors the sibling `idoklad` / `csob` services (config→env,
credentials→headers, client per upstream, routers, central exception handling,
Swagger at `/docs`), adapted to Python/FastAPI.
## Design principles
- **Stateless / no stored secrets.** Credentials arrive per request in `X-`
headers and are used only to call the upstream. Nothing is persisted; the
only in-memory state is a short-lived GA access-token cache (see below).
- **Thin passthrough.** GA request/response bodies and most Sklik calls are
forwarded as-is, so callers keep the full upstream API surface. Only
authentication, base URL and error mapping are added.
- **No silent failures.** Every error is logged (never the secret values) and
surfaced as JSON. Upstream errors preserve the upstream status and body.
## Layout
```
app/
config.py env-driven config (base URLs, scope, timeout) — no secrets
logging_config.py get_logger(); secrets are never logged
errors.py MissingCredentialsError, UpstreamError + handlers
credentials.py X- header dependencies (GA + Sklik)
clients/
ga_client.py GA Data/Admin HTTP client + service-account token minting
sklik_client.py Sklik JSON-RPC client (login + session + report paging)
routers/
meta.py /health, /version
ga_data.py /ga/data/...
ga_admin.py /ga/admin/...
sklik.py /sklik/...
main.py app factory, root_path, router + handler registration
```
## Reverse proxy
`ROOT_PATH` (e.g. `/apps/analytics`) is passed to FastAPI's `root_path`, so the
OpenAPI `servers` entry and Swagger "Try it out" use the public prefix. Internal
routes are unprefixed (Caddy `handle_path` strips the prefix).
## Authentication summary
| Upstream | Header(s) | Behaviour |
| --- | --- | --- |
| Google Analytics | `X-GA-Access-Token` **or** `X-GA-Credentials` (+ `X-GA-Quota-Project`) | Token used directly; else a token is minted from the base64 service-account JSON (scope `analytics.readonly`) and cached in memory until ~60 s before expiry. |
| Sklik | `X-Sklik-Token` (+ `X-Sklik-User-Id`) | `client.loginByToken` per request → session injected into the call. |
## Deliberately not wired
- **GA Admin write operations** (create/update/delete properties, streams). The
requested scope is read-only (`analytics.readonly`); add `analytics.edit` and
endpoints if management is needed later.
- **Sklik header-credential encryption.** Same deferral as `idoklad`/`csob`:
header values are plaintext over TLS for now.
- **Sklik session reuse across requests** — the chosen model logs in per
request; a future `X-Sklik-Session` passthrough could save the login call.
## Verification checklist (per AGENTS.md)
- `/health` returns 200.
- `/docs` loads; `/openapi.json` `servers` contains the proxy prefix.
- New endpoints appear in Swagger with their `X-` headers in "Try it out".
- Secrets never appear in logs or source.