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

GET /api/v1/categories — Справочник категорий постов + eligibility актора

Назначение

Возвращает публичный справочник Active-категорий новостной ленты (PostCategory, post-category.md) в порядке показа, с per-actor признаком CanPublish — «может ли текущий пользователь публиковать в категорию». Обслуживает два сценария фронта: (а) фильтры глобальной ленты — полный список Active-категорий (аноним/пользователь); (б) форма публикации — только доступные автору категории (onlyEligible = true). Справочник — стабильный публичный ключ ленты, правится редко и только администратором платформы (ADR-0053), поэтому ответ кэш-дружелюбен (HTTP-кэш фронта/CDN).

Metadata

ПолеЗначение
Сервис/контекстnews (Pin.News.Api)
API areapublic
Feature groupcategories
Статусdraft
Документdocs/06-services/news/api/public/categories/list-categories.md

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

ПроверкаЗначение
Actor typeAnonymous (гость ленты) или User (авторизованный — JWT identity)
Auth policy / Permissionsnone — публичное чтение справочника; permission не требуется. onlyEligible/CanPublish используют actor.UserType из JWT, если токен есть
Scope (RBAC)не применяется — платформенный справочник (не tenanted, ADR-0052 п.3)
Record-levelне применяется
Tenant isolationне применяется — PostCategory не tenanted (платформенный словарь, post-category.md); request не tenanted
Auditnone (публичное чтение reference data; PII нет)

HTTP-контракт

ПолеЗначение
MethodGET
Route/api/v1/categories
Success status200 (в т.ч. пустой результат — Items = [], не ошибка)
Idempotency headerне применяется (safe GET)
Correlationсквозной trace через traceparent

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

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
OnlyEligiblequery (onlyEligible)bool?Нет (default false)Только true/false; при true — вернуть лишь категории с CanPublish = true
ActorUserTypeidentity claimsPostAuthorType?НетЗаполнен для User из JWT; null для Anonymous — в body/query не принимаетсяиз actor-context, не из запроса

Пагинация не экспонируется: справочник — десятки строк (post-category.md), возвращается одной страницей.

Модель ответа (список CategoryItem)

Список возвращается одной страницей: Items + Page = 1, PageSize = TotalCount = число элементов в Items. Финансовых данных и PII в модели нет.

Модель CategoryItem

ПолеТипОбязательностьИсточникMaskingОписание
IdGuidДаPostCategory.IdИдентификатор категории (стабильный ключ фильтра ленты и привязки поста).
SlugstringДаPostCategory.SlugKebab-код (construction-progress, investor-story, …), INV-CAT-001.
TitlestringДаPostCategory.TitleОтображаемое название.
Descriptionstring?НетPostCategory.DescriptionПояснение назначения категории (может отсутствовать).
AllowedAuthorTypesIReadOnlyList<string> (enum PostAuthorType)ДаPostCategory.AllowedAuthorTypes.Types (owned VO CategoryAuthorTypes)Типы авторов, которым разрешена публикация (1..5, enum строкой).
CanPublishboolДаcomputed (шаг 4)true, если ActorUserType ∈ AllowedAuthorTypes; для Anonymous всегда false.

Алгоритм

  1. Контекст и доступ. Публичное чтение справочника: актор — Anonymous (JWT опционален) или User; permission не требуется. Тип автора ActorUserType берётся из JWT для User, для Anonymousnull. Справочник платформенный (не tenanted).
  2. Проверка входа. OnlyEligible — булев флаг (default false); нераспознанный литерал → ValidationError (400). Иных входных ограничений нет; OnlyEligible = true для Anonymous — валидный запрос (результат — пустой список).
  3. Выборка. Вернуть все категории со статусом Active в порядке показа (SortOrder ↑, индекс ix_post_categories_status); AllowedAuthorTypes разворачивается в enum-строки. Выборка глобальна (не tenanted), потому кэш-дружелюбна.
  4. Признак публикации и фильтр. Для каждой категории вычислить CanPublish: для UserActorUserType входит в AllowedAuthorTypes (тот же eligibility-предикат, что и гейт публикации INV-CAT-002); для Anonymous — всегда false. При OnlyEligible = true — оставить только категории с CanPublish = true (для Anonymous → пусто); порядок по SortOrder сохраняется. Справочник read-only (запись только admin-API, INV-CAT-004); событий не порождает.
  5. Результат. Список CategoryItem200 (пусто → Items = [], TotalCount = 0). Справочник — source of truth; при чтении из HTTP-кэша фронта/CDN возможна задержка до TTL / до admin-инвалидации (изменения редки, ADR-0056).

Диаграмма

Побочные эффекты

ТипДетали
Integration eventsnone (read-only)
External callsnone
Cache / projections / searchHTTP-кэш ответа: Anonymous + OnlyEligible=falseCache-Control: public, max-age + ETag (CDN/браузер); User/OnlyEligible=trueprivate (ответ зависит от ActorUserType). Server-side Redis не используется (ADR-0056); индекс ix_post_categories_status
Notificationsnone

Ошибки и failure modes

КодHTTP statusУсловиеСообщениеRetry
ValidationError400Нераспознанный литерал onlyEligibleСписок ошибок полейno retry
— (auth)401Передан невалидный/просроченный JWT (аноним без токена — допустим)без тела схемы (фронт очищает сессию)re-login
— (пустой результат, не ошибка)200Под фильтром категорий нет (напр. onlyEligible=true у анонима)Items = [], TotalCount = 0

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

  • Новые optional поля CategoryItem — additive; удаление/переименование/смена типа — breaking (migration + rollout фронта). Enum PostAuthorType сериализуется строкой; клиент обязан игнорировать неизвестные значения (unknown enum handling — показать код без бейджа).
  • Logs: categories-listed (OnlyEligible, ActorUserType, count; PII/суммы отсутствуют); metrics: news_public_operations_total{operation="list-categories"}, latency histogram; traces — сквозной trace через traceparent; dashboard news-overview.

Acceptance Criteria

  • Полный список для анонима. Дано: в справочнике есть категории со Status = Active и Archived. Когда: Anonymous вызывает GET /api/v1/categories без onlyEligible. Тогда: 200, в Items — только Active-категории в порядке SortOrder ↑, у каждой CanPublish = false; Archived-категории отсутствуют.
  • Eligibility авторизованного. Дано: User с ActorUserType = DeveloperManager. Когда: GET без фильтра. Тогда: CanPublish = true ровно у категорий, чей AllowedAuthorTypes содержит DeveloperManager (тот же предикат, что INV-CAT-002), у прочих — false.
  • Форма публикации. Дано: User с ActorUserType = Agent. Когда: GET ?onlyEligible=true. Тогда: в Items только категории с CanPublish = true, порядок SortOrder сохранён.
  • Аноним + onlyEligible. Дано: Anonymous. Когда: GET ?onlyEligible=true. Тогда: 200, Items = [], TotalCount = 0 — пустой результат, не ошибка.
  • Некорректный флаг. Дано: любой актор. Когда: onlyEligible = нераспознанный литерал. Тогда: 400 ValidationError, тело справочника не отдаётся.
  • Просроченный токен. Дано: передан невалидный/просроченный JWT. Когда: GET. Тогда: 401 (аноним без токена при этом остаётся валидным сценарием).
  • Свежесть после admin-правки. Дано: администратор изменил Title/SortOrder категории. Когда: клиент читает из HTTP-кэша фронта/CDN. Тогда: возможна задержка до TTL/до admin-инвалидации — новое значение гарантированно видно после истечения кэша (изменения редки, ADR-0056).

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

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

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