project( connectionType: $connection->connection_type, consentStatus: $connection->consent_status, verificationStatus: $connection->verification_status, currentStatus: is_string($connection->status) ? $connection->status : null, ); } /** * @return array{status: string, health_status: string} */ public function project( ProviderConnectionType|string|null $connectionType, ProviderConsentStatus|string|null $consentStatus, ProviderVerificationStatus|string|null $verificationStatus, ?string $currentStatus = null, ): array { $resolvedConnectionType = $this->normalizeConnectionType($connectionType) ?? ProviderConnectionType::Platform; $resolvedConsentStatus = $this->normalizeConsentStatus($consentStatus) ?? ProviderConsentStatus::Unknown; $resolvedVerificationStatus = $this->normalizeVerificationStatus($verificationStatus) ?? ProviderVerificationStatus::Unknown; $status = $currentStatus === 'disabled' ? 'disabled' : $this->projectStatus($resolvedConnectionType, $resolvedConsentStatus, $resolvedVerificationStatus); return [ 'status' => $status, 'health_status' => $this->projectHealthStatus($resolvedVerificationStatus), ]; } /** * @return array{ * consent_status: ProviderConsentStatus, * verification_status: ProviderVerificationStatus, * status: string, * health_status: string, * last_error_reason_code: ?string, * last_error_message: ?string, * consent_error_code: ?string, * consent_error_message: ?string, * consent_revoked_detected: bool * } */ public function projectVerificationOutcome(ProviderConnection $connection, HealthResult $result): array { $currentConsentStatus = $this->normalizeConsentStatus($connection->consent_status) ?? (((string) $connection->status === 'needs_consent') ? ProviderConsentStatus::Required : ProviderConsentStatus::Unknown); $effectiveReasonCode = $result->healthy ? null : $this->effectiveReasonCodeForVerification($currentConsentStatus, $result->reasonCode); $consentStatus = $this->consentStatusAfterVerification($currentConsentStatus, $effectiveReasonCode, $result->healthy); $verificationStatus = $this->verificationStatusAfterVerification($effectiveReasonCode, $result->healthy, $result->healthStatus); $projected = $this->project( connectionType: $connection->connection_type, consentStatus: $consentStatus, verificationStatus: $verificationStatus, currentStatus: is_string($connection->status) ? $connection->status : null, ); $consentErrorCode = in_array($consentStatus, [ ProviderConsentStatus::Required, ProviderConsentStatus::Failed, ProviderConsentStatus::Revoked, ], true) ? $effectiveReasonCode : null; return [ 'consent_status' => $consentStatus, 'verification_status' => $verificationStatus, 'status' => $projected['status'], 'health_status' => $projected['health_status'], 'last_error_reason_code' => $effectiveReasonCode, 'last_error_message' => $result->healthy ? null : $result->message, 'consent_error_code' => $consentErrorCode, 'consent_error_message' => $consentErrorCode === null || $result->healthy ? null : $result->message, 'consent_revoked_detected' => $currentConsentStatus === ProviderConsentStatus::Granted && $effectiveReasonCode === ProviderReasonCodes::ProviderConsentRevoked, ]; } private function normalizeConnectionType(ProviderConnectionType|string|null $connectionType): ?ProviderConnectionType { if ($connectionType instanceof ProviderConnectionType) { return $connectionType; } if (! is_string($connectionType)) { return null; } return ProviderConnectionType::tryFrom(trim($connectionType)); } private function normalizeConsentStatus(ProviderConsentStatus|string|null $consentStatus): ?ProviderConsentStatus { if ($consentStatus instanceof ProviderConsentStatus) { return $consentStatus; } if (! is_string($consentStatus)) { return null; } return ProviderConsentStatus::tryFrom(trim($consentStatus)); } private function normalizeVerificationStatus( ProviderVerificationStatus|string|null $verificationStatus, ): ?ProviderVerificationStatus { if ($verificationStatus instanceof ProviderVerificationStatus) { return $verificationStatus; } if (! is_string($verificationStatus)) { return null; } return ProviderVerificationStatus::tryFrom(trim($verificationStatus)); } private function projectStatus( ProviderConnectionType $connectionType, ProviderConsentStatus $consentStatus, ProviderVerificationStatus $verificationStatus, ): string { if ($connectionType === ProviderConnectionType::Dedicated && $verificationStatus === ProviderVerificationStatus::Blocked) { return 'error'; } if ($consentStatus === ProviderConsentStatus::Failed) { return 'error'; } if ($consentStatus !== ProviderConsentStatus::Granted) { return 'needs_consent'; } return match ($verificationStatus) { ProviderVerificationStatus::Blocked, ProviderVerificationStatus::Error => 'error', default => 'connected', }; } private function projectHealthStatus(ProviderVerificationStatus $verificationStatus): string { return match ($verificationStatus) { ProviderVerificationStatus::Healthy => 'ok', ProviderVerificationStatus::Degraded => 'degraded', ProviderVerificationStatus::Blocked, ProviderVerificationStatus::Error => 'down', default => 'unknown', }; } private function effectiveReasonCodeForVerification( ProviderConsentStatus $currentConsentStatus, ?string $reasonCode, ): ?string { if (! is_string($reasonCode) || $reasonCode === '') { return null; } if ( $currentConsentStatus === ProviderConsentStatus::Granted && $reasonCode === ProviderReasonCodes::ProviderConsentMissing ) { return ProviderReasonCodes::ProviderConsentRevoked; } return $reasonCode; } private function consentStatusAfterVerification( ProviderConsentStatus $currentConsentStatus, ?string $reasonCode, bool $healthy, ): ProviderConsentStatus { if ($healthy) { return ProviderConsentStatus::Granted; } return match ($reasonCode) { ProviderReasonCodes::ProviderConsentMissing => ProviderConsentStatus::Required, ProviderReasonCodes::ProviderConsentFailed => ProviderConsentStatus::Failed, ProviderReasonCodes::ProviderConsentRevoked => ProviderConsentStatus::Revoked, default => $currentConsentStatus, }; } private function verificationStatusAfterVerification( ?string $reasonCode, bool $healthy, string $healthStatus, ): ProviderVerificationStatus { if ($healthy) { return ProviderVerificationStatus::Healthy; } if ($healthStatus === 'degraded' || $reasonCode === ProviderReasonCodes::RateLimited) { return ProviderVerificationStatus::Degraded; } if (in_array($reasonCode, [ ProviderReasonCodes::ProviderConsentMissing, ProviderReasonCodes::ProviderConsentFailed, ProviderReasonCodes::ProviderConsentRevoked, ProviderReasonCodes::PlatformIdentityMissing, ProviderReasonCodes::PlatformIdentityIncomplete, ProviderReasonCodes::DedicatedCredentialMissing, ProviderReasonCodes::DedicatedCredentialInvalid, ProviderReasonCodes::ProviderConnectionInvalid, ProviderReasonCodes::ProviderConnectionReviewRequired, ProviderReasonCodes::ProviderConnectionTypeInvalid, ProviderReasonCodes::TenantTargetMismatch, ], true)) { return ProviderVerificationStatus::Blocked; } return ProviderVerificationStatus::Error; } }