TenantAtlas/specs/081-provider-connection-cutover/data-model.md
ahmido 4db8030f2a Spec 081: Provider connection cutover (#98)
Implements Spec 081 provider-connection cutover.

Highlights:
- Adds provider connection resolution + gating for operations/verification.
- Adds provider credential observer wiring.
- Updates Filament tenant verify flow to block with next-steps when provider connection isn’t ready.
- Adds spec docs under specs/081-provider-connection-cutover/ and extensive Spec081 test coverage.

Tests:
- vendor/bin/sail artisan test --compact tests/Feature/Filament/TenantSetupTest.php
- Focused suites for ProviderConnections/Verification ran during implementation (see local logs).

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #98
2026-02-08 11:28:51 +00:00

140 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Data Model: Provider Connection Full Cutover
**Feature**: [specs/081-provider-connection-cutover/spec.md](spec.md)
**Date**: 2026-02-07
This document describes the entities involved in Spec 081 using the repos current schema.
## Entities
### Tenant
**Represents**: A managed tenant target (Entra/Intune tenant) within a workspace.
**Relevant attributes (existing)**
- `id` (PK)
- `workspace_id` (FK)
- `name`
- `tenant_id` (GUID-ish, used as Entra tenant ID)
- `external_id` (alternate tenant identifier)
- Legacy (deprecated by this spec):
- `app_client_id`
- `app_client_secret` (encrypted)
- `app_certificate_thumbprint`
- `app_notes`
**Derived helpers (existing)**
- `graphTenantId(): ?string` returns `tenant_id` or `external_id`.
- `graphOptions(): array{tenant:?string,client_id:?string,client_secret:?string}` (to be deprecated/unused at runtime).
**Relationships (existing)**
- `providerConnections(): HasMany` → ProviderConnection
- `providerCredentials(): HasManyThrough` → ProviderCredential (via ProviderConnection)
- `operationRuns(): HasMany` (implicit via OperationRun.tenant_id)
### ProviderConnection
**Represents**: A workspace-owned integration connection for a tenant + provider (e.g., Microsoft).
**Table**: `provider_connections`
**Fields (existing)**
- `id` (PK)
- `workspace_id` (FK, NOT NULL after migration)
- `tenant_id` (FK)
- `provider` (string, e.g. `microsoft`)
- `entra_tenant_id` (string; expected to match the tenants Entra GUID)
- `display_name` (string)
- `is_default` (bool)
- `status` (string; default `needs_consent`)
- `health_status` (string; default `unknown`)
- `scopes_granted` (jsonb array)
- `last_health_check_at` (timestamp)
- `last_error_reason_code` (string, nullable)
- `last_error_message` (string, nullable; must be sanitized)
- `metadata` (jsonb)
- `created_at`, `updated_at`
**Relationships (existing)**
- `tenant(): BelongsTo`
- `workspace(): BelongsTo`
- `credential(): HasOne` → ProviderCredential
**Invariants (existing + required)**
- Uniqueness: `unique (tenant_id, provider, entra_tenant_id)`
- Exactly one default per (tenant_id, provider): enforced by partial unique index `provider_connections_default_unique`.
**Behaviors (existing)**
- `makeDefault()` clears other defaults and sets this record default in a DB transaction.
### ProviderCredential
**Represents**: Encrypted credential material for a provider connection.
**Table**: `provider_credentials`
**Fields (existing)**
- `id` (PK)
- `provider_connection_id` (FK, unique)
- `type` (string; default `client_secret`)
- `payload` (encrypted array; hidden from serialization)
- `created_at`, `updated_at`
**Payload contract (current)**
- For `type=client_secret`:
- `client_id` (string)
- `client_secret` (string)
- optional `tenant_id` (string) validated against `ProviderConnection.entra_tenant_id`
### OperationRun
**Represents**: A canonical record for a long-running or operationally relevant action.
**Table**: `operation_runs`
**Key fields (existing)**
- `id` (PK)
- `workspace_id` (FK)
- `tenant_id` (FK nullable in some cases)
- `type` (string)
- `status` (`queued`|`running`|`completed`)
- `outcome` (`pending`|`succeeded`|`partially_succeeded`|`failed` + reserved `cancelled`)
- `context` (json)
- `failure_summary` (json)
- `summary_counts` (json)
- `started_at`, `completed_at`
**Context contract (provider-backed runs)**
- `provider` (string)
- `provider_connection_id` (int)
- `target_scope.entra_tenant_id` (string)
- `module` (string; from ProviderOperationRegistry definition)
**Spec 081 extension (planned)**
- Introduce `outcome=blocked` and store `reason_code` + link-only `next_steps` in safe context/failure summary.
## State Transitions
### ProviderConnection default selection
- `is_default: false -> true` via `makeDefault()`.
- Invariant: only one default per (tenant_id, provider).
### Provider-backed operation starts
- Start surface enqueues work and creates/dedupes `OperationRun`.
- If blocked (missing default connection/credential), an `OperationRun` is still created and finalized as blocked.