Catalog Admin API — реестр методов и общие правила области
Реестр admin-методов каталога (контекст catalog, сервис Pin.Catalog.Api, area admin) и общие
правила этой области: модель актора администратора платформы (m2m), policy AdminSystemOnly,
обязательные заголовки, deep audit мутаций, соответствие permission-каталогу, общий формат ответа и
общий поток admin-мутации. Документы методов ссылаются сюда и описывают только свою дельту. Дома
полей — ../../domain/.
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | catalog |
| API area | admin (m2m, ADR-0053) |
| Тип документа | api (area-registry, не единичная операция) |
| Статус | draft |
| Owner | PIN Marketplace |
| Область действия | admin-оверрайды витрины (объекты недвижимости/корпуса), операционный rebuild проекций; public- и developers-области — отдельные реестры (../README.md, ../developers/); CRUD пакетов продвижения — admin-область контекста promotion (ADR-0060) |
| Assumptions | администратор платформы (через admin API) — единственный клиент области; permission-каталог — ../../../identity/domain/permission.md |
Реестр методов
Route-префикс области — /api/admin/v1/. Все permission-проверки — каталожное соответствие поверх
policy AdminSystemOnly (см. ниже).
| Метод | Route | Permission | Документ |
|---|---|---|---|
| GET | /api/admin/v1/lots/{propertyObjectId}/override | catalog.overrides.view | get-property-object-override.md |
| PUT · DELETE | /api/admin/v1/lots/{propertyObjectId}/override | catalog.overrides.manage | put-property-object-override.md (DELETE — дельта снятия) |
| GET | /api/admin/v1/buildings/{buildingId}/override | catalog.overrides.view | get-building-override.md |
| PUT · DELETE | /api/admin/v1/buildings/{buildingId}/override | catalog.overrides.manage | put-building-override.md (DELETE — дельта снятия) |
| POST | /api/admin/v1/projections/rebuild | catalog.overrides.manage | rebuild-projections.md (runbook расхождений) |
CRUD пакетов продвижения (PromotionPackage) — admin-область контекста
promotion (permission promotion.packages.admin, ADR-0060); в
каталоге его нет.
Feature groups: admin-overrides (оверрайды), admin-operations (rebuild).
Модель актора и доступ
Область обслуживает один класс актора — систему, не человека. Актор определяется до выполнения бизнес-логики.
| Проверка | Значение |
|---|---|
| Actor type | ExternalSystem — администратор платформы через admin API |
| Auth | OAuth2 client-credentials (m2m), клиент pin-admin-system — ADR-0053 |
| Auth policy | AdminSystemOnly: токен выдан клиенту pin-admin-system; иначе 403 |
| Scope (RBAC) | платформа — данные не tenanted (ADR-0052 п.3), см. ниже |
| Записи о вызывающем пользователе | нет: identity знает систему, конкретного администратора несёт заголовок X-Admin-User-Id (actor-stamping) |
Соответствие permission-каталогу
Поверх AdminSystemOnly каждый метод сверяет каталожный permission
(../../../identity/domain/permission.md); для администратора
платформы каталог permission — справочное соответствие возможностей.
| Permission | Что разрешает |
|---|---|
catalog.overrides.view | Чтение оверрайдов объекта недвижимости/корпуса (get-*-override). |
catalog.overrides.manage | Мутации оверрайдов (PUT/DELETE) и операционный rebuild проекций. |
Обязательные заголовки области
| Заголовок | Где обязателен | Назначение |
|---|---|---|
X-Admin-User-Id | все методы | Guid администратора-инициатора внутри admin API. Actor-stamping (CreatedByUserId/UpdatedByUserId/DeletedByUserId) и subject deep audit: m2m-токен идентифицирует систему, заголовок — человека. Пустой/отсутствует → ValidationError/401. |
Idempotency-Key | мутации (POST/PUT/DELETE) | Guid; администратор платформы ретраит по таймауту. Повтор с тем же ключом → сохранённый результат (replay), шаги мутации не повторяются. Ключ бизнес-идемпотентности — admin:<operation>:{Idempotency-Key}. |
traceparent | все | Сквозной trace (correlation). |
Чтения (GET) идемпотентны по природе — Idempotency-Key к ним не применяется.
Правила идемпотентности и конкурентности мутаций:
- Повтор с тем же ключом и тем же телом → сохранённый результат (replay), шаги мутации и пересчёт
не выполняются повторно; ответ идентичен первому (включая
Version/Effective). - Тот же ключ с иным телом запроса → отказ
CONCURRENCY_CONFLICT(409): ключ уже связан с другим запросом, повторно использовать его для другой правки нельзя (защита от случайного «двойного смысла» одного ключа). - Окно хранения отметок идемпотентности — ограниченное (envelope: сутки); по истечении повтор с тем же ключом трактуется как новый запрос. Ретраить по таймауту следует в пределах окна.
- Конкурентный доступ (два администратора либо администратор против консюмера источников, правящих
те же карточки): при заданном
ExpectedVersionпроигравший по версии получаетCONCURRENCY_CONFLICTи перечитывает форму; безExpectedVersionпобеждает последняя запись override-полей, но пересчёт эффективных полей карточек остаётся согласованным (пересчёт детерминирован и атомарен). Ретрай — с тем жеIdempotency-Keyпосле перечитки.
Deep audit admin-мутаций
Каждая мутация области попадает в deep audit — оверрайды/ранжирование помечены как чувствительные (README сервиса, «Безопасность»):
- actor —
X-Admin-User-Id(+ m2m-клиентpin-admin-system); - объект — сущность и
Id(PropertyObjectDisplayOverride/BuildingDisplayOverride); - диф полей — before/after изменённых полей (значения цен в plain-логах маскируются, в audit — полностью);
Comment— служебный комментарий администратора «зачем правка» (internal, в public-ответы не попадает);- результат — success либо код ошибки.
Запись audit фиксируется в том же согласованном изменении, что и мутация. Чтения управленческих списков логируются как admin-доступ, но deep audit не требуют. Audit не tenanted (платформенные данные).
Tenancy: область не tenanted
Оверрайды — платформенные данные (ADR-0052 п.3): tenant-изоляция к области не применяется; при
необходимости кросс-tenant агрегаты читаются в системном scope. Пакеты/покупки продвижения — контекст
promotion (ADR-0060), в admin-области каталога их нет.
Общий формат ответа и ProblemDetails
- Успех — коды
200/201(create —201+Location). - Списки — всегда пагинированный контейнер (
Items,TotalCount,Page,PageSize), не сырой массив. - Ошибка —
ProblemDetailsс машинным полемcodeиtraceId; enum сериализуются строкой; голыхobject/jsonв контрактах нет.
Общий поток admin-мутации
Скелет, общий для всех мутаций области (метод описывает только дельту):
- Контекст и доступ. Policy
AdminSystemOnlyподтверждает m2m-клиентаpin-admin-system(401/403до бизнес-логики); заголовкиX-Admin-User-Id(actor) иIdempotency-Key. Повтор по ключуadmin:<operation>:{key}→ сохранённый результат (replay). - Проверка входа. Диапазоны/enum/длины/пустой
X-Admin-User-Id→ValidationError. Существование цели в проекциях (property_object_cards/project_cards) →CATALOG_*_NOT_FOUND; допустимость состояния (цельDelisted→ отказ); optimistic-checkExpectedVersion→CONCURRENCY_CONFLICT. - Что читаем. Целевой агрегат и затрагиваемые карточки (батч при каскаде); область не tenanted.
- Правила и маппинг. Создание/замена/архивация оверрайда, маппинг поле-в-поле, actor-stamping
(
CreatedByUserId/UpdatedByUserId=X-Admin-User-Id),Version++; пересчёт эффективных полей карточек (Effective = Override ?? SourceSnapshot, INV-6 display-override). - Что меняется. Создание/обновление/снятие (soft-delete) оверрайда и пересчёт затронутых
карточек в порядке логических связей; правка не должна затирать чужую (конкурентный доступ) —
конфликт →
CONCURRENCY_CONFLICT. - Согласованность. Агрегат + пересчитанные карточки + отметка идемпотентности + deep audit фиксируются в одном согласованном изменении (simplicity-first, ADR-0056: один контекст, одна БД). Интеграционные события область не публикует: оверрайды в Kafka не эмитятся.
- Результат. Успех →
200/201; отказ →ProblemDetailsс кодом.
Диаграмма (общий поток мутации)
Чтения области следуют тем же шагам без записи (шаги 4–6 не применяются).
Общие коды ошибок области
Сквозные коды (детали и method-specific CATALOG_* — в документах методов):
| Код | HTTP | Условие | Retry |
|---|---|---|---|
ValidationError | 400 | Синтаксис/диапазоны/enum; пустой X-Admin-User-Id/Idempotency-Key | no retry |
UNAUTHORIZED | 401 | Нет/просрочен m2m client-credentials токен | no retry |
FORBIDDEN | 403 | Клиент не pin-admin-system либо нет требуемого permission | no retry |
CONCURRENCY_CONFLICT | 409 | ExpectedVersion не совпал / гонка с консюмером или вторым админом / тот же Idempotency-Key с иным телом запроса | retry с тем же Idempotency-Key после перечитки |
Method-specific коды (CATALOG_*): оверрайды — CATALOG_OVERRIDE_*,
CATALOG_PROPERTY_OBJECT_NOT_FOUND, CATALOG_BUILDING_NOT_FOUND. Дом каждого — документ
соответствующего метода и раздел «Инварианты» дома сущности.
Наблюдаемость и совместимость (общие правила area)
- Logs:
admin-<entity>-<action>(id,adminUserId, изменённые поля без значений цен в plain-логе). Metrics:catalog_admin_operations_total{operation}, duration histogram,catalog_projection_recompute_total{trigger="override"}. Traces: span приёма → span обработки → span обращения к хранилищу, correlation — сквозной trace черезtraceparent. Каждая мутация — в deep audit. Dashboardcatalog-overview; runbook — rebuild проекций по id при расхожденииSourceSnapshot.ChessboardVersion. - Совместимость: новые optional-поля запросов/ответов — additive; удаление/переименование/смена типа
поля, изменение route/permission, ужесточение валидации — breaking (migration + rollout). Неизвестное
значение enum в теле/query →
ValidationError(строгий разбор).
Связанные документы
- Дома полей: ../../domain/display-override.md (INV-1..6 оверрайдов);
пакеты продвижения — контекст
promotion. - Решения: ADR-0053 (m2m client-credentials, deep audit), ADR-0052 (платформенная витрина, не tenanted), ADR-0056 (simplicity-first, пересчёт в транзакции).
- Соседние реестры: ../README.md (public),
../developers/(ранжирование застройщика); продвижение —promotion/; README сервиса — ../../README.md.