272 lines
12 KiB
Markdown
272 lines
12 KiB
Markdown
# Phase 1 Data Model: Canonical Provider Connection State Cleanup
|
|
|
|
## Overview
|
|
|
|
This feature removes the persisted legacy provider-state columns `status` and `health_status` and replaces their last remaining behavioral responsibility with one explicit lifecycle truth: `is_enabled`. Consent and verification remain on their existing canonical enums. No new table, no new persisted summary artifact, and no new cross-domain provider-state framework are introduced.
|
|
|
|
## Persistent Source Truths
|
|
|
|
### ProviderConnection
|
|
|
|
**Purpose**: Canonical tenant-owned provider integration record for provider lifecycle, consent, verification, and supporting diagnostics.
|
|
|
|
**Key fields**:
|
|
- `id`
|
|
- `tenant_id`
|
|
- `workspace_id`
|
|
- `provider`
|
|
- `entra_tenant_id`
|
|
- `display_name`
|
|
- `is_default`
|
|
- `connection_type`
|
|
- `is_enabled`
|
|
- `consent_status`
|
|
- `consent_granted_at`
|
|
- `consent_last_checked_at`
|
|
- `consent_error_code`
|
|
- `consent_error_message`
|
|
- `verification_status`
|
|
- `migration_review_required`
|
|
- `migration_reviewed_at`
|
|
- `scopes_granted`
|
|
- `last_health_check_at`
|
|
- `last_error_reason_code`
|
|
- `last_error_message`
|
|
- `metadata`
|
|
|
|
**Removed fields**:
|
|
- `status`
|
|
- `health_status`
|
|
|
|
**Relationships**:
|
|
- `ProviderConnection` belongs to `Tenant`
|
|
- `ProviderConnection` belongs to `Workspace`
|
|
- `ProviderConnection` has one `ProviderCredential`
|
|
|
|
**Validation rules**:
|
|
- `is_enabled` is the only persisted lifecycle truth and defaults to `true` for newly created connections.
|
|
- `consent_status` remains the only consent truth.
|
|
- `verification_status` remains the only verification truth.
|
|
- `status` and `health_status` are not recreated through accessors, casts, appended attributes, helper arrays, or audit context shims.
|
|
- Existing unique constraints on tenant, provider, and Entra tenant identity remain unchanged.
|
|
- Existing indexes for `consent_status` and `verification_status` remain. Legacy indexes tied to removed columns are dropped. No new lifecycle index is introduced unless implementation profiling proves it necessary.
|
|
|
|
### ProviderCredential
|
|
|
|
**Purpose**: Encrypted credential material associated with a provider connection.
|
|
|
|
**Key fields**:
|
|
- `provider_connection_id`
|
|
- encrypted credential payload fields
|
|
|
|
**Relationships**:
|
|
- `ProviderCredential` belongs to `ProviderConnection`
|
|
|
|
**Validation rules**:
|
|
- This feature does not change credential storage or encryption.
|
|
- Credential presence remains a diagnostic or blocker input for lifecycle-safe verification semantics, not a fourth provider-state dimension.
|
|
|
|
### HealthResult (internal contract)
|
|
|
|
**Purpose**: Canonical result object emitted by provider health checks before persistence.
|
|
|
|
**Key fields**:
|
|
- `verification_status`
|
|
- `reason_code`
|
|
- `message`
|
|
- `meta`
|
|
|
|
**Validation rules**:
|
|
- The contract must not carry legacy `status` or `health_status` fields.
|
|
- The contract may carry only the minimum data needed to derive canonical consent and verification outcomes plus diagnostics.
|
|
|
|
## Canonical State Families
|
|
|
|
### Lifecycle (derived from `is_enabled`)
|
|
|
|
**Persisted form**:
|
|
- `is_enabled = true`
|
|
- `is_enabled = false`
|
|
|
|
**Operator-facing form**:
|
|
- `Enabled`
|
|
- `Disabled`
|
|
|
|
**Rule**:
|
|
- Lifecycle answers only whether the connection is administratively allowed to operate.
|
|
- Lifecycle does not imply consent and does not imply the latest verification outcome.
|
|
|
|
### ProviderConsentStatus
|
|
|
|
**Values**:
|
|
- `unknown`
|
|
- `required`
|
|
- `granted`
|
|
- `failed`
|
|
- `revoked`
|
|
|
|
**Rule**:
|
|
- Consent answers only whether required provider consent currently exists and whether consent-specific failure or revocation was detected.
|
|
- Consent does not imply lifecycle and does not imply technical verification success.
|
|
|
|
### ProviderVerificationStatus
|
|
|
|
**Values**:
|
|
- `unknown`
|
|
- `pending`
|
|
- `healthy`
|
|
- `degraded`
|
|
- `blocked`
|
|
- `error`
|
|
|
|
**Rule**:
|
|
- Verification answers only what the latest verification attempt or local canonical blocker currently proves.
|
|
- Verification does not imply lifecycle and does not rewrite consent.
|
|
|
|
## State Transitions
|
|
|
|
### Lifecycle transitions
|
|
|
|
| From | To | Trigger | Notes |
|
|
|------|----|---------|-------|
|
|
| `true` | `false` | Explicit disable action | Consent and verification remain intact as separate truths. |
|
|
| `false` | `true` | Explicit enable action | Lifecycle resumes without fabricating consent or a healthy verification state. |
|
|
|
|
### Consent transitions
|
|
|
|
| From | To | Trigger | Notes |
|
|
|------|----|---------|-------|
|
|
| `unknown` or `required` | `granted` | Successful admin consent callback or successful verification that proves consent still exists | Does not change lifecycle. |
|
|
| `granted` | `revoked` | Verification detects consent was revoked | Verification may become `blocked`, but lifecycle remains separate. |
|
|
| any | `failed` | Consent-specific failure during callback or verification | Does not disable the connection. |
|
|
|
|
### Verification transitions
|
|
|
|
| From | To | Trigger | Notes |
|
|
|------|----|---------|-------|
|
|
| `unknown` | `pending` | Verification start | Existing start surfaces remain authoritative. |
|
|
| `pending` | `healthy`, `degraded`, `blocked`, or `error` | Verification completion | Derived from the health-check result and current consent truth. |
|
|
| any | `unknown` | New connection creation, successful enable with present credentials, or credential mutation that invalidates prior verification but does not prove a blocker | Keeps stale success from surviving a material configuration change. |
|
|
| any | `blocked` | Local or remote evidence proves a blocker, such as missing credentials, invalid connection type, consent missing, or review-required state | Blocked remains a verification consequence, not a lifecycle substitute. |
|
|
|
|
## Mutation Rules
|
|
|
|
### Connection creation and onboarding bootstrap
|
|
|
|
**Expected persisted shape**:
|
|
- `is_enabled = true`
|
|
- `consent_status = required` for pre-consent onboarding starts
|
|
- `verification_status = unknown`
|
|
- diagnostics cleared or initialized from the current event
|
|
|
|
### Consent callback
|
|
|
|
**Expected persisted shape**:
|
|
- lifecycle unchanged or initialized to `is_enabled = true`
|
|
- `consent_status` updated from callback outcome
|
|
- `verification_status = unknown`
|
|
- consent and error timestamps refreshed
|
|
|
|
### Verification start
|
|
|
|
**Expected persisted shape**:
|
|
- lifecycle unchanged
|
|
- consent unchanged
|
|
- `verification_status = pending`
|
|
- legacy status or health projections are not written
|
|
|
|
### Verification completion / health check
|
|
|
|
**Expected persisted shape**:
|
|
- lifecycle unchanged
|
|
- `consent_status` updated only when the result proves a consent consequence
|
|
- `verification_status` updated from the canonical health result
|
|
- error and recency diagnostics refreshed
|
|
|
|
### Enable and disable actions
|
|
|
|
**Expected persisted shape**:
|
|
- Disable writes only `is_enabled = false` plus any audit metadata that records the lifecycle change.
|
|
- Enable writes `is_enabled = true` and resets stale verification truth without fabricating a healthy state.
|
|
- When enabling reveals a local blocker such as missing credentials, `verification_status` becomes `blocked` and the blocker reason is recorded in diagnostics.
|
|
|
|
### Credential-source and mutation-service changes
|
|
|
|
**Expected persisted shape**:
|
|
- lifecycle unchanged unless the operator explicitly disables the connection
|
|
- consent unchanged unless the mutation directly changes consent evidence
|
|
- verification reset to `unknown` or `blocked` depending on whether the mutation invalidates prior verification or proves a blocker
|
|
|
|
## Derived Surface Contracts
|
|
|
|
### Canonical provider state bundle
|
|
|
|
**Purpose**: Shared derived state used by provider detail, edit, tenant summaries, and system read-only views.
|
|
|
|
| Field | Type | Required | Description |
|
|
|------|------|----------|-------------|
|
|
| `lifecycle` | string | yes | `enabled` or `disabled`, derived from `is_enabled` |
|
|
| `isEnabled` | boolean | yes | Raw persisted lifecycle truth |
|
|
| `consentStatus` | string nullable | yes | Canonical consent status |
|
|
| `verificationStatus` | string nullable | yes | Canonical verification status |
|
|
| `connectionType` | string | yes | Platform or dedicated |
|
|
| `isDefault` | boolean | yes | Default designation |
|
|
| `lastCheckedAt` | string nullable | no | Stored verification recency |
|
|
| `lastErrorReasonCode` | string nullable | no | Latest diagnostic reason |
|
|
| `lastErrorMessage` | string nullable | no | Latest diagnostic message |
|
|
| `migrationReviewRequired` | boolean | yes | Existing migration-review diagnostic |
|
|
|
|
**Validation rules**:
|
|
- No helper array or view model may expose `status` or `health_status` keys.
|
|
- Diagnostics remain subordinate to lifecycle, consent, and verification.
|
|
|
|
### Tenant provider summary
|
|
|
|
**Purpose**: Compact provider summary rendered from `TenantResource::providerConnectionState()` and the shared provider-state Blade entry.
|
|
|
|
| Field | Type | Required | Description |
|
|
|------|------|----------|-------------|
|
|
| `state` | string | yes | `missing`, `configured`, or `default_configured` |
|
|
| `cta_url` | string | yes | Canonical provider-connections destination |
|
|
| `needs_default_connection` | boolean | yes | Signals action needed without inventing provider health |
|
|
| `display_name` | string nullable | no | Chosen connection display name |
|
|
| `provider` | string nullable | no | Provider label |
|
|
| `is_enabled` | boolean nullable | no | Raw lifecycle truth when a connection exists |
|
|
| `consent_status` | string nullable | no | Canonical consent status |
|
|
| `verification_status` | string nullable | no | Canonical verification status |
|
|
| `last_health_check_at` | string nullable | no | Stored verification recency |
|
|
| `last_error_reason_code` | string nullable | no | Latest diagnostic reason |
|
|
|
|
**Validation rules**:
|
|
- Missing or non-default states must never read as healthy or ready.
|
|
- The summary must not return removed legacy keys for compatibility.
|
|
|
|
### System tenant health rollup
|
|
|
|
**Purpose**: Read-only system-directory aggregate used to derive `SystemHealth` badges.
|
|
|
|
| Field | Type | Required | Description |
|
|
|------|------|----------|-------------|
|
|
| `tenantStatus` | string | yes | Existing tenant lifecycle/status input |
|
|
| `criticalProviderCount` | integer | yes | Count of each tenant's default Microsoft provider connection when its `verification_status` is `blocked` or `error` |
|
|
| `warningProviderCount` | integer | yes | Count of each tenant's default Microsoft provider connection when its `verification_status = degraded` |
|
|
| `missingPermissionCount` | integer | yes | Existing tenant-permission signal |
|
|
|
|
**Validation rules**:
|
|
- System health rollups must not query removed `health_status` values.
|
|
- Tenant-directory list rollups count only the default Microsoft provider connection for each tenant. Non-default connections may still appear on the system tenant detail page, but they do not affect list rollup counts.
|
|
- The same provider connection must produce semantically aligned admin and system readings.
|
|
|
|
## Removal Rules
|
|
|
|
1. `status` and `health_status` are removed from the database schema, Eloquent model interactions, helper arrays, Blade views, badge domains, query filters, and audit metadata.
|
|
2. `ProviderConnection::classificationProjection()` and any equivalent projection helpers no longer emit removed fields.
|
|
3. Runtime gates read lifecycle from `is_enabled`, not from any verification or consent surrogate.
|
|
4. Diagnostics remain stored and displayed only as supporting facts, not as another state language.
|
|
|
|
## No New Persistence Beyond The Narrow Lifecycle Field
|
|
|
|
- No new table is introduced.
|
|
- No new provider-readiness summary is persisted.
|
|
- No new enum class is introduced for lifecycle; the persisted truth is a boolean and the operator-facing labels are derived.
|
|
- The cleanup removes more persisted truth than it adds: two legacy columns out, one lifecycle column in. |