Перейти к основному содержимому

GET /api/v1/agent/calendar/events/{id} — Карточка события календаря

Назначение

Отдаёт владельцу календаря (агенту агентства или консультанту ПИН) полную карточку одного события: все поля агрегата, признак пересечения HasConflicts и текущую Version для последующих операций изменения (update-event.md, reschedule-event.md). Используется при открытии события из календарной сетки (list-events.md) и как перечитка после проигрыша конкурентной правки.

Metadata

ПолеЗначение
Сервис/контекстcalendar
API areaagent
Feature groupcalendar-events
Статусdraft
Документdocs/06-services/calendar/api/agent/get-event.md

Актор и доступ

ПроверкаЗначение
Actor typeUser (агент агентства / консультант ПИН — владелец календаря)
Auth policy / Permissionscalendar.events.view
Scope (RBAC)организация: компания актора (агентство / ПИН)
Record-levelвладелец: OwnerUserId = ActorUserId из токена (sub). Событие другого агента того же tenant → CAL_EVENT_ACCESS_DENIED (календарь команды — read-only операция ../agency/list-team-events.md, calendar.team.view)
Tenant isolationTenantId из токена (tenant_id); событие чужого tenant неотличимо от несуществующего (CAL_EVENT_NOT_FOUND)
Auditread: актор и EventId фиксируются в trace; Title/Description/Location (могут содержать ПДн клиента) в логи не пишутся

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/agent/calendar/events/{id}
Success status200
Idempotency headerнет (безопасное чтение)
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdтокен (sub)GuidДаRequiredrecord-level проверка владельца
TenantIdтокен (tenant_id)GuidДаRequiredTenant isolation
IdrouteGuidДавалидный Guid

Модель ответа — CalendarEventModel

Единичная CalendarEventModel — дом модели и полная таблица полей (включая правило вычисления HasConflicts по INV-CAL-6) — list-events.md. Отличий от списочной проекции нет: карточка возвращает те же поля, HasConflicts вычисляется по тому же правилу строгого пересечения с активными (Planned/InProgress) интервальными событиями владельца.

Алгоритм

Тривиальное чтение: найти событие по Id в tenant актора — не найдено (или чужой tenant) → CAL_EVENT_NOT_FOUND; событие принадлежит другому владельцу того же tenant → CAL_EVENT_ACCESS_DENIED; иначе вычислить HasConflicts (для интервального события — по активным интервальным событиям владельца) и вернуть 200 с CalendarEventModel. Данные отдаются из source of truth calendar — актуальны на момент чтения; Version из ответа передаётся в операции изменения (оптимистическая блокировка).

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Id — не валидный Guid«Некорректный идентификатор»no retry
UNAUTHORIZED401нет/просрочен JWTno retry
FORBIDDEN403нет права calendar.events.viewno retry
CAL_EVENT_ACCESS_DENIED403событие существует в tenant актора, но принадлежит другому владельцу«Нет доступа к событию»no retry
CAL_EVENT_NOT_FOUND404событие не существует или принадлежит другому tenant«Событие не найдено»no retry

Acceptance-критерии

  • Happy path. Дано: событие владельца в любом статусе (включая Completed/Cancelled). Когда: GET по его Id. Тогда: 200, все поля CalendarEventModel заполнены из агрегата, Version актуальна.
  • HasConflicts. Дано: событие пересекается с другим активным интервальным событием владельца. Тогда: HasConflicts = true; после отмены контрагента повторный GET возвращает HasConflicts = false.
  • Чужое событие в tenant. Дано: Id события другого агента того же агентства. Тогда: 403 CAL_EVENT_ACCESS_DENIED.
  • Чужой tenant / несуществующее. Дано: Id события другого tenant либо случайный Guid. Тогда: 404 CAL_EVENT_NOT_FOUND — существование чужих событий не раскрывается.
  • Терминальное событие читается. Дано: событие Cancelled с CancelReason. Тогда: 200, Status = Cancelled, CancelReason заполнен (чтение терминальных не ограничено — иммутабельны только изменения).

Совместимость и наблюдаемость

  • Модель — общая CalendarEventModel (list-events.md): новые поля — additive nullable; удаление/переименование/смена типа — breaking. Новые значения EventKind/EventStatus/EventOrigin в ответе клиент обязан переживать (forward-compatible отображение «прочее»).
  • Logs: TenantId, ActorUserId, EventId, исход (без содержимого полей); metrics: calendar_api_request_duration_seconds{route}; traces — сквозной traceparent.

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы