feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6) #167

Merged
longjacksonle merged 1 commit from feat/pms-139-sweep-hubs into main 2026-06-11 14:21:09 +02:00

What

Batch 6 of the PMS-139 typed-tenant sweep: the first of the three cross-module hubs - the audit module. Migrating it is what lets the already-swept modules shed their transitional tenant_id.get() unwraps.

How

  • audit_write writer now takes tenant_id: TenantId. The transparent newtype binds as its inner Uuid, so the INSERT body is unchanged.
  • AuditService read/append methods take TenantId: append and list_entity_history directly, list as Option<TenantId> (the query builder binds the unwrapped value transparently, so no body change). Audit routes derive scope via u.tenant().
  • Global cleanup: every swept module (assets, projects, contacts, billing, contracts, tenants) drops the tenant_id.get() it passed at the audit call site. The codebase-wide tenant_id.get() count falls 39 -> 5, and all 5 survivors are correct: the PaymentResponse / TenantUsage DTO fields and the tenants update entity_id carry a plain Uuid; AuditCtx::system stays a Uuid context bag; the one remaining transitional unwrap is rmm's TicketService call (clears when tickets is swept).
  • Bridges (TenantId::from_trusted(..), documented on the audit_write doc comment): the not-yet-swept auth and tickets modules, the tenants create path (a minted id), and the audit_auth_event helper.
  • Middleware: the audit logger now passes TenantScoped::tenant(user) instead of the raw auth_state.tenant_id. AuditCtx deliberately keeps its Option<Uuid> field (it is the request-extractor context, tolerant of unauthenticated/system callers).

Verification

  • cargo check --all-targets, cargo clippy --all-targets: clean.
  • Tests pass: audit (4), audit_hooks (3), audit_list (1), auth (14), tickets (2), contacts (7), assets (4), projects (4), billing (3), recurring_invoicing (5).
  • cargo test --doc: compile_fail newtype guard holds.

Progress: 14 of ~17 modules migrated. Two hubs remain - notifications (the dispatch hub) and tickets (the TicketService hub). Sweeping those clears the last transitional .get() (rmm's TicketService call).

🤖 Generated with Claude Code

## What Batch 6 of the PMS-139 typed-tenant sweep: the first of the three cross-module hubs - the **audit** module. Migrating it is what lets the already-swept modules shed their transitional `tenant_id.get()` unwraps. ## How - **`audit_write` writer** now takes `tenant_id: TenantId`. The transparent newtype binds as its inner `Uuid`, so the INSERT body is unchanged. - **`AuditService`** read/append methods take `TenantId`: `append` and `list_entity_history` directly, `list` as `Option<TenantId>` (the query builder binds the unwrapped value transparently, so no body change). Audit routes derive scope via `u.tenant()`. - **Global cleanup**: every swept module (assets, projects, contacts, billing, contracts, tenants) drops the `tenant_id.get()` it passed at the audit call site. The codebase-wide `tenant_id.get()` count falls **39 -> 5**, and all 5 survivors are correct: the `PaymentResponse` / `TenantUsage` DTO fields and the `tenants` update `entity_id` carry a plain `Uuid`; `AuditCtx::system` stays a `Uuid` context bag; the one remaining transitional unwrap is rmm's `TicketService` call (clears when tickets is swept). - **Bridges** (`TenantId::from_trusted(..)`, documented on the `audit_write` doc comment): the not-yet-swept `auth` and `tickets` modules, the `tenants` create path (a minted id), and the `audit_auth_event` helper. - **Middleware**: the audit logger now passes `TenantScoped::tenant(user)` instead of the raw `auth_state.tenant_id`. `AuditCtx` deliberately keeps its `Option<Uuid>` field (it is the request-extractor context, tolerant of unauthenticated/system callers). ## Verification - `cargo check --all-targets`, `cargo clippy --all-targets`: clean. - Tests pass: `audit` (4), `audit_hooks` (3), `audit_list` (1), `auth` (14), `tickets` (2), `contacts` (7), `assets` (4), `projects` (4), `billing` (3), `recurring_invoicing` (5). - `cargo test --doc`: `compile_fail` newtype guard holds. Progress: 14 of ~17 modules migrated. Two hubs remain - `notifications` (the `dispatch` hub) and `tickets` (the `TicketService` hub). Sweeping those clears the last transitional `.get()` (rmm's `TicketService` call). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6)
Some checks failed
Check / fmt + clippy + compile + tests (pull_request) Failing after 12s
Create release / Create release from merged PR (pull_request) Has been skipped
E2E (staging) / Playwright against staging (pull_request) Successful in 40s
Build OCI container / Build and push mokosh-api image (push) Successful in 3m23s
f607764e73
Sweep batch 6: the first of the three cross-module hubs. The `audit_write` writer and the `AuditService` read/append methods (`append`, `list`, `list_entity_history`) now take the typed `TenantId` (`list` takes `Option<TenantId>`), and the audit routes derive scope via `u.tenant()`. The transparent newtype binds and `%tenant_id` traces unchanged, so the query bodies are untouched.

Because `audit_write` is now typed, every already-swept module (assets, projects, contacts, billing, contracts, tenants) drops the transitional `tenant_id.get()` it was passing at the audit call site - the scope flows straight through. The global `tenant_id.get()` count falls from 39 to 5, and the survivors are all correct: the `PaymentResponse` / `TenantUsage` DTO fields and the `tenants` update `entity_id` carry a plain `Uuid`, `AuditCtx::system` stays a `Uuid` context bag, and the single remaining transitional unwrap is rmm's `TicketService` call (cleared when tickets is swept).

Four caller groups still hold a bare `Uuid` and bridge through `TenantId::from_trusted(..)`, documented on the `audit_write` doc comment: the `auth` and `tickets` modules (not yet swept), the `tenants` create path (a freshly minted id), and the `audit_auth_event` helper. The audit-logging middleware now passes `TenantScoped::tenant(user)` instead of the raw `auth_state.tenant_id`. `AuditCtx` deliberately keeps its `Option<Uuid>` field - it is the request-extractor context, tolerant of unauthenticated and system callers.

Verification: `cargo check --all-targets` and `cargo clippy --all-targets` clean; `audit` (4), `audit_hooks` (3), `audit_list` (1), `auth` (14), `tickets` (2), `contacts` (7), `assets` (4), `projects` (4), `billing` (3), `recurring_invoicing` (5) all pass; the `compile_fail` newtype doctest guard holds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch feat/pms-139-sweep-hubs 2026-06-11 14:21:09 +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!167
No description provided.