Favorites API#

Per-user report favorites. Scope is always the caller (JWT sub) — there is no admin route for viewing / editing another user’s favorites.

  • Endpoints:
    • GET /api/v1/users/me/favorites
    • POST /api/v1/users/me/favorites { reportId }
    • DELETE /api/v1/users/me/favorites/{reportId}
  • Auth: [Authorize]
  • Feature flag: FeatureFlags:EnableFavoritesWrites (default false)
  • Jira: BFC-23
  • Legacy source: wmReportsUserFavorites (composite PK FkRptId + FkUserId)

Flow — add favorite#

flowchart TD
    Req[POST /users/me/favorites] --> Flag{EnableFavoritesWrites?}
    Flag -->|false| R404[404]
    Flag -->|true| JWT[Extract sub claim as userId]
    JWT --> Svc[FavoritesService.Add]
    Svc --> Chk{Row exists?}
    Chk -->|yes| R204[204 No Content — idempotent]
    Chk -->|no| Ins[INSERT wmReportsUserFavorites]
    Ins --> R201[201 Created]

Rules#

  • Own-scope enforcement: caller identity comes from JWT sub, never from URL. A malicious client cannot construct a route to touch someone else’s favorites — the route shape doesn’t allow it.
  • Idempotent add: re-adding an existing favorite returns 204, not 409. Simpler client logic.
  • Composite PK prevents duplicates at the DB layer regardless of race conditions.
  • Follow-up: module pinning (wmFavorite table) is a separate scope if needed later — not shipped in Phase 1.