POST /api/v1/posts/{id}/comments-settings — переключить комментарии
Назначение
Дать автору (или менеджеру компании в scope поста) закрыть либо открыть обсуждение под своим постом:
установить Post.CommentsEnabled = enabled. При выключении существующие комментарии сохраняются и
остаются видимыми (дерево не трогается), а добавление новых запрещается гейтом INV-NWS-006 /
INV-CMT-001 в add-comment.md. Операция без интеграционных
событий и без пересчёта поиска/счётчиков.
| Поле | Значение |
|---|
| Сервис/контекст | news |
| API area | authoring |
| Feature group | authoring |
| Статус | draft |
| Документ | docs/06-services/news/api/authoring/toggle-comments.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User (аутентифицированный автор; guest — 401) |
| Auth policy / Permissions | JWT identity; record-level (ниже) ИЛИ permission news.posts.manage (INV-NWS-007) |
| Scope (RBAC) | self + company scope (менеджер компании — по AuthorCompanyId поста) |
| Record-level | actor.UserId == Post.AuthorUserId, либо менеджер компании в scope Post.AuthorCompanyId, либо news.posts.manage; иначе FORBIDDEN (INV-NWS-007) |
| Tenant isolation | TenantId = Post.AuthorCompanyId (nullable; null — пост инвестора/сотрудника вне tenant, ADR-0052); record-level — первичный гейт мутации |
| Audit | update: UpdatedByUserId = actor.UserId, Version++; PII нет |
HTTP-контракт
| Поле | Значение |
|---|
| Method | POST |
| Route | /api/v1/posts/{id}/comments-settings |
| Success status | 200 |
| Idempotency header | не применяется — операция идемпотентна по природе (установка булева значения; повтор с тем же enabled даёт тот же результат) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
Единая секция: HTTP-источник + типизированное поле request-модели SetCommentsEnabledCommand.
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
PostId | route (id) | Guid | Да | != Guid.Empty; существующий неудалённый Post (шаг 2) | — |
Enabled | body | bool | Да | обязателен в теле; true — открыть, false — закрыть приём новых | — |
ActorUserId | identity claims | Guid | Да | из JWT актора | стамп мутации, не из тела |
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|
PostId | Guid | Да | Post.Id | — | Идентификатор поста. |
CommentsEnabled | bool | Да | Post.CommentsEnabled (после мутации) | — | Актуальное состояние приёма новых комментариев. |
Алгоритм
- Контекст и доступ. Выполняет автор поста или менеджер его компании в scope (
User); гость → 401.
Требуется record-level (ниже) или право news.posts.manage (INV-NWS-007).
- Проверка входа и доступа.
PostId != Guid.Empty, Enabled присутствует в теле, иначе
VALIDATION_FAILED. Найти пост по PostId; если не найден или Archived (soft-deleted) →
NEWS_POST_NOT_FOUND. Флаг переключается в любом живом статусе (Draft/Published/Unpublished);
Archived уже отсечён. Record-level (INV-NWS-007): актор — автор, либо менеджер компании в scope
AuthorCompanyId, либо носитель news.posts.manage; иначе → FORBIDDEN.
- Переключение флага. Установить
CommentsEnabled = Enabled (идемпотентно — повтор с тем же значением
даёт тот же результат), actor-stamp. Статус поста не меняется, дерево комментариев не затрагивается. При
выключении (false) — «мягкий» гейт: существующие комментарии остаются Visible и читаемы, а добавление
новых блокируется на входе add-comment проверкой INV-NWS-006 (Post.CommentsEnabled) / INV-CMT-001.
Событий нет: подписчиков не уведомляем, поиск и денормализованные счётчики от флага не зависят.
- Результат.
CommentsSettingsResult { PostId, CommentsEnabled } → 200.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|
| Integration events | none — переключение комментариев событий не публикует |
| External calls | none |
| Cache / projections / search | none — search-проекция и CommentsCount от флага не зависят; существующие комментарии не пересчитываются |
| Notifications | none |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
VALIDATION_FAILED | 400 | PostId == Guid.Empty / отсутствует Enabled в теле | «Проверьте параметры запроса» | no retry |
NEWS_POST_NOT_FOUND | 404 | пост не найден либо Archived (soft-delete-фильтр) | «Пост не найден» | no retry |
FORBIDDEN | 403 | актор не автор, не менеджер компании поста и без news.posts.manage (INV-NWS-007) | «Недостаточно прав» | no retry |
Предусловия и постусловия
Предусловия (истинны до сценария):
- актор — автор поста, менеджер компании в scope
AuthorCompanyId либо носитель news.posts.manage (INV-NWS-007);
- пост существует и в живом статусе
Draft/Published/Unpublished (Archived отсечён).
Постусловия (гарантированы после переключения):
Post.CommentsEnabled = Enabled, actor-stamp UpdatedByUserId, доменная версия +1; статус поста и дерево комментариев не изменены;
- при
Enabled = false существующие комментарии остаются Visible и читаемы; добавление новых блокируется гейтом INV-NWS-006 / INV-CMT-001 на входе add-comment;
- интеграционных событий, пересчёта поиска и счётчиков нет — флаг не влияет на
news-posts и CommentsCount.
Операция полностью локальна: синхронных кросс-сервисных вызовов и исходящих событий нет — подписчиков не уведомляем, единственный «потребитель» флага — операция add-comment, читающая CommentsEnabled при добавлении.
Acceptance Criteria
- Закрытие обсуждения. Дано: автор, пост
Published с CommentsEnabled = true. Когда: POST /api/v1/posts/{id}/comments-settings {enabled:false}. Тогда: 200, CommentsEnabled = false, существующие комментарии видимы, дерево не тронуто.
- Открытие обсуждения. Дано: пост с
CommentsEnabled = false. Когда: POST {enabled:true}. Тогда: 200, CommentsEnabled = true.
- Гейт новых комментариев. Дано:
CommentsEnabled = false. Когда: кто-то вызывает add-comment. Тогда: добавление отклонено гейтом INV-NWS-006 / INV-CMT-001 (см. add-comment.md).
- Идемпотентный повтор. Дано: то же значение
Enabled. Когда: повтор POST. Тогда: тот же 200, результат не меняется.
- Архивный/несуществующий пост. Дано: пост
Archived/soft-deleted или отсутствует. Когда: POST. Тогда: 404 NEWS_POST_NOT_FOUND.
- Не владелец. Дано: актор не автор/не менеджер scope и без
news.posts.manage (INV-NWS-007). Когда: POST. Тогда: 403 FORBIDDEN.
- Невалидный вход. Дано:
PostId == Guid.Empty или отсутствует Enabled в теле. Когда: POST. Тогда: 400 VALIDATION_FAILED.
- Гость. Дано: запрос без JWT. Когда:
POST. Тогда: 401.
Совместимость и наблюдаемость
CommentsSettingsResult — стабильный контракт; новые optional поля запроса/ответа — additive,
удаление/переименование/смена типа — breaking (migration + rollout). Семантика «выключено» —
запрет новых комментариев без удаления существующих — часть контракта (INV-NWS-006).
- Logs:
comments-settings toggled (PostId, CommentsEnabled, ActorUserId); metrics:
news_comments_settings_total{enabled}, latency p95; traces — сквозной trace через traceparent;
dashboard news-overview; runbook — общий authoring-troubleshooting (record-level отказы).
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы