Files
Zimin A.N. d5268b9395 docs: Diplodoc setup + полное руководство интеграции по API
Новая документация-as-code на платформе Diplodoc (Yandex open-source) с
YFM (Yandex Flavored Markdown). Build → static HTML, deployable куда угодно.

Что добавлено:
- package.json + .yfm + toc.yaml — Diplodoc setup, навигация для sidebar
- index.md — главная страница с tabs для разных audiences
- README.md — инструкция для contributors

Раздел integration/api/ — 11 страниц про интеграцию consumers:
- index — обзор + глоссарий
- auth — Keycloak service accounts, scope mapping
- read-endpoints — GET /dictionaries, list/by-key, spatial filters
- events — Kafka outbox топики, partition keys, idempotency, DLQ
- webhooks — HMAC, SSRF guard, retry policy, test ping endpoint
- bitemporal — validFrom/validTo + history, future-dated entries, bulk close/export
- x-references — FK syntax, validation errors, scope hiding, orphan scanner, v2 cascade preview
- caching — TTL / push / snapshot strategies, hybrid pattern
- errors — full error code reference (validation/auth/conflict/server), retry strategy
- best-practices — 33 numbered rules для consumers
- bundle-cuod — все 40 справочников с FK графом и сценариями

Обновлены:
- ops/README.md — link на новую integration/api/, убран inline <br>
- ops/slo.md — broken cross-repo links заменены на text references

Build:
  cd docs && pnpm install && pnpm build
  → ./_dist (20 HTML files, 7.1M)
  pnpm serve  # → http://localhost:8088

CI deploy job для GitLab Pages — в roadmap (см. docs/README.md).

Существующий tech/api-integration.md оставлен в toc как legacy
(содержимое мигрировано + расширено в integration/api/).
2026-05-07 23:19:02 +03:00

6.0 KiB
Raw Permalink Blame History

Bitemporal модель

Каждая запись Ordinis имеет двухмерное время:

  • Бизнес-время (validFrom / validTo) — когда запись действует в реальности.
  • Транзакционное время (Hibernate Envers revinfo.timestamp) — когда изменение зафиксировано в БД.

Это две независимые оси: можно ввести запись задним числом (business → past, txTime → now), либо запланировать на будущее (business → future, txTime → now), либо retroactively изменить прошлое (исправить ошибку — txTime → now, но business range уже past).

Поля

{
  "businessKey": "RESURS-P-3",
  "validFrom": "2025-01-01T00:00:00+03:00",
  "validTo": "9999-12-31T23:59:59+03:00",
  "version": 2,
  "data": { ... }
}
Поле Тип Описание
validFrom timestamp tz Когда версия начинает действовать.
validTo timestamp tz До какого момента (или 9999-12-31 если бессрочно).
version int Optimistic lock + порядковый номер версии. Monotonic per businessKey.

Update создаёт новую версию

Update — это не overwrite. Это:

  1. Старая запись: validTo = now() (закрыта).
  2. Новая запись: validFrom = now(), validTo = 9999..., version + 1.

Обе версии остаются в БД. История доступна через GET /records/{key}/history (см. Read endpoints).

gantt
    dateFormat YYYY-MM-DD
    title Версии записи RESURS-P-3
    section v1
    v1 (status=in-orbit)        :2024-06-01, 2024-12-30
    section v2
    v2 (status=active)          :2024-12-30, 2026-05-07
    section v3
    v3 (name updated)           :2026-05-07, 9999-12-31

Запросы на момент

?at=2025-06-15 возвращает версию, которая была активна 15 июня 2025:

curl "https://ordinis.k8s.264.nstart.cloud/api/v1/spacecraft/records/RESURS-P-3?at=2025-06-15T00:00:00Z"

→ v2 (status=active), потому что 2024-12-30 ≤ 2025-06-15 < 2026-05-07.

Без ?at — default = now().

Future-dated entries

Можно ввести запись в будущее:

PUT /api/v1/dictionaries/spacecraft/records/MISSION-2027
{
  "validFrom": "2027-01-01T00:00:00+03:00",
  "data": { "name": "Запланированный КА", "status": "planned" }
}

Запись существует в БД, но GET ?at=now её не вернёт (потому что now < validFrom). Появится автоматически 1 января 2027.

Закрытие записи (close)

POST /api/v1/dictionaries/spacecraft/records/RESURS-P-3/close
{
  "at": "2026-05-07T12:00:00+03:00",
  "reason": "Списан с орбиты"
}

Устанавливает validTo = at. После этого:

  • GET ?at=2026-05-07T11:00:00Z → возвращает запись (ещё активна).
  • GET ?at=2026-05-07T13:00:00Z → 404 (уже закрыта).
  • GET ?at=2026-05-07T11:30:00Z&include_closed=true → для UI history.

Outbox emit'ит event action: closed.

Bulk close

Закрытие нескольких записей одной транзакцией:

POST /api/v1/dictionaries/spacecraft/records/_bulk-close
{
  "businessKeys": ["RESURS-P-1", "RESURS-P-2", "RESURS-P-3"],
  "at": "2026-05-07T00:00:00+03:00",
  "reason": "Программа Ресурс-П завершена"
}

Response:

{
  "closed": 3,
  "skipped": [],
  "errors": []
}

Каждая запись в собственной sub-transaction (Spring AOP per-record). Если одна fail'нула с invalid_key — она в errors, остальные продолжают.

Bulk export CSV

GET /api/v1/dictionaries/spacecraft/records/_export?format=csv&lang=ru-RU

Stream CSV (RFC 4180) с активными на now записями. Используется UI bulk operations.

Параметры — те же что list endpoint (lang, at, фильтры).

Использование для consumer'ов

Snapshot at order time

Когда нужен «как было на момент заказа» — два варианта:

Вариант A: query с ?at=

GET /api/v1/spacecraft/records/RESURS-P-3?at=2025-06-15T10:00:00Z

Pro: всегда свежие данные если возможна retroactive correction. Con: depends on Ordinis availability + retention.

Вариант B: snapshot в свою БД

CREATE TABLE imaging_order (
  id              UUID PRIMARY KEY,
  spacecraft_business_key  VARCHAR(256),
  reference_snapshot       JSONB,      -- копии записей, не FK
  created_at      TIMESTAMPTZ DEFAULT NOW()
);

Pro: read-only к Ordinis, не зависит от availability/retention. Con: stale если Ordinis retroactively исправил данные.

Альтум использует Вариант B (snapshot) для legal compliance — заказы attestation требуют не depending on external service.

Update с конкретным validFrom

Не всегда нужно validFrom = now(). Можно указать:

PUT /api/v1/dictionaries/spacecraft/records/RESURS-P-3
{
  "validFrom": "2026-05-15T00:00:00+03:00",
  "data": { "name": "Ресурс-П № 3 (новое имя с 15 мая)" }
}

Старая версия закрывается с validTo = 2026-05-15. Новая validFrom = 2026-05-15. До 15 мая ?at=now возвращает старую, после — новую.

Дальше