feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7) #169

Merged
longjacksonle merged 1 commit from feat/pms-139-sweep-notifications into main 2026-06-11 14:26:08 +02:00

What

Batch 7 of the PMS-139 typed-tenant sweep: the second cross-module hub - the notifications module.

How

  • dispatch and the rest of NotificationsService now take tenant_id: TenantId; notifications routes derive scope via u.tenant(). Transparent newtype -> query bodies unchanged. No audit-hub calls in this module, so nothing to unwrap.
  • Dispatcher worker (NotificationDispatcherWorker) stays a Uuid-internal struct: it reads pending rows off the notifications table and its deliver/lookup_user_email helpers never cross the public service boundary, like the calendar/sla workers.
  • Six dispatch bridges via TenantId::from_trusted(..): the not-yet-swept auth (password-reset + welcome) and tickets (note-added + automation) modules, plus the cross-tenant calendar reminder and sla sweep workers (which dispatch off DB-projected row tenants). The auth/tickets bridges clear when those modules are swept; the worker bridges are the standing cross-tenant pattern.
  • Global tenant_id.get() count unchanged at 5 (dispatch callers passed raw Uuids, not .get() unwraps): the PaymentResponse/TenantUsage DTO fields and the tenants update entity_id are plain Uuid, AuditCtx::system is a Uuid context bag, and the one transitional survivor is rmm's TicketService call (clears when tickets is swept).

Verification

  • cargo check --all-targets, cargo clippy --all-targets: clean.
  • Tests pass: notifications (2), notifications_backfill (1), sla_notify (1), calendar_reminders (1), tickets (2), auth (14).
  • cargo test --doc: compile_fail newtype guard holds.
  • (One auth MFA test flaked once on sqlx test-db provisioning and passed on rerun - unrelated to this change.)

Progress: 15 of ~17 modules migrated. One hub remains - tickets (the TicketService hub). Sweeping it clears the last transitional .get() (rmm's call) and lets auth/tickets shed their from_trusted audit/dispatch bridges, completing PMS-139.

🤖 Generated with Claude Code

## What Batch 7 of the PMS-139 typed-tenant sweep: the second cross-module hub - the **notifications** module. ## How - **`dispatch`** and the rest of `NotificationsService` now take `tenant_id: TenantId`; notifications routes derive scope via `u.tenant()`. Transparent newtype -> query bodies unchanged. No audit-hub calls in this module, so nothing to unwrap. - **Dispatcher worker** (`NotificationDispatcherWorker`) stays a `Uuid`-internal struct: it reads pending rows off the `notifications` table and its `deliver`/`lookup_user_email` helpers never cross the public service boundary, like the calendar/sla workers. - **Six `dispatch` bridges** via `TenantId::from_trusted(..)`: the not-yet-swept `auth` (password-reset + welcome) and `tickets` (note-added + automation) modules, plus the cross-tenant `calendar` reminder and `sla` sweep workers (which dispatch off DB-projected row tenants). The auth/tickets bridges clear when those modules are swept; the worker bridges are the standing cross-tenant pattern. - Global `tenant_id.get()` count unchanged at **5** (dispatch callers passed raw `Uuid`s, not `.get()` unwraps): the `PaymentResponse`/`TenantUsage` DTO fields and the `tenants` update `entity_id` are plain `Uuid`, `AuditCtx::system` is a `Uuid` context bag, and the one transitional survivor is rmm's `TicketService` call (clears when tickets is swept). ## Verification - `cargo check --all-targets`, `cargo clippy --all-targets`: clean. - Tests pass: `notifications` (2), `notifications_backfill` (1), `sla_notify` (1), `calendar_reminders` (1), `tickets` (2), `auth` (14). - `cargo test --doc`: `compile_fail` newtype guard holds. - (One `auth` MFA test flaked once on sqlx test-db provisioning and passed on rerun - unrelated to this change.) Progress: 15 of ~17 modules migrated. One hub remains - `tickets` (the `TicketService` hub). Sweeping it clears the last transitional `.get()` (rmm's call) and lets `auth`/`tickets` shed their `from_trusted` audit/dispatch bridges, completing PMS-139. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7)
Some checks failed
Check / fmt + clippy + compile + tests (pull_request) Failing after 11s
E2E (staging) / Playwright against staging (pull_request) Successful in 34s
Create release / Create release from merged PR (pull_request) Has been skipped
Build OCI container / Build and push mokosh-api image (push) Successful in 3m19s
d5c61336a7
Sweep batch 7: the second of the three cross-module hubs. The `dispatch` entry point and the rest of `NotificationsService` now take the typed `TenantId`, and the notifications routes derive scope via `u.tenant()`. The transparent newtype binds and traces unchanged, so the query bodies are untouched. The module has no audit-hub calls, so nothing to unwrap there.

The cross-tenant dispatcher worker (`NotificationDispatcherWorker`) stays a `Uuid`-internal struct - it reads pending rows straight off the `notifications` table and its `deliver`/`lookup_user_email` helpers never cross the public service boundary, exactly like the calendar/sla workers.

Six `dispatch` callers still hold a bare `Uuid` and bridge through `TenantId::from_trusted(..)`: the not-yet-swept `auth` (password-reset + welcome) and `tickets` (note-added + automation) modules, and the cross-tenant `calendar` reminder and `sla` sweep workers (which dispatch off DB-projected row tenants). The auth/tickets bridges disappear when those modules are swept; the worker bridges are the standing cross-tenant pattern.

The global `tenant_id.get()` count is unchanged at 5 (dispatch callers passed raw `Uuid`s, not `.get()` unwraps): the `PaymentResponse`/`TenantUsage` DTO fields and the `tenants` update `entity_id` carry a plain `Uuid`, `AuditCtx::system` stays a `Uuid` context bag, and the one transitional survivor is rmm's `TicketService` call (cleared when tickets - the last hub - is swept).

Verification: `cargo check --all-targets` and `cargo clippy --all-targets` clean; `notifications` (2), `notifications_backfill` (1), `sla_notify` (1), `calendar_reminders` (1), `tickets` (2), `auth` (14) all pass; the `compile_fail` newtype doctest guard holds. (One `auth` MFA test flaked once on sqlx test-db provisioning and passed on rerun - unrelated to this change.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/pms-139-sweep-notifications 2026-06-11 14:26:08 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/mokosh-server!169
No description provided.