# Read-side endpoints Read-API в Ordinis — отдельный deployable (`ordinis-read-api`, scaled 2× в проде) работающий на Postgres replica. Запросы из read-api **не блокируют** writer'а. Base URL: | Среда | URL | |---|---| | prod | `https://ordinis.k8s.264.nstart.cloud/api/v1` | | staging | `https://ordinis.k8s.265.nstart.cloud/api/v1` | Все endpoints требуют JWT (см. [Авторизация](auth.md)). ## OpenAPI / Swagger UI Interactive playground с **Keycloak login** (PKCE flow, без client_secret). **Используй staging для exploration, не prod.** Прод — для реальных пользователей и боевых данных. Запросы из Swagger UI создают audit-записи и могут менять данные если ты случайно нажмёшь POST/PUT/DELETE. - **Swagger UI (staging):** `https://ordinis.k8s.265.nstart.cloud/swagger-ui.html` → нажми **Authorize** → выбери `oauth2` → войдёшь через Keycloak. После login каждый "Try it out" автоматически добавит JWT. - **Spec JSON (staging):** `https://ordinis.k8s.265.nstart.cloud/v3/api-docs` - **Spec YAML (staging):** `https://ordinis.k8s.265.nstart.cloud/v3/api-docs.yaml` OpenAPI spec для clients (SDK generation, code-first integration) можно также pull from prod (`/v3/api-docs` на k8s.264) — это статичный файл, безопасный для machine consumption. Для interactive exploration — только staging. {% note tip %} Если у тебя уже есть pre-issued service token (например для CI scripts) — выбери схему `bearerAuth` в Authorize dialog и paste'ни raw JWT. PKCE flow тогда не нужен. {% endnote %} Из spec'а можно generate'ить клиент: ```bash openapi-generator-cli generate \ -i https://ordinis.k8s.264.nstart.cloud/v3/api-docs \ -g python \ -o ./altum-backend/clients/ordinis ``` Поддерживаются: Python, TypeScript, Go, Java, Kotlin, Ruby и др. ## `GET /api/v1/dictionaries` Список доступных справочников (фильтрован по scope JWT'а). ```bash curl -H "Authorization: Bearer $TOKEN" \ https://ordinis.k8s.264.nstart.cloud/api/v1/dictionaries ``` Response: ```json [ { "name": "spacecraft", "displayName": "Космические аппараты", "scope": "PUBLIC", "schemaVersion": "1.0.0", "recordsCount": 4 }, { "name": "mission", "displayName": "Миссии / программы", "scope": "PUBLIC", "schemaVersion": "1.0.0", "recordsCount": 9 } ] ``` ## `GET /api/v1/{name}/records` {% note warning %} **БЕЗ префикса `/dictionaries/`!** Read-api сидит на `/api/v1/{name}/records`. Префикс `/api/v1/dictionaries/...` — это admin write controller (CRUD), у него нет bulk-list эндпоинта. {% endnote %} Активные записи на момент `at` (default: сейчас). ### Параметры | Параметр | Тип | Пример | Назначение | |---|---|---|---| | `lang` | string | `ru-RU`, `en-US` | Flatten i18n полей в строку. Короткие `ru`/`en` тоже. | | `at` | ISO 8601 | `2026-05-04T12:00:00+03:00` | Bitemporal query — активные на момент. | | `as_scope` | string | `PUBLIC,INTERNAL` | Legacy для anonymous + `allowQueryScope=true`. | ### Пример ```bash curl -H "Authorization: Bearer $TOKEN" \ "https://ordinis.k8s.264.nstart.cloud/api/v1/spacecraft/records?lang=ru-RU" ``` Response: ```json [ { "businessKey": "RESURS-P-3", "displayKey": "Ресурс-П № 3", "scope": "PUBLIC", "validFrom": "2025-01-01T00:00:00+03:00", "validTo": "9999-12-31T23:59:59+03:00", "data": { "name": "Ресурс-П № 3", "satelliteTypeCode": "EARTH-OBSERVATION-OPTICAL", "launchDate": "2024-12-25", "status": "active" }, "version": 2 } ] ``` ### Без `?lang` (raw i18n) ```json "name": { "ru-RU": "Ресурс-П № 3", "en-US": "Resurs-P No.3" } ``` С `?lang=ru-RU`: ```json "name": "Ресурс-П № 3" ``` Если запрошенного языка нет — fallback к `default_locale` (обычно `ru-RU`). ## `GET /api/v1/{name}/records/{businessKey}` Одна запись. Параметры те же что у list. ```bash curl -H "Authorization: Bearer $TOKEN" \ "https://ordinis.k8s.264.nstart.cloud/api/v1/spacecraft/records/RESURS-P-3?lang=ru" ``` `404 Not Found` — запись либо не существует, либо закрыта на момент `at`, либо вне scope JWT'а (Ordinis скрывает существование). ## `GET /api/v1/{name}/records/{businessKey}/history` Все версии записи (включая закрытые) для построения истории изменений в UI. ```json [ { "version": 1, "validFrom": "2024-06-01T00:00:00+03:00", "validTo": "2025-01-01T00:00:00+03:00", "data": {...}, "txTime": "2024-06-01T10:23:45+03:00", "changedBy": "admin@nstart.space", "changeReason": "initial" }, { "version": 2, "validFrom": "2025-01-01T00:00:00+03:00", "validTo": "9999-12-31T23:59:59+03:00", "data": {...}, "txTime": "2024-12-30T14:11:22+03:00", "changedBy": "admin@nstart.space", "changeReason": "Обновление статуса после успешного запуска" } ] ``` Для consumers history endpoint обычно не нужен — это UI-feature. ## Spatial filters (geometry) Если справочник имеет geometry поля (`spacecraft.aoi`, `mission.coverage_area`), поддерживаются bbox + polygon фильтры: ### `?bbox=` (bounding box) ``` GET /api/v1/spacecraft/records?bbox=37.0,55.0,38.0,56.0 ``` Format: `min_lon,min_lat,max_lon,max_lat` (WGS84). Возвращает записи где `geometry` пересекает bbox через PostGIS `ST_Intersects` + GiST index. ### `?polygon=` (GeoJSON) ``` GET /api/v1/spacecraft/records?polygon={"type":"Polygon","coordinates":[[[37,55],[38,55],[38,56],[37,56],[37,55]]]} ``` Polygon — GeoJSON object (URL-encoded). Сложнее bbox, точнее. ## Дальше - [События (Kafka outbox)](events.md) — push-обновления вместо polling - [Bitemporal](bitemporal.md) — детали `validFrom`/`validTo` модели - [Caching](caching.md) — стратегии актуализации