Springdoc-openapi генерирует OpenAPI 3 spec из аннотаций контроллеров — интеграторам не нужно поддерживать markdown-ТЗ синхронно с кодом. - /swagger-ui.html (UI), /v3/api-docs (JSON), /v3/api-docs.yaml. - OpenApiConfig: title/description/contact/servers (staging+prod+local), bearerAuth security scheme (Keycloak JWT). - SecurityConfig: permitAll на swagger paths. Документация: - docs/integration/altum-imaging-order.md — ТЗ интеграции «Заказ съёмки». Согласованы: m2m service account через Keycloak, BFF в altum-backend (FastAPI), 4 справочника со схемами, cascading select через instrument.supported_*_codes, snapshot strategy для аудита. - docs/tech/api-integration.md — общий гайд для интеграторов: auth flow, endpoints, кэш-стратегии, bitemporal модель, current dictionaries, cross-refs, best practices. Скрипты codegen openapi-generator-cli работают off /v3/api-docs.
14 KiB
Интеграция «Заказ съёмки» (Альтум) ↔ Ordinis НСИ
Status: схемы согласованы, ожидаем service-account Keycloak и сидинг. Owner со стороны Ordinis: zimin.an@nstart.space. Owner со стороны Альтума: altum-backend (FastAPI BFF).
1. Зачем
В форме «Заказ съёмки» (greenfield на стороне Альтума — поля mission / instrument / processing_level / data_format пока отсутствуют в БД и UI) поля будут читаться из Ordinis НСИ — единый источник истины. Когда оператор НСИ заводит новый КА «Ресурс-П 4» — он сразу появляется в выпадашке заказа без релиза Альтума.
Польза: новый спутник / станция / прибор появляется в заказе через 5 минут после ввода в админке Ordinis вместо релизного цикла Альтума.
2. Контракт API (Ordinis → Альтум)
Base URL:
- staging:
https://ordinis.k8s.265.nstart.cloud/api/v1 - prod:
https://ordinis.k8s.264.nstart.cloud/api/v1(после prod stand-up)
Авторизация:
- На staging пока выключена (
ORDINIS_AUTH_REQUIRED=false), на prod включится. - Header
Authorization: Bearer <jwt>от Keycloak (auth.nstart.space/realms/nstart). - Альтум использует service account (m2m client) в реалме
nstart, рольordinis-public. JWT issued черезclient_credentialsflow, рефрешится в altum-backend перед TTL. - Ordinis маппит роль
ordinis-public→ DataScope.PUBLIC. Маппинг — см.ordinis-auth/.../ScopeContext.java.
Endpoints (read-only):
GET /dictionaries
→ список доступных справочников.
GET /dictionaries/{name}/records?lang=ru&at=2026-05-04T12:00:00Z
→ активные записи на момент `at` (default: now).
?lang=ru/en — i18n поля приходят строкой на выбранном языке.
GET /dictionaries/{name}/records/{businessKey}?lang=ru&at=...
→ одна запись по businessKey.
Пример:
curl https://ordinis.k8s.265.nstart.cloud/api/v1/dictionaries/spacecraft/records?lang=ru
[
{
"businessKey": "RESURS-P-3",
"validFrom": "2025-01-01T00:00:00+03:00",
"validTo": "9999-12-31T23:59:59+00:00",
"data": {
"code": "RESURS-P-3",
"name": "Ресурс-П № 3",
"norad_id": 41386,
"satellite_type": "OPTICAL",
"launched_at": "2016-03-13",
"operator": "ROSCOSMOS",
"status": "ACTIVE"
}
}
]
3. Согласованные схемы 4 справочников
Все справочники имеют общую базовую структуру: code (string, businessKey),
status (enum: active/archived). Поля name/description — i18n localized.
Дальше описаны специфические поля.
mission — программа / семейство КА
| поле | тип | пример | примечание |
|---|---|---|---|
code |
string | RESURS |
businessKey, x-id-source |
name |
i18n | Ресурс / Resurs |
для UI |
description |
i18n | Российская программа оптического ДЗЗ среднего разрешения |
tooltip |
spacecraft_codes |
array of string | ["RESURS-P-3","RESURS-P-4"] |
businessKeys из spacecraft |
status |
enum | active / archived |
instrument — конкретный сенсор на борту
| поле | тип | пример | примечание |
|---|---|---|---|
code |
string | GEOTON-L1 |
businessKey, x-id-source |
name |
i18n | Геотон-Л1 |
|
spacecraft_codes |
array of string | ["RESURS-P-3","RESURS-P-4"] |
many-to-many |
type |
enum | optical / sar / infrared / multispectral / hyperspectral |
UI-фильтр |
spatial_resolution_m |
number | 1.0 |
для сортировки/фильтра |
swath_km |
number | 38 |
|
spectral_bands |
array of string | ["PAN","B","G","R","NIR"] |
опц. |
supported_format_codes |
array of string | ["GEOTIFF","NITF"] |
businessKeys из data_format |
supported_level_codes |
array of string | ["L1A","L2"] |
businessKeys из processing_level |
status |
enum | active / archived |
Связи instrument → format/level в самом instrument'е — даёт фронту cascading select без отдельного эндпоинта совместимости.
processing_level — уровень обработки
| поле | тип | пример |
|---|---|---|
code |
string | L1A (businessKey, x-id-source) |
name |
i18n | Радиометрически скорректированный |
description |
i18n | (детали обработки) |
sort_order |
integer | 20 (для упорядочивания L0→L1→L2) |
status |
enum | active / archived |
data_format — формат файлов
| поле | тип | пример |
|---|---|---|
code |
string | GEOTIFF (businessKey, x-id-source) |
name |
i18n | GeoTIFF |
mime_type |
string | image/tiff |
file_extension |
string | .tif |
description |
i18n | опц. |
status |
enum | active / archived |
4. Что делает команда Альтум
Backend (altum-backend, FastAPI, единая точка интеграций)
backend/app/clients/ordinis.py— httpx-клиент с retry + cache:- retry 3× с exponential backoff
- timeout 5 сек
- кэш с TTL 5 мин per
(dictionary, lang)(in-process LRU + ETag когда появится; пока без webhook → достаточно TTL) - на 401 — refresh service-account JWT через Keycloak
- на 5xx/таймаут — stale cache + warn в лог. Не падать.
backend/app/routers/reference.py— единая точка для фронта:Каждый внутри дёргает Ordinis read-api с кэшом. Авторизация JWT-инжекта на стороне backend — фронт ничего не знает про Keycloak Ordinis.GET /api/reference/spacecraft GET /api/reference/spacecraft/{business_key} GET /api/reference/instrument GET /api/reference/mission GET /api/reference/processing_level GET /api/reference/data_format- Таблица
ordersполучает новые колонки:spacecraft_business_key VARCHAR(256) instrument_business_key VARCHAR(256) processing_level_code VARCHAR(256) data_format_code VARCHAR(256) reference_snapshot JSONB - Снэпшот на момент создания заказа — копия всех использованных справочных
записей (для аудита: «через 2 года КА вывели из эксплуатации, но мы должны
помнить какой был на момент заказа»):
{ "reference_snapshot": { "spacecraft": {"RESURS-P-3": {"version": 2, "data": {...}}}, "instrument": {"GEOTON-L1": {"version": 1, "data": {...}}} }, "captured_at": "2026-05-15T10:30:00Z" }
Frontend (TanStack Query)
const { data: spacecrafts } = useQuery({
queryKey: ['reference', 'spacecraft', lang],
queryFn: () => fetch(`/api/reference/spacecraft?lang=${lang}`).then(r => r.json()),
staleTime: 5 * 60_000,
})
<Select options={spacecrafts.map(s => ({ value: s.businessKey, label: s.data.name }))} />
- Skeleton при первой загрузке.
- Fallback с retry-кнопкой при недоступности backend.
POST /api/orders— отправляет businessKey'и (не локальные id).- Каскадные селекты: instrument → отфильтрованы по выбранному spacecraft через
instrument.spacecraft_codes; processing_level + data_format → поinstrument.supported_level_codes/supported_format_codes.
TTL vs webhook
5 минут TTL достаточно для v1 — справочники меняются редко (новый КА — раз в
месяцы). На v2, если потребуется realtime: подписка altum-backend через
aiokafka на топик nsi.dictionary.changed (Ordinis опубликует) с
payload {name, businessKey, action: created|updated|closed}. Не блокер.
CORS
Фронт ходит через altum-backend, прямой CORS не нужен. Для отладки добавим в allowlist:
https://altum.nstart.cloudhttps://altum.k8s.264.nstart.cloud
5. Что делает команда Ordinis (todo)
- Сидинг 4 справочников (
mission,instrument,processing_level,data_format) согласно §3 в bundlecuod. Отдельный changelog для добавления схем + начальные записи. - Service account
altum-backendв Keycloak realmnstartс рольюordinis-public. ПередатьORDINIS_CLIENT_ID/ORDINIS_CLIENT_SECRETкоманде Альтума. - CORS allowlist для отладочных доменов Альтума на ingress.
- Включить
ORDINIS_AUTH_REQUIRED=trueна staging когда altum-backend будет готов тестировать с m2m JWT. - Swagger URL read-api в
docs/tech/api-integration.md(создать). - (v2) Kafka топик
nsi.dictionary.changedс change events.
6. Acceptance criteria
- Оператор НСИ заводит новый КА в Ordinis admin — через ≤5 минут он появляется в выпадашке заказа Альтума без редеплоя.
- Оператор закрывает КА (
PUT /records/{key}/close) — через ≤5 минут пропадает из выпадашки активных, но в существующих заказах (snapshot) всё видно. - Заказ съёмки PDF/выгрузка — берёт name КА из snapshot, не из live read-api.
- При недоступности Ordinis Альтум продолжает работать со stale-кэшом, выводит баннер «справочники могут быть устаревшими».
- Lang switching — оператор переключает на EN, КА называются «Resurs-P № 3».
- Cascading select работает: выбираешь инструмент — список форматов и уровней обработки сужается.
7. Сроки
- Ordinis: сидинг + Keycloak service account + CORS — 1 день после ответа на §5.
- Альтум: BFF + 4 ref-эндпоинта + миграция БД (
reference_snapshot) + UI с 4 каскадными селектами + тесты — 2-3 дня после готовности Ordinis.
Repo cross-refs:
- Read-api routing:
ordinis-read-api/ - Auth/scope:
ordinis-auth/.../{ScopeContext, SecurityConfig}.java - Bitemporal model:
ordinis-domain/.../record/DictionaryRecord.java - Audit log:
ordinis-rest-api/.../audit/AuditLogger.java(фиксирует все изменения справочников — Альтум по trace_id может проследить «когда оператор закрыл КА»).