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:
@@ -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 в инвалидации
|
||||
Reference in New Issue
Block a user