DELETE /api/messenger/blacklist/{blockedUserId} — Разблокировать пользователя
Удалить пользователя из чёрного списка актора: снимается запрет писать и звонить, политика пары
восстанавливается по существующему контакту. Удаляет запись BlacklistEntity
пары Owner → Blocked — hard delete (INV-BL-4; статуса нет, состояние = наличие строки). Идемпотентно:
отсутствие записи — no-op. Владелец — контекст messenger, feature group blacklist.
Назначение
Принять от авторизованного пользователя команду разблокировки собеседника и удалить запись чёрного списка;
восстановить CommunicationPolicy пары.
| Поле | Значение |
|---|
| Сервис/контекст | messenger |
| API area | public |
| Feature group | blacklist |
| Статус | draft |
| Документ | docs/06-services/messenger/api/blacklist/unblock-user.md |
Актор и доступ
| Проверка | Значение |
|---|
| Actor type | User |
| Auth policy / Permissions | messenger.blacklist.manage (JWT) |
| Scope (RBAC) | платформа (person-level, вне tenant, ADR-0052) |
| Record-level | OwnerUserId = actor — удаляется только своя запись |
| Tenant isolation | Запрос не tenanted (blacklist — вне tenant) |
| Audit | delete (hard); след разблокировки — в аудите операции UnblockUser (INV-BL-4), не soft-delete-полями |
HTTP-контракт
| Поле | Значение |
|---|
| Method | DELETE |
| Route | /api/messenger/blacklist/{blockedUserId} |
| Success status | 204 (разблокировано или уже не было — идемпотентно) |
| Idempotency header | не применяется (DELETE идемпотентен по природе) |
| Correlation | сквозной trace через traceparent |
Входные данные / параметры запроса
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|
OwnerUserId | identity claims | Guid | Да | Required (= actor) | Record-level (own-only) |
BlockedUserId | route | Guid | Да | != Guid.Empty | — |
Модель ответа
Тело успеха отсутствует (204 No Content). Ошибки — ProblemDetails (см. «Ошибки и failure modes»).
Алгоритм
- Контекст и доступ. Операцию выполняет авторизованный пользователь (
User); OwnerUserId = актор;
требуется право messenger.blacklist.manage (иначе MESSENGER_BLACKLIST_FORBIDDEN).
- Проверка входа.
BlockedUserId непустой, иначе ошибка валидации. Существование заблокированного
пользователя отдельно не проверяется — операция целится в запись блокировки, а не в пользователя.
- Загрузка записи. Запись блокировки пары
Owner → Blocked (уникальность ux_blacklist_owner_blocked);
отсутствие допустимо.
- Удаление или no-op. Запись не найдена → идемпотентный no-op: сразу успех (
204). Найдена → удалить
(hard delete, INV-BL-4; soft-delete-полей у агрегата нет). Политика пары CommunicationPolicy после
удаления восстанавливается по существующему ContactEntity: если контакт был
Accepted — вновь доступны сообщения и звонки.
- Реалтайм-уведомление. В той же согласованной операции только инициатору (
Owner) уходит realtime-
сигнал BlacklistChanged через SignalR-hub (UI возвращает кнопки «написать»/«позвонить»). Интеграционных
Kafka-событий нет (приватность, локальный эффект).
- Согласованность. Удаление и реалтайм-эффект фиксируются атомарно.
- Результат. HTTP
204 — и при реальном удалении, и при идемпотентном no-op.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|
| Integration events | none (blacklist приватен; Kafka/outbox не используется) |
| External calls | none |
| Cache / projections / search | CommunicationPolicy пары пересчитывается на чтении (deny снят, если не блокирует и обратная сторона) |
| Notifications | realtime BlacklistChanged в SignalR-hub — только Owner |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|
VALIDATION_FAILED | 400 | пустой BlockedUserId | «Некорректный идентификатор» | no retry |
UNAUTHORIZED | 401 | нет/просрочен JWT | «Требуется авторизация» | no retry |
MESSENGER_BLACKLIST_FORBIDDEN | 403 | нет permission messenger.blacklist.manage | «Недостаточно прав» | no retry |
Отсутствие записи блокировки — не ошибка (204, идемпотентность INV-BL-4). Обратная блокировка
Blocked → Owner, если существует, снятием этой записи не затрагивается — общение остаётся deny,
пока живёт хотя бы одна запись пары (INV-BL-3).
Совместимость и наблюдаемость
- Контракт стабилен (route +
204); изменение семантики удаления — breaking. Идемпотентность гарантирует
безопасный retry клиента.
- Logs:
user unblocked (OwnerUserId, BlockedUserId, existed); metrics:
messenger_blacklist_unblock_total{result}, latency p95; traces — сквозной trace (traceparent);
dashboard pin-messenger.
Acceptance Criteria
- Happy path. Given запись
Owner → Blocked существует; When DELETE /{blockedUserId}; Then 204,
запись удалена hard delete (INV-BL-4), CommunicationPolicy пары восстановлена по существующему
контакту, realtime BlacklistChanged только Owner.
- Идемпотентный no-op. Given записи блокировки нет; When
DELETE; Then 204 (no-op), без события.
- Встречная блокировка. Given существует обратная запись
Blocked → Owner; When DELETE Owner → Blocked;
Then 204, но общение остаётся deny, пока жива хотя бы одна запись пары (INV-BL-3).
- Восстановление по контакту. Given
Accepted-контакт пары и нет встречной блокировки; When DELETE;
Then CanMessage/CanCall снова allow.
- Валидация. Given
BlockedUserId = Guid.Empty; When DELETE; Then 400 VALIDATION_FAILED.
- Доступ. Given нет
messenger.blacklist.manage; When DELETE; Then 403 MESSENGER_BLACKLIST_FORBIDDEN;
удаляется только своя запись (record-level).
Обратные ссылки
Автоссылки: где используется этот документ.
Связанные документы