feat(tenants): TenantStatus enum + status endpoint + optional mode#17
Merged
Conversation
Closes the fourth contract gap with the admin UI. The Tenants page
needs three-state lifecycle (ACTIVE / SUSPENDED / ARCHIVED) and a
single setStatus endpoint, not the boolean active flag plus two
separate /activate /deactivate routes; it also doesn't send `mode`
when provisioning a tenant.
Status enum
-----------
- New TenantStatus enum in tenant-api: ACTIVE / SUSPENDED / ARCHIVED.
Vocabulary matches the UI Tag set so wire shape and presentation
line up without translation.
Schema (V10)
------------
- Three-step online-safe rewrite of platform_tenant:
1. add nullable status VARCHAR(16),
2. back-fill from active (true -> ACTIVE, false -> SUSPENDED),
3. swap NOT NULL + indexes and drop active.
- New idx_platform_tenant_status replaces the old idx_..._active.
Entity / metadata
-----------------
- PlatformTenantEntity: active boolean -> status TenantStatus
(@Enumerated STRING). All-args constructor updated.
- TenantMetadata: active boolean -> status TenantStatus, with
null-check in the canonical constructor.
Service
-------
- TenantService gains setStatus(id, status) as the primary lifecycle
method.
- activate(id) / deactivate(id) demoted to default methods that
dispatch through setStatus(ACTIVE) / setStatus(SUSPENDED). All
prior callers keep working.
- DefaultTenantService.create now persists status = ACTIVE on new
tenants.
Controller
----------
- POST /tenants: mode is now optional (defaults to TenantMode.SINGLE
when the UI doesn't send it).
- PUT /tenants/{id}/status (new): the admin UI's single setter; body
is {"status": "ACTIVE" | "SUSPENDED" | "ARCHIVED"}.
- PUT /tenants/{id}/activate and /deactivate kept as backward-
compatible shortcuts that route through setStatus.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth PR in the admin-ui ↔ backend contract-gap series (after #14 menus, #15 audit-logs, #16 policies). The Tenants page needs a three-state lifecycle (
ACTIVE/SUSPENDED/ARCHIVED) and a single setStatus endpoint, not the booleanactiveflag plus two separate/activate//deactivateroutes; it also doesn't sendmodewhen provisioning a tenant.Summary
Status enum
TenantStatusenum intenant-api:ACTIVE/SUSPENDED/ARCHIVED. Vocabulary matches the UI Tag set so wire shape and presentation line up without translation.Schema (V10) — online-safe rewrite of
platform_tenantADD COLUMN status VARCHAR(16)(nullable).UPDATEback-fill:active = true → 'ACTIVE',active = false → 'SUSPENDED'(treat as "paused", not "soft-deleted").ALTER COLUMN status SET NOT NULL, swapidx_platform_tenant_active→idx_platform_tenant_status,DROP COLUMN active.Entity / metadata
PlatformTenantEntity:active boolean→status TenantStatus(@Enumerated(STRING)). All-args constructor updated.TenantMetadata:active boolean→status TenantStatus, with a null-check in the canonical constructor.Service
TenantServicegainssetStatus(id, status)as the primary lifecycle method.activate(id)/deactivate(id)demoted to default methods that dispatch throughsetStatus(ACTIVE)/setStatus(SUSPENDED). All prior callers keep working unchanged.DefaultTenantService.createnow persistsstatus = ACTIVEon new tenants.Controller
POST /tenants:modeis now optional (defaults toTenantMode.SINGLEwhen the UI doesn't send it).PUT /tenants/{id}/status(new): the admin UI's single setter; body is{ "status": "ACTIVE" | "SUSPENDED" | "ARCHIVED" }.PUT /tenants/{id}/activateand/deactivatekept as backward-compatible shortcuts that route throughsetStatus.Test plan
./gradlew buildgreen (55 tasks, all sample-app + tenant-core tests pass; migration runs cleanly against Testcontainers Postgres)mode, change status to SUSPENDED, archiveCloses the series
This was the last large gap. The Diagnostics + Settings field-name mismatches (cosmetic JSON-key renames) can land as a small follow-up PR or be absorbed on the admin-ui side, depending on which direction you prefer.