feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8) #171

Merged
longjacksonle merged 1 commit from feat/pms-139-sweep-tickets into main 2026-06-11 14:35:04 +02:00

What

Batch 8 of the PMS-139 typed-tenant sweep - the finale. The last cross-module hub, tickets (the TicketService), now takes TenantId, completing the migration.

How

  • TicketService (service + automation) takes tenant_id: TenantId; tickets routes derive scope via user.tenant(). Transparent newtype -> query bodies unchanged. FromRow projections keep their Uuid fields.
  • Bridges collapse: the three audit_write calls and the ticket.note_added / ticket.automation.notify dispatches drop the TenantId::from_trusted(..) wrappers added in batches 6-7 and hand the scope straight through.
  • rmm: the alert path drops the tenant_id.get() it passed to create_ticket - this was the last transitional unwrap in the codebase.
  • portal: the three ticket feeds (get/list/create) bridge their verified contact-JWT tenant via from_trusted, like the KB/invoice feeds. seed: its create_ticket bridges the trusted seeder's id.
  • Survivors (6 tenant_id.get(), all genuine Uuid boundaries, none transitional): PaymentResponse/TenantUsage DTO fields, the tenants update entity_id, AuditCtx::system (a Uuid context bag), and the ticket-automation webhook payload (serialised as Uuid).
  • dev-docs cross-cutting issue #8 marked resolved; the stale "always takes tenant_id: Uuid" architecture notes corrected.

Verification

  • cargo check --all-targets, cargo clippy --all-targets: clean.
  • Full cargo test suite passes except the two pre-existing tests/settings.rs billing-404 failures (disabled_module_returns_404_on_route_access, enabled_module_response_unchanged), which reproduce identically on clean main (verified in batch 4) and are unrelated to this change. Spot-run green: tickets (2), rmm (3), seed_demo (2), audit (4), audit_hooks (3), plus the full integration sweep.
  • cargo test --doc: compile_fail newtype guard holds.

PMS-139 complete

All request-scoped modules and all three hubs (audit, notifications, tickets) take TenantId. A handler that forgets to pass the authenticated scope no longer compiles. The remaining from_trusted sites are the deliberate ones (machine HMAC webhook, portal contact sessions, trusted seeders, cross-tenant workers, the auth login path), each noted in-code.

🤖 Generated with Claude Code

## What Batch 8 of the PMS-139 typed-tenant sweep - **the finale**. The last cross-module hub, `tickets` (the `TicketService`), now takes `TenantId`, completing the migration. ## How - **`TicketService`** (service + automation) takes `tenant_id: TenantId`; tickets routes derive scope via `user.tenant()`. Transparent newtype -> query bodies unchanged. FromRow projections keep their `Uuid` fields. - **Bridges collapse**: the three `audit_write` calls and the `ticket.note_added` / `ticket.automation.notify` dispatches drop the `TenantId::from_trusted(..)` wrappers added in batches 6-7 and hand the scope straight through. - **rmm**: the alert path drops the `tenant_id.get()` it passed to `create_ticket` - this was the **last transitional unwrap in the codebase**. - **portal**: the three ticket feeds (get/list/create) bridge their verified contact-JWT tenant via `from_trusted`, like the KB/invoice feeds. **seed**: its `create_ticket` bridges the trusted seeder's id. - **Survivors** (6 `tenant_id.get()`, all genuine `Uuid` boundaries, none transitional): `PaymentResponse`/`TenantUsage` DTO fields, the `tenants` update `entity_id`, `AuditCtx::system` (a `Uuid` context bag), and the ticket-automation webhook payload (serialised as `Uuid`). - dev-docs cross-cutting issue #8 marked **resolved**; the stale "always takes `tenant_id: Uuid`" architecture notes corrected. ## Verification - `cargo check --all-targets`, `cargo clippy --all-targets`: clean. - Full `cargo test` suite passes **except** the two pre-existing `tests/settings.rs` billing-404 failures (`disabled_module_returns_404_on_route_access`, `enabled_module_response_unchanged`), which reproduce identically on clean `main` (verified in batch 4) and are unrelated to this change. Spot-run green: `tickets` (2), `rmm` (3), `seed_demo` (2), `audit` (4), `audit_hooks` (3), plus the full integration sweep. - `cargo test --doc`: `compile_fail` newtype guard holds. ## PMS-139 complete All request-scoped modules and all three hubs (audit, notifications, tickets) take `TenantId`. A handler that forgets to pass the authenticated scope no longer compiles. The remaining `from_trusted` sites are the deliberate ones (machine HMAC webhook, portal contact sessions, trusted seeders, cross-tenant workers, the auth login path), each noted in-code. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8)
Some checks failed
Check / fmt + clippy + compile + tests (pull_request) Failing after 11s
E2E (staging) / Playwright against staging (pull_request) Successful in 32s
Create release / Create release from merged PR (pull_request) Has been skipped
Build OCI container / Build and push mokosh-api image (push) Successful in 3m52s
17611c3592
Sweep batch 8, the finale: the last of the three cross-module hubs. The `TicketService` (service + automation) now takes the typed `TenantId`, and the tickets routes derive scope via `user.tenant()`. The transparent newtype binds and traces unchanged, so the query bodies are untouched.

Because `TicketService` is now typed, the transitional bridges added in batches 6-7 collapse: the three `audit_write` calls and the `ticket.note_added` / `ticket.automation.notify` dispatches drop their `TenantId::from_trusted(..)` wrappers and hand the scope straight through. Externally, rmm's alert path drops the `tenant_id.get()` it passed to `create_ticket` - this was the last transitional unwrap in the codebase.

This completes the PMS-139 sweep: every request-scoped module and all three hubs (audit, notifications, tickets) take `TenantId`. The six surviving `tenant_id.get()` calls are all genuine `Uuid` boundaries, not transitional - the `PaymentResponse`/`TenantUsage` DTO fields and the `tenants` update `entity_id` carry a plain `Uuid`, `AuditCtx::system` (used by rmm and the ticket service's system path) is a `Uuid` context bag, and the ticket-automation webhook payload serialises the tenant as a `Uuid`.

The remaining `from_trusted` sites are all deliberate, each noted in-code: the rmm webhook (machine HMAC), the portal feeds (KB/invoices/tickets - contact sessions, not `CurrentUser`), the demo seeder and tenants-create (trusted system actors), the cross-tenant workers, and the `auth` module + `audit_auth_event` helper (the login/session path off the raw JWT claim). dev-docs cross-cutting issue #8 is marked resolved and the stale architecture notes are corrected.

Verification: `cargo check --all-targets` and `cargo clippy --all-targets` clean; the full `cargo test` suite passes except the two pre-existing `tests/settings.rs` billing-404 failures, which reproduce identically on clean main (verified in batch 4) and are unrelated; 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-tickets 2026-06-11 14:35:04 +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!171
No description provided.