Files
mdm-ordinis/docs/integration/api/x-references.md
T
Zimin A.N. bcfb07f547 docs: split integrator guide (Diplodoc) vs internal docs + add build-docs CI
Чёткое разделение audiences:
- docs/ — consumer-facing only, Diplodoc-built. Public contract для
  интеграторов (Альтум, BI, third-party).
- docs-internal/ — operator runbooks, status snapshots, legacy tech pages.
  НЕ в Diplodoc build, НЕ публикуются.

Изменения:
- Move docs/{ops,status,tech} → docs-internal/{ops,status,tech}
- docs/toc.yaml: убраны секции Operations, Архитектура (legacy), Status
- docs/index.md: убран tab "оператор / on-call", focus на integrators
- docs/README.md: rewritten — scope только integrator guide, editorial
  guidelines (no leak internal architecture, stable API contract)
- .yfm: убран явный ignore status/* (теперь не нужно — папка переехала)
- docs-internal/README.md: index для внутренней документации с rationale
  разделения

Scrubbing implementation details из Diplodoc страниц:
- events.md: убраны Strimzi/cluster.142 references, OutboxPoller детали,
  internal alert names (OrdinisOutboxLagHigh, OrdinisOutboxDLQNonEmpty),
  metric names (nsi_outbox_*). Заменены на consumer-observable contract.
- webhooks.md: убраны metric names (nsi_webhook_ssrf_rejected_total),
  alert table (OrdinisWebhookHighFailureRate и т.д.). Retry policy
  переписана на user-side perspective.
- errors.md: убран alert name OrdinisReadApiErrorBudgetBurnFast, Tempo
  endpoint URL, internal connection details.
- x-references.md: OrphanReferenceScanner → general "Ordinis периодически
  детектирует". Убраны metric/alert names. Cascade roadmap без link на
  internal design doc.
- auth.md: убран file path ScopeContext.java.
- bundle-cuod.md: убран Maven module path.
- caching.md: cascade design link заменён на text reference.

CI:
- .gitlab-ci.yml: новый job build-docs (stage: build) — Node 20 alpine,
  pnpm install --frozen-lockfile + pnpm build, artifact docs/_dist на
  неделю. Triggers: auto на docs/**, либо manual web. Готов к follow-up
  pages-deploy job когда host решён.
- Новый pattern .docs-changes для rules.

Build verified clean: 13 HTML pages, 6.9M, no broken links/refs.
2026-05-07 23:27:03 +03:00

145 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cross-references (FK между справочниками)
JSON Schema extension `x-references` объявляет, что значение поля должно
ссылаться на active record в указанном словаре.
## Синтаксис
```json
{
"type": "object",
"properties": {
"satelliteTypeCode": {
"type": "string",
"x-references": "satellite_type.code"
}
}
}
```
Format: `"<dictionary_name>.<field_name>"`. Когда POST/PUT приходит, Ordinis
делает lookup в `satellite_type` где `data->>code == satelliteTypeCode`.
Если не найден / не активен / вне scope — `422 Validation Error`.
## Текущие FK в bundle cuod
| Source | Target | Поле |
|---|---|---|
| `spacecraft.satelliteTypeCode` | `satellite_type` | `code` |
| `spacecraft.statusCode` | `spacecraft_status` | `code` |
| `mission.spacecraftCodes` (array) | `spacecraft` | `businessKey` |
| `instrument.spacecraftCodes` (array) | `spacecraft` | `businessKey` |
| `instrument.supportedFormatCodes` (array) | `data_format` | `businessKey` |
| `instrument.supportedLevelCodes` (array) | `processing_level` | `businessKey` |
(Точное число и список могут меняться с bundle versions — см.
[Bundle cuod](bundle-cuod.md).)
## Server-side validation
При POST/PUT ReferenceValidator проверяет каждое x-references поле:
| Симптом | Error code | Описание |
|---|---|---|
| Referenced dictionary не существует | `x_references_dict_not_found` | Опечатка в schema. |
| Referenced record не найден / inactive | `x_references_target_not_found` | Bad value либо record закрыт. |
| Value не string (для v1) | `x_references_non_string` | v1 поддерживает только string refs. |
| Schema spec malformed | `x_references_malformed` | Bad schema. |
Response:
```json
{
"errors": [
{
"path": "/satelliteTypeCode",
"code": "x_references_target_not_found",
"message": "Referenced record not found in 'satellite_type' for code='UNKNOWN-TYPE'"
}
]
}
```
## Scope hiding
Если referenced dictionary — INTERNAL/RESTRICTED, а consumer имеет только
PUBLIC scope, ReferenceValidator возвращает тот же error
(`x_references_target_not_found`) что и на real not-found. Ordinis **не leak'ает**
existence of out-of-scope dictionary через differential errors.
## Orphan references
Возможна ситуация когда referenced record **закрывается**, а referencing
record остаётся:
```
1. spacecraft.RESURS-P-3 references satellite_type.EARTH-OPTICAL
2. Admin closes satellite_type.EARTH-OPTICAL (record.closed event emitted)
3. spacecraft.RESURS-P-3 теперь имеет dangling FK
```
Ordinis периодически детектирует такие orphan'ы серверной стороной — данные
**не корректируются автоматически** (может быть intentional state до
update referencing записей), но Ordinis team получает уведомление и
может coordinate fix с data stewards.
Consumer-side defensiveness:
- При получении event `record.closed` для записи на которую ты ссылаешься —
invalidate свой cache + либо fetch свежую версию referencing
записи (если она тоже обновилась), либо handle dangling FK явно.
## Cascade rules — roadmap (v2)
Сегодня закрытие источника НЕ closes referencing automatically. Это
дoлжно явно делаться через bulk close либо individual updates.
В v2 планируется schema extension `x-references-on-close`:
```json
{
"satelliteTypeCode": {
"type": "string",
"x-references": "satellite_type.code",
"x-references-on-close": "block"
}
}
```
Опции:
- **block** — закрытие источника невозможно пока есть active referencing (default).
- **warn** — закрытие проходит, в response warning.
- **cascade** — все referencing закрываются в той же транзакции, отдельные
events emit'ятся per record.
Конкретный timeline и финальная семантика будут опубликованы перед
deployment v2 — следи за анонсами от Ordinis team.
## Lineage discovery — roadmap (v2)
Сегодня нет endpoint для "что depends on X". В v2 планируется:
- `GET /api/v1/dictionaries/{name}/dependents` — словари которые ссылаются.
- `GET /api/v1/records/{dict}/{key}/dependents` — конкретные записи.
Это позволит consumer'ам и UI узнавать связи без чтения всех schemas
руками. Spec будет опубликован перед deployment v2.
## Best practices
1. **Используй businessKey как target field** для большинства FK. Это
стабильный идентификатор. Альтернатива (`code`, `name`) работает но
требует поддержки `x-unique` в target (иначе ambiguous matches).
2. **Не делай deeply nested refs** — v1 поддерживает только top-level
fields. Nested objects с x-references — пропустят validation.
3. **Array refs работают** — если поле `type: array, items: {type: string,
x-references: ...}`, каждый элемент валидируется. (Но в v1.2.1 это
делается на каждый элемент отдельно — for large arrays медленно. Будет
batch'нуто в v2.)
4. **Document FK в displayName** — `"description": "FK на satellite_type
через code"` помогает при ревью schemas.
## Дальше
- [Bundle cuod](bundle-cuod.md) — список текущих FK в проде
- [События](events.md) — учитывай cascade closure при инвалидации