Merge branch 'docs/embed-cheatsheet-as-appendix' into 'main'
docs(quickstart): встроить cheatsheet как Приложение A See merge request 2-6/2-6-4/terravault/ordinis!247
This commit is contained in:
@@ -579,8 +579,296 @@ DictionaryDetail detail = mapper.readValue(resp.body(), DictionaryDetail.class);
|
|||||||
|
|
||||||
## 13. Дальше
|
## 13. Дальше
|
||||||
|
|
||||||
|
- **Приложение A: Cheat Sheet** — одностраничник со всеми endpoints, ниже в этом документе
|
||||||
- **OpenAPI:** `$ORDINIS/v3/api-docs` (staging) / Swagger UI выше
|
- **OpenAPI:** `$ORDINIS/v3/api-docs` (staging) / Swagger UI выше
|
||||||
|
- **Postman collection:** `~/Desktop/Ordinis-Postman/` (27 рабочих запросов + 10-шаговый demo flow)
|
||||||
- **Bundle spec (для интеграторов):** [`bundle-format-spec.md`](./bundle-format-spec.md)
|
- **Bundle spec (для интеграторов):** [`bundle-format-spec.md`](./bundle-format-spec.md)
|
||||||
- **CHANGELOG:** [`docs/changelog.md`](../changelog.md) — что меняется по версиям
|
- **CHANGELOG:** [`docs/changelog.md`](../changelog.md) — что меняется по версиям
|
||||||
- **Webhooks deep-dive:** в admin UI → `/webhooks` → Detail page
|
- **Webhooks deep-dive:** в admin UI → `/webhooks` → Detail page
|
||||||
показывает histogram доставок, DLQ, signature test
|
показывает histogram доставок, DLQ, signature test
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Приложение A · Cheat Sheet
|
||||||
|
|
||||||
|
> Дублирует [`api-cheatsheet.md`](./api-cheatsheet.md) — одностраничный quick-reference со всеми endpoints, query-params, error codes и реальными примерами для copy-paste. Дублирование осознанное: интегратор открывает quickstart один раз, далее живёт в приложении.
|
||||||
|
|
||||||
|
|
||||||
|
Одностраничный референс. Полный гайд — `api-quickstart.md`.
|
||||||
|
|
||||||
|
**Base prod:** `https://ordinis.nstart.cloud/api/v1` (public LE-cert)
|
||||||
|
**Base staging:** `https://ordinis.k8s.265.nstart.cloud/api/v1` (Swagger UI здесь)
|
||||||
|
**Version:** `v2.37.x` · **OpenAPI:** `/v3/api-docs` (только staging — на prod Swagger выключен)
|
||||||
|
|
||||||
|
## Auth
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# client_credentials (для service-to-service интеграторов)
|
||||||
|
TOKEN=$(curl -s -X POST https://auth.nstart.space/realms/nstart/protocol/openid-connect/token \
|
||||||
|
-d grant_type=client_credentials \
|
||||||
|
-d client_id=$KC_CLIENT_ID -d client_secret=$KC_CLIENT_SECRET | jq -r .access_token)
|
||||||
|
|
||||||
|
curl -H "Authorization: Bearer $TOKEN" $ORDINIS/api/v1/spacecraft/records?as_scope=PUBLIC,INTERNAL
|
||||||
|
```
|
||||||
|
|
||||||
|
Scopes: `PUBLIC` (anon ok) · `INTERNAL` (auth) · `RESTRICTED` (admin).
|
||||||
|
|
||||||
|
## Read (no auth для PUBLIC)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/version` | Версия + commit + builtAt |
|
||||||
|
| `GET` | `/dictionaries` | Список всех справочников |
|
||||||
|
| `GET` | `/dictionaries/{name}` | Детали + JSON Schema |
|
||||||
|
| `GET` | `/dictionaries/templates` | 6 шаблонов схем |
|
||||||
|
| `GET` | `/dictionaries/templates/{id}` | Один шаблон со schemaJson |
|
||||||
|
| `GET` | `/dictionaries/graph/outgoing` | FK-граф между справочниками |
|
||||||
|
| `GET` | `/dictionaries/{name}/dependents` | Кто ссылается на этот dict |
|
||||||
|
| `GET` | `/dictionaries/{name}/changelog` | Список изменений схемы |
|
||||||
|
| `GET` | `/dictionaries/{name}/changelog/{id}/diff` | Diff между версиями |
|
||||||
|
| `GET` | `/dictionaries/{name}/snapshots?from=&to=&granularity=` | Bucket'ы изменений для TimeTravel slider (granularity: hour/day/week) |
|
||||||
|
| `GET` | `/{name}/records?as_scope=PUBLIC` | Список записей (⚠️ **БЕЗ** `/dictionaries/` префикса) |
|
||||||
|
| `GET` | `/{name}/records/{businessKey}?as_scope=PUBLIC` | Одна запись |
|
||||||
|
| `GET` | `/dictionaries/{name}/records/{businessKey}/history?as_scope=PUBLIC` | История версий записи (Envers) — **с** `/dictionaries/` |
|
||||||
|
| `GET` | `/dictionaries/{name}/records/{businessKey}/cascade-preview?as_scope=PUBLIC` | Preview каскада — **с** `/dictionaries/` |
|
||||||
|
| `GET` | `/{name}/records/scheduled-summary?as_scope=PUBLIC` | Будущие версии (count + nearestValidFrom) |
|
||||||
|
| `GET` | `/search?q=` | Поиск по справочникам (records не индексируются) |
|
||||||
|
| `GET` | `/ai/info` | `{enabled: bool}` AI feature flag |
|
||||||
|
|
||||||
|
> ⚠️ **Корень частой путаницы**: consumer endpoints (`/records/...`) идут БЕЗ `/dictionaries/` префикса. Admin-style audit endpoints (history, cascade-preview, changelog, drafts, snapshots) — С `/dictionaries/`. См. [api-quickstart.md § Topology](api-quickstart.md).
|
||||||
|
|
||||||
|
### Query params (records)
|
||||||
|
|
||||||
|
```
|
||||||
|
?as_scope=PUBLIC,INTERNAL (required) CSV scope filter
|
||||||
|
?at=2026-03-15T00:00:00Z bitemporal: запись на конкретный момент (validFrom <= at < validTo)
|
||||||
|
Accept-Language: en-US (header) локаль — единственный рабочий путь
|
||||||
|
⚠️ ?locale= query param backend сейчас игнорирует
|
||||||
|
⚠️ Работает ТОЛЬКО на records endpoints.
|
||||||
|
Schema endpoint (`/dictionaries/{name}`) всегда
|
||||||
|
возвращает schemaJson на defaultLocale.
|
||||||
|
?bbox=lon1,lat1,lon2,lat2 geo: bounding box (для geo-dicts)
|
||||||
|
?polygon=<GeoJSON> geo: polygon-фильтр
|
||||||
|
```
|
||||||
|
|
||||||
|
**Что НЕ работает (на read-api endpoint):**
|
||||||
|
- `?limit=`, `?offset=` — pagination отсутствует, возвращается весь массив
|
||||||
|
- `?status=ACTIVE&country=RU` — нет ad-hoc filtering по schema полям; фильтруй на клиенте
|
||||||
|
|
||||||
|
## Write (требует auth)
|
||||||
|
|
||||||
|
⚠️ Все write-endpoints — на **writer** сервисе, с `/dictionaries/` префиксом.
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `POST` | `/dictionaries/{name}/records` | Создать запись (если approval не нужен) |
|
||||||
|
| `PUT` | `/dictionaries/{name}/records/{businessKey}` | Обновить |
|
||||||
|
| `DELETE` | `/dictionaries/{name}/records/{businessKey}` | Soft-delete (tombstone) |
|
||||||
|
| `POST` | `/dictionaries/{name}/records/bulk-close` | Массовое закрытие |
|
||||||
|
| `POST` | `/dictionaries/{name}/records/{businessKey}/cascade-close` | Закрыть с зависимыми |
|
||||||
|
| `POST` | `/dictionaries/{name}/records/export-selected.csv` | Bulk export (выборка по ids в body) |
|
||||||
|
| `PATCH` | `/dictionaries/{name}/schema` | Изменить схему inline (non-breaking) |
|
||||||
|
| `PUT` | `/dictionaries/{name}` | Полностью пересоздать (breaking) |
|
||||||
|
|
||||||
|
## Workflow (drafts + approvals)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `POST` | `/dictionaries/{name}/records/drafts` | Создать record draft |
|
||||||
|
| `POST` | `/dictionaries/{name}/drafts` | Создать schema draft |
|
||||||
|
| `GET` | `/dictionaries/{name}/drafts` | Список schema drafts по dict |
|
||||||
|
| `GET` | `/dictionaries/{name}/drafts/active` | Активный draft (200+null если нет) |
|
||||||
|
| `GET` | `/dictionaries/{name}/drafts/{id}` | Один draft |
|
||||||
|
| `DELETE` | `/dictionaries/{name}/drafts/{id}` | Withdraw |
|
||||||
|
| `POST` | `/dictionaries/{name}/drafts/{id}/review` | Назначить ревью |
|
||||||
|
| `POST` | `/dictionaries/{name}/drafts/{id}/decision` | Approve/reject schema draft |
|
||||||
|
| `POST` | `/dictionaries/{name}/drafts/{id}/publish` | Опубликовать после approve |
|
||||||
|
| `GET` | `/drafts/me` | Мои record-drafts (status filter) |
|
||||||
|
| `GET` | `/drafts/{id}` | Один record draft |
|
||||||
|
| `DELETE` | `/drafts/{id}` | Withdraw record draft |
|
||||||
|
| `POST` | `/drafts/{id}/approve` | Одобрить (body: `{comment}`) |
|
||||||
|
| `POST` | `/drafts/{id}/reject` | Отклонить (body: `{comment}`) |
|
||||||
|
| `GET` | `/admin/reviews/pending` | Очередь ревьюера (records) |
|
||||||
|
| `GET` | `/admin/schema-reviews/pending` | Очередь ревьюера (schemas) |
|
||||||
|
| `GET` | `/admin/schema-drafts/me` | Мои schema drafts |
|
||||||
|
|
||||||
|
## AI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||||||
|
$ORDINIS/api/v1/ai/suggest-field \
|
||||||
|
-d '{"prompt":"контактный email оператора"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Limits: **30 req/min** на IP, circuit breaker **10 fails / 60s → open 5min** (`503 circuit_open`).
|
||||||
|
|
||||||
|
## Notifications (per-user, auth)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/me/notifications` | Bell-bar feed |
|
||||||
|
| `POST` | `/me/notifications/{id}/read` | Mark as read |
|
||||||
|
| `POST` | `/me/notifications/read-all` | Mark all as read |
|
||||||
|
| `GET` | `/me/notifications/preferences` | Per-event matrix |
|
||||||
|
| `PUT` | `/me/notifications/preferences` | Сохранить матрицу |
|
||||||
|
| `GET` | `/me/notifications/settings` | Quiet hours config |
|
||||||
|
| `PUT` | `/me/notifications/settings` | Сохранить quiet hours |
|
||||||
|
|
||||||
|
## Webhooks (admin)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/admin/webhooks/subscriptions` | Список подписок |
|
||||||
|
| `POST` | `/admin/webhooks/subscriptions` | Создать (`secret` в ответе, **один раз**) |
|
||||||
|
| `GET` | `/admin/webhooks/subscriptions/{id}` | Детали |
|
||||||
|
| `PUT` | `/admin/webhooks/subscriptions/{id}` | Обновить |
|
||||||
|
| `DELETE` | `/admin/webhooks/subscriptions/{id}` | Удалить |
|
||||||
|
| `POST` | `/admin/webhooks/subscriptions/{id}/rotate-secret` | Перевыпуск ключа |
|
||||||
|
| `POST` | `/admin/webhooks/subscriptions/{id}/test` | Test delivery |
|
||||||
|
| `GET` | `/admin/webhooks/subscriptions/{id}/stats` | Histogram (24h/7d) |
|
||||||
|
| `GET` | `/admin/webhooks/subscriptions/{id}/deliveries` | Recent deliveries |
|
||||||
|
| `GET` | `/admin/webhooks/deliveries/dlq` | DLQ list |
|
||||||
|
| `POST` | `/admin/webhooks/deliveries/{id}/retry` | Manual retry |
|
||||||
|
|
||||||
|
### Headers delivered
|
||||||
|
|
||||||
|
```
|
||||||
|
X-Ordinis-Event: RecordCreated
|
||||||
|
X-Ordinis-Signature: sha256=<hex> HMAC-SHA256(body, secret)
|
||||||
|
X-Ordinis-Delivery: del_01HXY...
|
||||||
|
User-Agent: ordinis-alerts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event types
|
||||||
|
|
||||||
|
`RecordCreated · RecordUpdated · RecordClosed · RecordRestored · BulkClosed · SchemaDraftSubmitted · SchemaDraftApproved · SchemaDraftRejected · SchemaDraftPublished · RecordDraftSubmitted · RecordDraftApproved`
|
||||||
|
|
||||||
|
## Audit (admin)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/admin/audit?dictionary=&action=&user=&dateFrom=&dateTo=` | Фильтрованный аудит |
|
||||||
|
| `GET` | `/admin/audit/export.csv` | Полный экспорт |
|
||||||
|
| `GET` | `/admin/audit/export-selected.csv` | Selected ids экспорт |
|
||||||
|
|
||||||
|
Action types (13): `DICTIONARY_CREATED · SCHEMA_UPDATED · SCHEMA_PUBLISHED · RECORD_CREATED · RECORD_UPDATED · RECORD_CLOSED · RECORD_RESTORED · DRAFT_CREATED · DRAFT_SUBMITTED · DRAFT_APPROVED · DRAFT_REJECTED · DRAFT_WITHDRAWN · REVIEWER_ASSIGNED`
|
||||||
|
|
||||||
|
## Outbox (admin)
|
||||||
|
|
||||||
|
| Verb | Path | Что |
|
||||||
|
|---|---|---|
|
||||||
|
| `GET` | `/admin/outbox/stats` | Pending/DLQ counters |
|
||||||
|
| `GET` | `/admin/outbox/dlq` | DLQ events list |
|
||||||
|
|
||||||
|
## Errors — единая форма
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "status": 422, "code": "validation_failed", "message": "...",
|
||||||
|
"details": {}, "traceId": "...", "timestamp": "..." }
|
||||||
|
```
|
||||||
|
|
||||||
|
| HTTP | code | |
|
||||||
|
|---|---|---|
|
||||||
|
| 400 | `bad_request` | Невалидный body |
|
||||||
|
| 401 | `unauthorized` | Нет/просрочен токен |
|
||||||
|
| 403 | `forbidden` | Scope/role не пускает |
|
||||||
|
| 404 | `not_found` | Нет такого ресурса |
|
||||||
|
| 409 | `conflict` | Concurrent update / unique violation |
|
||||||
|
| 422 | `validation_failed` | Schema validation fail |
|
||||||
|
| 429 | `rate_limited` | Лимит запросов |
|
||||||
|
| 500 | `internal_error` | См. traceId |
|
||||||
|
| 502 | `llm_error` | AI: LLM upstream |
|
||||||
|
| 503 | `circuit_open` | AI: circuit breaker |
|
||||||
|
|
||||||
|
## Реальные примеры (staging 265)
|
||||||
|
|
||||||
|
### FK-зависимость: кто использует справочник
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s "$ORDINIS/api/v1/dictionaries/satellite_type/dependents" | jq
|
||||||
|
# [
|
||||||
|
# {
|
||||||
|
# "sourceDict": "spacecraft",
|
||||||
|
# "sourceDisplayName": "Космические аппараты",
|
||||||
|
# "sourceField": "satellite_type_code",
|
||||||
|
# "targetField": "code",
|
||||||
|
# "onClose": "BLOCK",
|
||||||
|
# "activeRecordsInSourceDict": 14
|
||||||
|
# }
|
||||||
|
# ]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Семантика**: `spacecraft.satellite_type_code` ссылается на `satellite_type.code`. На staging 14 КА используют этот справочник, и `onClose: BLOCK` означает что нельзя удалить тип КА пока на него ссылаются записи (без cascade-close).
|
||||||
|
|
||||||
|
Другие живые FK на staging:
|
||||||
|
- `ground_station` ← `antenna.ground_station` (4 records, BLOCK)
|
||||||
|
- `spacecraft_status` ← `spacecraft.status`
|
||||||
|
- `country` ← `spacecraft.country`
|
||||||
|
|
||||||
|
### Локализация записи (Accept-Language)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# default ru-RU
|
||||||
|
curl -s "$ORDINIS/api/v1/spacecraft/records/1999-068A?as_scope=PUBLIC" | jq '.data | {mission, operator}'
|
||||||
|
# {"mission": "Глобальный мониторинг суши и атмосферы", "operator": "НАСА"}
|
||||||
|
|
||||||
|
# en-US
|
||||||
|
curl -sH "Accept-Language: en-US" \
|
||||||
|
"$ORDINIS/api/v1/spacecraft/records/1999-068A?as_scope=PUBLIC" | jq '.data | {mission, operator}'
|
||||||
|
# {"mission": "Global land and atmosphere monitoring", "operator": "NASA"}
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ Только Accept-Language header. `?locale=` query param backend сейчас игнорирует. Schema endpoint (`/dictionaries/{name}`) **не локализуется** вообще — всегда возвращает на defaultLocale.
|
||||||
|
|
||||||
|
### Time-travel: запись на момент в прошлом
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Запись на 14.05.2026 12:00 UTC (точно после её validFrom = 11:34)
|
||||||
|
curl -s "$ORDINIS/api/v1/test1/records/Dima_record_test_1?as_scope=PUBLIC&at=2026-05-14T12:00:00Z" \
|
||||||
|
| jq '{businessKey, validFrom, validTo, data}'
|
||||||
|
# 200 OK — запись действительная на указанный момент
|
||||||
|
|
||||||
|
# До validFrom → 404 record_not_active (это ожидаемо)
|
||||||
|
curl -s "$ORDINIS/api/v1/test1/records/Dima_record_test_1?as_scope=PUBLIC&at=2026-05-01T00:00:00Z" | jq '.code'
|
||||||
|
# "record_not_active"
|
||||||
|
```
|
||||||
|
|
||||||
|
### History — все ревизии записи
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s "$ORDINIS/api/v1/dictionaries/spacecraft/records/1999-068A/history?as_scope=PUBLIC" | jq '.[0] | {revisionNumber, revisionType, updatedAt}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schema time-travel (changelog diff)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Список ревизий
|
||||||
|
curl -s "$ORDINIS/api/v1/dictionaries/test1/changelog" | jq '.currentVersion, (.entries | length)'
|
||||||
|
|
||||||
|
# Diff конкретной ревизии (before/after JSON-Schema)
|
||||||
|
curl -s "$ORDINIS/api/v1/dictionaries/test1/changelog/22/diff" | jq '{eventType, occurredAt, before: .before.properties | keys, after: .after.properties | keys}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 60-second smoke
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ORDINIS=https://ordinis.nstart.cloud # prod (public LE)
|
||||||
|
# или: ORDINIS=https://ordinis.k8s.265.nstart.cloud # staging
|
||||||
|
|
||||||
|
curl -s $ORDINIS/api/v1/version | jq # alive
|
||||||
|
curl -s $ORDINIS/api/v1/dictionaries | jq 'length' # 37 на prod / 39 на staging
|
||||||
|
curl -s $ORDINIS/api/v1/dictionaries/spacecraft | jq '.scope' # "PUBLIC"
|
||||||
|
curl -s "$ORDINIS/api/v1/spacecraft/records?as_scope=PUBLIC" | jq '.[0].businessKey'
|
||||||
|
curl -s "$ORDINIS/api/v1/dictionaries/satellite_type/dependents" | jq 'length' # 1 (spacecraft зависит)
|
||||||
|
curl -s "$ORDINIS/api/v1/search?q=spacecraft" | jq '.total' # >0
|
||||||
|
curl -s $ORDINIS/api/v1/ai/info | jq '.enabled' # true / false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Постман collection (на случай если потерял ссылку из section 13)
|
||||||
|
|
||||||
|
27 рабочих запросов + 10-шаговый demo flow:
|
||||||
|
|
||||||
|
- `~/Desktop/Ordinis-Postman/` (если генерировал локально)
|
||||||
|
- [docs/integration/postman/](postman/) — версионный snapshot
|
||||||
|
|
||||||
|
— конец приложения A —
|
||||||
|
|||||||
Reference in New Issue
Block a user