Files
mostovik-backend/docs/source-records-public-runtime-contract.md
Aleksandr Meshchryakov 18971d33ec
All checks were successful
Mostovik Backend CI/CD / Tests and lint (push) Successful in 3m55s
Mostovik Backend CI/CD / Build linux/amd64 release images (push) Successful in 3m43s
Mostovik Backend CI/CD / Deploy and verify internal main (push) Has been skipped
Mostovik Backend CI/CD / Deploy customer main (push) Has been skipped
Mostovik Backend CI/CD / Deploy dev (push) Successful in 1m45s
feat: complete published registry contracts and gated SRO ingestion
2026-09-14 17:01:02 +02:00

104 lines
9.2 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.
# Карточки, задачи, история и готовые выгрузки — 14.09.2026
Этот документ уточняет общий runtime-контракт по новому
[source-records-backend-improvements.md](source-integration/source-records-backend-improvements.md).
Для выгрузок он заменяет разделы SME/SRO source-first спецификаций, в которых
описан `preparing` / HTTP 202. Действующий frontend использует готовый ticket
HTTP 201 и native form POST со скачиванием HTTP 200; этот flow сохранён.
| Источник | Card slug | Parser source | Export group |
| --- | --- | --- | --- |
| Бюджет | budget-process-registry | budget_ubpandnubp | budget_process_registry |
| МСП | sme-support-recipients-registry | fns_sme_support_recipients | government_support |
| СРО | sro-membership-check | sro_membership_check | sro_membership |
## Карточки и история
GET `/api/v1/sources/` и `/api/v1/sources/{slug}/` возвращают одинаковую полную
форму карточки, включая `source_items`, `active_tasks`, `latest_load` и
`latest_success_load`. Для трёх источников `records_count` — все опубликованные
записи, `organizations_count` — уникальные организации опубликованного набора.
ОПК остаётся отдельным показателем dashboard `registry_data_coverage` и
`registry_enrichment_analytics`. Scope остальных карточек сохранён.
Последняя неуспешная попытка видна в `latest_load`; она не заменяет
`latest_success_load` и не сдвигает `last_updated_at`. Данные `snapshot`
в карточках, dashboard и логах содержат `artifact_id`, `snapshot_date`, `version`,
`checksum_sha256`, `published_at`. Неизвестная дата/отсутствующая публикация —
null; пустая версия/checksum не подменяется выдуманным значением.
В `/api/v1/system/logs/`, detail и CSV для новых источников `source`
канонический parser source из таблицы, `source_label` — человекочитаемое название.
CSV добавляет код источника, дату/версию/SHA256 снимка. `records_count` отражает
опубликованный итог. У incremental СРО текущий итог и изменённая часть разделены
в artifact metadata (`snapshot_records_count`, `snapshot_organizations_count`,
`batch_published_records_count`, `updated_records_count`).
## Запуск и polling
Оба admin entrypoint — POST `/api/v1/sources/{slug}/refresh/` и
`/api/v1/parsers/run/{source}/` — принимают `{}` или `{"params": {}}`.
Внешние URL, file paths и параметры loader не принимаются для трёх источников.
Только SRO parser-run дополнительно принимает `mode: incremental|full`,
по умолчанию incremental; неверное значение — HTTP 400 `invalid_mode`.
Card refresh возвращает plain HTTP 202 `{task_id, task_ids, status: "queued"}`;
parser-run возвращает эти поля плюс `source` и `task_name` внутри стандартного
`{success, data, errors, meta}`. Активный запуск исключает второй запуск через
любой entrypoint: HTTP 409 `refresh_already_running`. До согласования upstream
доступа СРО возвращает HTTP 409 `upstream_access_not_approved`, без постановки
задачи в очередь. Worker повторно проверяет доступ.
GET `/api/v1/jobs/{task_id}/` для этих трёх источников различает `queued`,
`running`, `retrying`, `success`, `failed`, `cancelled`, `skipped`; `progress`
остаётся целым числом, `message` отражает ход работы. `source` — канонический код.
`result` успешной публикации содержит batch/load/artifact IDs, счётчики и
метаданные снимка (`SnapshotJobResult`). `meta` для новых задач типизирован как `SnapshotRunMetadata`: режим СРО,
число кандидатов/запрошенных организаций/найденных членств, публикация и карантин,
счётчики HTTP/parse ошибок и nullable дата реестра. В публичный meta входят только
разрешённые счётчики, без конфигурации loader или raw responses. Те же счётчики
доступны в log meta; terminal result имеет aliases `records_count` / `organizations_count`.
Legacy jobs сохраняют прежние значения
статуса и прежний result. Общая схема допускает legacy `error`, deprecated raw aliases
`pending|started|retry|failure|revoked` и дополнительные
поля result. Generic parser results list имеет уникальный operationId
`api_v1_parsers_results_list`; detail сохраняет прежний `api_v1_parsers_results_read`.
Dashboard сохраняет operationId `api_v1_parsers_dashboard_list` и tag `api`,
чтобы generated client оставался в прежнем модуле.
## Выгрузка
POST `/api/v2/organization-source-records/export-ticket/` с `sources` и `format`
возвращает HTTP 201 `{ticket, file_name, expires_in}`. Ticket одноразовый,
TTL по умолчанию 300 секунд. POST `/export-download/` с form field `ticket`
возвращает ZIP HTTP 200; истёкший, неизвестный или использованный ticket —
HTTP 410 `{detail, code: "ticket_expired"}`. Capability позволяет native form
без передачи JWT в URL. Для выпуска ticket нужны прежние admin permissions.
Публикация любого из трёх снимков запрашивает существующую фоновую сборку
экспорта после commit. Если подготовленное поколение относится к предыдущей
публикации, новый ticket получает HTTP 503 `source_export_not_ready` до готовности
новой сборки. Ошибка импорта не делает успешное поколение устаревшим.
При занятой блокировке запрос сборки повторяется через 30 секунд; бюджет покрывает
TTL блокировки плюс два повтора. Исчерпание бюджета — ошибка Celery с явной записью
в журнале, а не успешный пропуск. Ночной запуск остаётся восстановительным путём.
Сборка читает записи и идентификаторы публикаций из одного PostgreSQL REPEATABLE
READ snapshot; спулирование завершается до рендеринга CSV/JSON/XLSX. Вложенный
transaction защищён дополнительной проверкой неизменности идентичности публикаций.
Ticket закрепляет immutable generation: новая публикация не меняет его содержимое.
Удаление старых поколений выдерживает TTL после retirement; уже начавшийся stream
держит открытые файлы и не ломается при последующей уборке поколения.
Все три группы экспортируют полную опубликованную историю; ИНН, ОГРН и ОКПО
остаются строками во всех форматах. После обновления схемы/нормализации старого
payload требуется запустить существующую команду `build_source_record_exports`:
появление СРО меняет матрицу с 40 на 43 артефакта, а нормализация payload сама
по себе не создаёт новый artifact ID. Она атомарно увеличивает content revision
затронутых опубликованных артефактов; новая выдача ticket проверяет и эту ревизию.
До пересборки возвращается 503, ранее выданные tickets сохраняют свою generation.
Локальные контрактные тесты не подтверждают живой upstream СРО или принятие
frontend UI. Доступ к upstream по умолчанию выключен; включение требует отдельного
согласованного основания. Настоящие адреса/секреты стендов в эти проверки не входят.