## Summary - migrate provider connections to the canonical three-dimension state model: lifecycle via `is_enabled`, consent via `consent_status`, and verification via `verification_status` - remove legacy provider status and health badge paths, update admin and system directory surfaces, and align onboarding, consent callback, verification, resolver, and mutation flows with the new model - add the Spec 188 artifact set, schema migrations, guard coverage, and expanded provider-state tests across admin, system, onboarding, verification, and rendering paths ## Verification - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Auth/SystemPanelAuthTest.php tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php tests/Feature/ProviderConnections/ProviderConnectionEnableDisableTest.php tests/Feature/ProviderConnections/ProviderConnectionTruthCleanupSpec179Test.php` - integrated browser smoke: validated admin provider list/detail/edit, tenant provider summary, system directory tenant detail, provider-connection search exclusion, and cleaned up the temporary smoke record afterward ## Filament / implementation notes - Livewire v4.0+ compliance: preserved; this change targets Filament v5 on Livewire v4 and does not introduce older APIs - Provider registration location: unchanged; Laravel 11+ panel providers remain registered in `bootstrap/providers.php` - Globally searchable resources: `ProviderConnectionResource` remains intentionally excluded from global search; tenant global search remains enabled and continues to resolve to view pages - Destructive actions: no new destructive action surface was introduced without confirmation or authorization; existing capability checks continue to gate provider mutations - Asset strategy: unchanged; no new Filament assets were added, so deploy behavior for `php artisan filament:assets` remains unchanged - Testing plan covered: system auth, tenant global search, provider lifecycle enable/disable behavior, and provider truth cleanup cutover behavior Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #219
431 lines
13 KiB
YAML
431 lines
13 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Canonical Provider Connection State Cleanup Internal Contract
|
|
version: 0.1.0
|
|
summary: Internal planning contract for the hard cut from legacy provider status and health to canonical lifecycle, consent, and verification
|
|
description: |
|
|
This contract is an internal planning artifact for Spec 188. The affected
|
|
routes continue to render HTML. The schemas below describe the canonical
|
|
provider-state contract that must be derivable before rendering and before
|
|
runtime gates execute. Legacy `status` and `health_status` do not appear in
|
|
any schema because the feature requires a hard cut with no compatibility
|
|
payload.
|
|
servers:
|
|
- url: /internal
|
|
x-cutover-order:
|
|
- runtime_readers
|
|
- runtime_writers
|
|
- shared_presenters_and_badges
|
|
- factories_helpers_and_tests
|
|
- schema_removal
|
|
x-runtime-readers:
|
|
- surface: resolver_and_runtime_gates
|
|
guardScope:
|
|
- app/Services/Providers/ProviderConnectionResolver.php
|
|
- app/Jobs/ScanEntraAdminRolesJob.php
|
|
- app/Filament/Resources/ProviderConnectionResource.php
|
|
expectedContract:
|
|
- lifecycle_reads_is_enabled_only
|
|
- consent_inference_reads_consent_status_only
|
|
- verification_inference_reads_verification_status_only
|
|
- surface: tenant_and_system_summaries
|
|
guardScope:
|
|
- app/Filament/Resources/TenantResource.php
|
|
- resources/views/filament/infolists/entries/provider-connection-state.blade.php
|
|
- app/Filament/System/Pages/Directory/Tenants.php
|
|
- app/Filament/System/Pages/Directory/ViewTenant.php
|
|
- resources/views/filament/system/pages/directory/view-tenant.blade.php
|
|
expectedContract:
|
|
- helper_payload_has_no_legacy_status_keys
|
|
- system_rollups_derive_from_verification_and_permission_truth
|
|
- admin_and_system_planes_tell_the_same_provider_state_story
|
|
x-runtime-writers:
|
|
- surface: onboarding_and_consent_bootstrap
|
|
writerScope:
|
|
- app/Http/Controllers/TenantOnboardingController.php
|
|
- app/Http/Controllers/AdminConsentCallbackController.php
|
|
- app/Filament/Pages/Workspaces/ManagedTenantOnboardingWizard.php
|
|
expectedContract:
|
|
- initialize_is_enabled
|
|
- initialize_or_update_consent_status
|
|
- initialize_verification_status_without_legacy_projection
|
|
- surface: verification_and_health_check
|
|
writerScope:
|
|
- app/Services/Verification/StartVerification.php
|
|
- app/Jobs/ProviderConnectionHealthCheckJob.php
|
|
- app/Services/Providers/Contracts/HealthResult.php
|
|
- app/Services/Providers/MicrosoftProviderHealthCheck.php
|
|
- app/Services/Providers/ProviderConnectionStateProjector.php
|
|
expectedContract:
|
|
- verification_contract_uses_canonical_status
|
|
- verification_writes_update_only_canonical_fields_and_diagnostics
|
|
- surface: provider_mutations
|
|
writerScope:
|
|
- app/Services/Providers/ProviderConnectionMutationService.php
|
|
- app/Filament/Resources/ProviderConnectionResource.php
|
|
expectedContract:
|
|
- enable_disable_mutations_write_lifecycle_not_legacy_status
|
|
- credential_mutations_reset_or_block_verification_without_legacy_projection
|
|
x-legacy-removal-scope:
|
|
removed_columns:
|
|
- provider_connections.status
|
|
- provider_connections.health_status
|
|
removed_badge_domains:
|
|
- provider_connection.status
|
|
- provider_connection.health
|
|
removed_helper_keys:
|
|
- status
|
|
- health_status
|
|
paths:
|
|
/admin/provider-connections:
|
|
get:
|
|
summary: Render the canonical provider-connections list with lifecycle, consent, and verification as the only state axes
|
|
operationId: viewCanonicalProviderConnections
|
|
responses:
|
|
'200':
|
|
description: Provider-connections list rendered from canonical provider-state truth only
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.provider-connections-canonical+json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProviderConnectionListTruthBundle'
|
|
'302':
|
|
description: Existing admin tenant-scoping redirects still apply
|
|
'404':
|
|
description: Workspace or tenant scope is outside entitlement
|
|
/admin/provider-connections/{record}:
|
|
get:
|
|
summary: Render canonical provider connection detail without legacy provider status or health
|
|
operationId: viewCanonicalProviderConnection
|
|
parameters:
|
|
- name: record
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type:
|
|
- integer
|
|
- string
|
|
responses:
|
|
'200':
|
|
description: Provider connection detail rendered from lifecycle, consent, verification, and diagnostics only
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.provider-connection-canonical+json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProviderConnectionDetailTruthModel'
|
|
'403':
|
|
description: Actor is in scope but lacks the capability required for protected actions
|
|
'404':
|
|
description: Provider connection is outside entitlement scope
|
|
/admin/provider-connections/{record}/edit:
|
|
get:
|
|
summary: Render canonical provider connection edit context with lifecycle, consent, and verification
|
|
operationId: editCanonicalProviderConnection
|
|
parameters:
|
|
- name: record
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type:
|
|
- integer
|
|
- string
|
|
responses:
|
|
'200':
|
|
description: Provider connection edit rendered without legacy status or health fields
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.provider-connection-edit-canonical+json:
|
|
schema:
|
|
$ref: '#/components/schemas/ProviderConnectionDetailTruthModel'
|
|
'403':
|
|
description: Actor is in scope but lacks manage capability
|
|
'404':
|
|
description: Provider connection is outside entitlement scope
|
|
/admin/tenants/{tenant}:
|
|
get:
|
|
summary: Render tenant detail provider summary from canonical provider-state truth only
|
|
operationId: viewCanonicalTenantProviderSummary
|
|
parameters:
|
|
- name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Tenant detail rendered with a canonical provider summary and no legacy provider-state payload
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.tenant-provider-summary-canonical+json:
|
|
schema:
|
|
$ref: '#/components/schemas/TenantProviderSummary'
|
|
'404':
|
|
description: Tenant is outside entitlement scope
|
|
/system/directory/tenants:
|
|
get:
|
|
summary: Render system tenant directory with health rollups derived from canonical verification and permission truth
|
|
operationId: viewSystemTenantDirectoryCanonicalHealth
|
|
responses:
|
|
'200':
|
|
description: System directory list rendered without legacy provider health aggregation
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.system-tenant-directory-canonical+json:
|
|
schema:
|
|
$ref: '#/components/schemas/SystemTenantDirectoryTruthBundle'
|
|
'404':
|
|
description: Platform actor lacks directory access or tenant scope is unavailable
|
|
/system/directory/tenants/{tenant}:
|
|
get:
|
|
summary: Render system tenant detail with canonical provider rows
|
|
operationId: viewSystemTenantCanonicalProviderRows
|
|
parameters:
|
|
- name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type:
|
|
- integer
|
|
- string
|
|
responses:
|
|
'200':
|
|
description: System tenant detail rendered with canonical provider-state rows only
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.system-tenant-canonical-provider-rows+json:
|
|
schema:
|
|
$ref: '#/components/schemas/SystemTenantDetailTruthModel'
|
|
'404':
|
|
description: Platform actor lacks directory access or tenant is unavailable
|
|
components:
|
|
schemas:
|
|
LifecycleState:
|
|
type: string
|
|
enum:
|
|
- enabled
|
|
- disabled
|
|
ProviderConsentState:
|
|
type: string
|
|
enum:
|
|
- unknown
|
|
- required
|
|
- granted
|
|
- failed
|
|
- revoked
|
|
ProviderVerificationState:
|
|
type: string
|
|
enum:
|
|
- unknown
|
|
- pending
|
|
- healthy
|
|
- degraded
|
|
- blocked
|
|
- error
|
|
ProviderDiagnostics:
|
|
type: object
|
|
required:
|
|
- migrationReviewRequired
|
|
properties:
|
|
lastCheckedAt:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
lastErrorReasonCode:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
lastErrorMessage:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
consentErrorCode:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
consentErrorMessage:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
migrationReviewRequired:
|
|
type: boolean
|
|
CanonicalProviderConnectionState:
|
|
type: object
|
|
required:
|
|
- lifecycle
|
|
- isEnabled
|
|
- consentStatus
|
|
- verificationStatus
|
|
- diagnostics
|
|
properties:
|
|
lifecycle:
|
|
$ref: '#/components/schemas/LifecycleState'
|
|
isEnabled:
|
|
type: boolean
|
|
consentStatus:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/ProviderConsentState'
|
|
- type: 'null'
|
|
verificationStatus:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/ProviderVerificationState'
|
|
- type: 'null'
|
|
diagnostics:
|
|
$ref: '#/components/schemas/ProviderDiagnostics'
|
|
ProviderConnectionRow:
|
|
type: object
|
|
required:
|
|
- id
|
|
- displayName
|
|
- provider
|
|
- connectionType
|
|
- isDefault
|
|
- state
|
|
properties:
|
|
id:
|
|
type: integer
|
|
tenantLabel:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
displayName:
|
|
type: string
|
|
provider:
|
|
type: string
|
|
connectionType:
|
|
type: string
|
|
isDefault:
|
|
type: boolean
|
|
state:
|
|
$ref: '#/components/schemas/CanonicalProviderConnectionState'
|
|
ProviderConnectionListTruthBundle:
|
|
type: object
|
|
required:
|
|
- rows
|
|
properties:
|
|
rows:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ProviderConnectionRow'
|
|
ProviderConnectionDetailTruthModel:
|
|
type: object
|
|
required:
|
|
- id
|
|
- displayName
|
|
- provider
|
|
- connectionType
|
|
- isDefault
|
|
- state
|
|
properties:
|
|
id:
|
|
type: integer
|
|
displayName:
|
|
type: string
|
|
provider:
|
|
type: string
|
|
connectionType:
|
|
type: string
|
|
isDefault:
|
|
type: boolean
|
|
effectiveAppId:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
effectiveAppSource:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
state:
|
|
$ref: '#/components/schemas/CanonicalProviderConnectionState'
|
|
TenantProviderSummary:
|
|
type: object
|
|
required:
|
|
- state
|
|
- ctaUrl
|
|
- needsDefaultConnection
|
|
properties:
|
|
state:
|
|
type: string
|
|
enum:
|
|
- missing
|
|
- configured
|
|
- default_configured
|
|
ctaUrl:
|
|
type: string
|
|
needsDefaultConnection:
|
|
type: boolean
|
|
displayName:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
provider:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
canonicalState:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/CanonicalProviderConnectionState'
|
|
- type: 'null'
|
|
SystemTenantDirectoryRow:
|
|
type: object
|
|
required:
|
|
- tenantId
|
|
- tenantLabel
|
|
- workspaceLabel
|
|
- providerConnectionsCount
|
|
- criticalProviderCount
|
|
- warningProviderCount
|
|
- missingPermissionCount
|
|
- systemHealth
|
|
properties:
|
|
tenantId:
|
|
type: integer
|
|
tenantLabel:
|
|
type: string
|
|
workspaceLabel:
|
|
type: string
|
|
providerConnectionsCount:
|
|
type: integer
|
|
criticalProviderCount:
|
|
type: integer
|
|
warningProviderCount:
|
|
type: integer
|
|
missingPermissionCount:
|
|
type: integer
|
|
systemHealth:
|
|
type: string
|
|
SystemTenantDirectoryTruthBundle:
|
|
type: object
|
|
required:
|
|
- rows
|
|
properties:
|
|
rows:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/SystemTenantDirectoryRow'
|
|
SystemTenantDetailTruthModel:
|
|
type: object
|
|
required:
|
|
- tenantId
|
|
- tenantLabel
|
|
- providerConnections
|
|
properties:
|
|
tenantId:
|
|
type: integer
|
|
tenantLabel:
|
|
type: string
|
|
providerConnections:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/ProviderConnectionRow' |