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/).
This commit is contained in:
Zimin A.N.
2026-05-07 23:19:02 +03:00
parent 0c79821f8e
commit d5268b9395
20 changed files with 3450 additions and 5 deletions
+185
View File
@@ -0,0 +1,185 @@
# Bitemporal модель
Каждая запись Ordinis имеет двухмерное время:
- **Бизнес-время** (`validFrom` / `validTo`) — когда запись действует в реальности.
- **Транзакционное время** (Hibernate Envers `revinfo.timestamp`) — когда
изменение зафиксировано в БД.
Это две независимые оси: можно ввести запись задним числом
(business → past, txTime → now), либо запланировать на будущее
(business → future, txTime → now), либо retroactively изменить
прошлое (исправить ошибку — txTime → now, но business range уже past).
## Поля
```json
{
"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](read-endpoints.md)).
```mermaid
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:
```bash
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
Можно ввести запись в будущее:
```json
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)
```bash
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
Закрытие нескольких записей одной транзакцией:
```bash
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:
```json
{
"closed": 3,
"skipped": [],
"errors": []
}
```
Каждая запись в собственной sub-transaction (Spring AOP per-record).
Если одна fail'нула с invalid_key — она в `errors`, остальные продолжают.
## Bulk export CSV
```bash
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=`**
```bash
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 в свою БД**
```sql
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()`. Можно указать:
```bash
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` возвращает старую, после — новую.
## Дальше
- [Cross-references (FK)](x-references.md) — связи между записями
- [Caching](caching.md) — учитываем bitemporal в инвалидации