*/ public function forEnvironment( ManagedEnvironment $environment, string $surface = self::SURFACE_PROVIDER_CONNECTIONS_INDEX, ): array { $resolution = $this->connections->resolveDefault($environment, 'microsoft'); $connection = $resolution->connection; return $this->buildCase( environment: $environment, connection: $connection, resolution: $resolution, permissions: $this->permissionSignals($environment), latestVerificationRun: $connection instanceof ProviderConnection ? $this->latestVerificationRun($environment, $connection) : null, surface: $surface, ); } /** * @return array */ public function forConnection( ManagedEnvironment $environment, ProviderConnection $connection, string $surface = self::SURFACE_PROVIDER_CONNECTIONS_VIEW, ): array { $provider = trim((string) $connection->provider) !== '' ? trim((string) $connection->provider) : 'microsoft'; return $this->buildCase( environment: $environment, connection: $connection, resolution: $this->connections->validateConnection($environment, $provider, $connection), permissions: $this->permissionSignals($environment), latestVerificationRun: $this->latestVerificationRun($environment, $connection), surface: $surface, ); } /** * @param array{ * counts: array{missing_application:int,missing_delegated:int,present:int,error:int}, * freshness: array{last_refreshed_at:?string,is_stale:bool} * } $permissions * @return array */ private function buildCase( ManagedEnvironment $environment, ?ProviderConnection $connection, ProviderConnectionResolution $resolution, array $permissions, ?OperationRun $latestVerificationRun, string $surface, ): array { $counts = $permissions['counts']; $freshness = $permissions['freshness']; $missingApplication = (int) ($counts['missing_application'] ?? 0); $missingDelegated = (int) ($counts['missing_delegated'] ?? 0); $errorCount = (int) ($counts['error'] ?? 0); if ($resolution->effectiveReasonCode() === ProviderReasonCodes::ProviderConnectionMissing) { return $this->case( key: 'provider_readiness.connection_missing', severity: 'danger', status: __('localization.provider_guidance.status_blocked'), title: __('localization.provider_guidance.connection_missing_title'), reason: __('localization.provider_guidance.connection_missing_reason'), impact: __('localization.provider_guidance.connection_missing_impact'), primaryAction: $this->providerConnectionsAction($environment), secondaryActions: [ $this->environmentDashboardAction($environment), ], technicalDetails: [ $this->technicalDetail( __('localization.provider_guidance.detail_provider_label'), __('localization.provider_guidance.detail_provider_value'), ), $this->technicalDetail( __('localization.provider_guidance.detail_verification_state_label'), __('localization.provider_guidance.verification_not_run_detail'), ), ], ); } if ($this->isConsentBlocker($resolution->effectiveReasonCode())) { return $this->case( key: 'provider_readiness.admin_consent_required', severity: 'danger', status: __('localization.provider_guidance.status_blocked'), title: __('localization.provider_guidance.admin_consent_required_title'), reason: $this->consentReason($resolution->effectiveReasonCode()), impact: __('localization.provider_guidance.admin_consent_required_impact'), primaryAction: $this->adminConsentAction($environment), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeRequiredPermissions: true, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } if (! $resolution->resolved) { return $this->case( key: 'provider_readiness.connection_review_required', severity: 'danger', status: __('localization.provider_guidance.status_blocked'), title: $connection instanceof ProviderConnection && ! (bool) $connection->is_enabled ? __('localization.provider_guidance.connection_disabled_title') : __('localization.provider_guidance.connection_review_title'), reason: $connection instanceof ProviderConnection && ! (bool) $connection->is_enabled ? __('localization.provider_guidance.connection_disabled_reason') : (trim((string) ($resolution->message ?? '')) !== '' ? trim((string) $resolution->message) : __('localization.provider_guidance.connection_review_reason')), impact: $connection instanceof ProviderConnection && ! (bool) $connection->is_enabled ? __('localization.provider_guidance.connection_disabled_impact') : __('localization.provider_guidance.connection_review_impact'), primaryAction: $this->providerConnectionReviewAction($environment, $connection, $surface), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeRequiredPermissions: true, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } if ($missingApplication > 0) { return $this->case( key: 'provider_readiness.required_permissions_missing', severity: 'danger', status: __('localization.provider_guidance.status_blocked'), title: $surface === self::SURFACE_REQUIRED_PERMISSIONS ? __('localization.provider_guidance.required_permissions_missing_title') : __('localization.provider_guidance.provider_readiness_blocked_title'), reason: __('localization.provider_guidance.required_application_permissions_reason'), impact: __('localization.provider_guidance.required_application_permissions_impact'), primaryAction: $surface === self::SURFACE_REQUIRED_PERMISSIONS ? $this->adminConsentAction($environment) : $this->requiredPermissionsAction($environment), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeAdminConsent: $surface !== self::SURFACE_REQUIRED_PERMISSIONS, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } if ($missingDelegated > 0) { return $this->case( key: 'provider_readiness.delegated_permissions_missing', severity: 'warning', status: __('localization.provider_guidance.status_action_required'), title: $surface === self::SURFACE_REQUIRED_PERMISSIONS ? __('localization.provider_guidance.required_permissions_missing_title') : __('localization.provider_guidance.provider_readiness_attention_title'), reason: __('localization.provider_guidance.required_delegated_permissions_reason'), impact: __('localization.provider_guidance.required_delegated_permissions_impact'), primaryAction: $surface === self::SURFACE_REQUIRED_PERMISSIONS ? $this->adminConsentAction($environment) : $this->requiredPermissionsAction($environment), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeAdminConsent: $surface !== self::SURFACE_REQUIRED_PERMISSIONS, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } $verificationState = $this->verificationState($connection); if ($errorCount > 0 || in_array($verificationState, [ ProviderVerificationStatus::Error->value, ProviderVerificationStatus::Blocked->value, ProviderVerificationStatus::Degraded->value, ProviderVerificationStatus::Pending->value, ], true)) { return $this->case( key: 'provider_readiness.verification_failed', severity: $verificationState === ProviderVerificationStatus::Pending->value ? 'warning' : 'danger', status: $verificationState === ProviderVerificationStatus::Pending->value ? __('localization.provider_guidance.status_action_required') : __('localization.provider_guidance.status_blocked'), title: $verificationState === ProviderVerificationStatus::Pending->value ? __('localization.provider_guidance.verification_in_progress_title') : __('localization.provider_guidance.verification_failed_title'), reason: $this->verificationFailureReason( verificationState: $verificationState, connection: $connection, errorCount: $errorCount, ), impact: __('localization.provider_guidance.verification_failed_impact'), primaryAction: $latestVerificationRun instanceof OperationRun ? $this->verificationOperationAction($latestVerificationRun, $environment) : $this->providerConnectionReviewAction($environment, $connection, $surface), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeRequiredPermissions: true, includeRunVerification: $surface === self::SURFACE_REQUIRED_PERMISSIONS, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } $stalePermissionSnapshot = (bool) ($freshness['is_stale'] ?? true); $lastHealthCheckAt = $connection?->last_health_check_at instanceof Carbon ? $connection->last_health_check_at : ($connection?->last_health_check_at !== null ? Carbon::parse((string) $connection?->last_health_check_at) : null); $verificationNotRun = $verificationState === ProviderVerificationStatus::Unknown->value || $lastHealthCheckAt === null; $verificationStale = $lastHealthCheckAt instanceof Carbon && $lastHealthCheckAt->lt(now()->subDays(30)); if ($verificationNotRun || $verificationStale || $stalePermissionSnapshot) { return $this->case( key: 'provider_readiness.verification_required', severity: 'warning', status: __('localization.provider_guidance.status_action_required'), title: __('localization.provider_guidance.verification_required_title'), reason: $this->verificationRequiredReason( verificationNotRun: $verificationNotRun, verificationStale: $verificationStale, stalePermissionSnapshot: $stalePermissionSnapshot, ), impact: __('localization.provider_guidance.verification_required_impact'), primaryAction: $this->verificationPrimaryAction( environment: $environment, connection: $connection, surface: $surface, ), secondaryActions: $this->secondaryActionsFor( environment: $environment, connection: $connection, latestVerificationRun: $latestVerificationRun, surface: $surface, includeRequiredPermissions: true, ), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } return $this->case( key: 'provider_readiness.ready', severity: 'success', status: __('localization.provider_guidance.status_ready'), title: __('localization.provider_guidance.ready_title'), reason: __('localization.provider_guidance.ready_reason'), impact: __('localization.provider_guidance.ready_impact'), primaryAction: $this->environmentDashboardAction($environment), secondaryActions: array_values(array_filter([ $surface === self::SURFACE_REQUIRED_PERMISSIONS ? null : $this->requiredPermissionsAction($environment), $connection instanceof ProviderConnection ? $this->providerConnectionAction($environment, $connection, $surface) : null, ])), technicalDetails: $this->technicalDetails( connection: $connection, permissions: $permissions, latestVerificationRun: $latestVerificationRun, ), ); } /** * @return array{ * counts: array{missing_application:int,missing_delegated:int,present:int,error:int}, * freshness: array{last_refreshed_at:?string,is_stale:bool} * } */ private function permissionSignals(ManagedEnvironment $environment): array { $viewModel = $this->requiredPermissions->build($environment); $overview = is_array($viewModel['overview'] ?? null) ? $viewModel['overview'] : []; return [ 'counts' => array_replace([ 'missing_application' => 0, 'missing_delegated' => 0, 'present' => 0, 'error' => 0, ], is_array($overview['counts'] ?? null) ? $overview['counts'] : []), 'freshness' => array_replace([ 'last_refreshed_at' => null, 'is_stale' => true, ], is_array($overview['freshness'] ?? null) ? $overview['freshness'] : []), ]; } private function latestVerificationRun(ManagedEnvironment $environment, ProviderConnection $connection): ?OperationRun { return OperationRun::query() ->where('workspace_id', (int) $environment->workspace_id) ->where('managed_environment_id', (int) $environment->getKey()) ->where('type', 'provider.connection.check') ->where('context->provider_connection_id', (int) $connection->getKey()) ->orderByDesc('id') ->first(); } private function verificationState(?ProviderConnection $connection): string { $state = $connection?->verification_status; if ($state instanceof ProviderVerificationStatus) { return $state->value; } if (is_string($state) && trim($state) !== '') { return trim($state); } return ProviderVerificationStatus::Unknown->value; } private function isConsentBlocker(string $reasonCode): bool { return in_array($reasonCode, [ ProviderReasonCodes::ProviderConsentMissing, ProviderReasonCodes::ProviderConsentFailed, ProviderReasonCodes::ProviderConsentRevoked, ], true); } private function consentReason(string $reasonCode): string { return match ($reasonCode) { ProviderReasonCodes::ProviderConsentFailed => __('localization.provider_guidance.admin_consent_failed_reason'), ProviderReasonCodes::ProviderConsentRevoked => __('localization.provider_guidance.admin_consent_revoked_reason'), default => __('localization.provider_guidance.admin_consent_required_reason'), }; } private function verificationFailureReason( string $verificationState, ?ProviderConnection $connection, int $errorCount, ): string { if ($verificationState === ProviderVerificationStatus::Pending->value) { return __('localization.provider_guidance.verification_in_progress_reason'); } if ($verificationState === ProviderVerificationStatus::Degraded->value) { return __('localization.provider_guidance.verification_degraded_reason'); } if ($errorCount > 0) { return __('localization.provider_guidance.verification_errors_reason', ['count' => $errorCount]); } $message = trim((string) ($connection?->last_error_message ?? '')); if ($message !== '') { return Str::limit($message, 180); } return __('localization.provider_guidance.verification_failed_reason'); } private function verificationRequiredReason( bool $verificationNotRun, bool $verificationStale, bool $stalePermissionSnapshot, ): string { return match (true) { $verificationNotRun => __('localization.provider_guidance.verification_not_run_reason'), $verificationStale, $stalePermissionSnapshot => __('localization.provider_guidance.verification_stale_reason'), default => __('localization.provider_guidance.verification_not_run_reason'), }; } /** * @return array */ private function verificationPrimaryAction( ManagedEnvironment $environment, ?ProviderConnection $connection, string $surface, ): array { if ($surface === self::SURFACE_REQUIRED_PERMISSIONS) { return [ 'label' => __('localization.provider_guidance.action_run_provider_verification'), 'url' => null, 'action_name' => 'runProviderVerification', 'external' => false, 'disabled' => false, ]; } if ($surface === self::SURFACE_PROVIDER_CONNECTIONS_VIEW || $surface === self::SURFACE_PROVIDER_CONNECTIONS_EDIT) { return [ 'label' => __('localization.provider_guidance.action_run_provider_verification'), 'url' => null, 'action_name' => 'runProviderVerification', 'external' => false, 'disabled' => false, ]; } if ($connection instanceof ProviderConnection) { return $this->providerConnectionAction($environment, $connection, $surface, __('localization.provider_guidance.action_open_provider_connection')); } return $this->providerConnectionsAction($environment); } /** * @return list> */ private function secondaryActionsFor( ManagedEnvironment $environment, ?ProviderConnection $connection, ?OperationRun $latestVerificationRun, string $surface, bool $includeRequiredPermissions = false, bool $includeAdminConsent = false, bool $includeRunVerification = false, ): array { $actions = []; if ($includeRequiredPermissions && $surface !== self::SURFACE_REQUIRED_PERMISSIONS) { $actions[] = $this->requiredPermissionsAction($environment); } if ($includeAdminConsent) { $actions[] = $this->adminConsentAction($environment); } if ($latestVerificationRun instanceof OperationRun) { $actions[] = $this->verificationOperationAction($latestVerificationRun, $environment); } if ($includeRunVerification && $surface === self::SURFACE_REQUIRED_PERMISSIONS) { $actions[] = [ 'label' => __('localization.provider_guidance.action_run_provider_verification'), 'url' => null, 'action_name' => 'runProviderVerification', 'external' => false, 'disabled' => false, ]; } if ($connection instanceof ProviderConnection && ! in_array($surface, [ self::SURFACE_PROVIDER_CONNECTIONS_VIEW, self::SURFACE_PROVIDER_CONNECTIONS_EDIT, ], true)) { $actions[] = $this->providerConnectionAction($environment, $connection, $surface); } $actions[] = $this->environmentDashboardAction($environment); return array_values(array_filter($actions, static fn (array $action): bool => ($action['url'] ?? null) !== null || ($action['action_name'] ?? null) !== null)); } /** * @param array{ * counts: array{missing_application:int,missing_delegated:int,present:int,error:int}, * freshness: array{last_refreshed_at:?string,is_stale:bool} * } $permissions * @return list */ private function technicalDetails( ?ProviderConnection $connection, array $permissions, ?OperationRun $latestVerificationRun, ): array { $counts = $permissions['counts']; $freshness = $permissions['freshness']; $details = [ $this->technicalDetail( __('localization.provider_guidance.detail_missing_application_permissions_label'), (string) ((int) ($counts['missing_application'] ?? 0)), ), $this->technicalDetail( __('localization.provider_guidance.detail_missing_delegated_permissions_label'), (string) ((int) ($counts['missing_delegated'] ?? 0)), ), $this->technicalDetail( __('localization.provider_guidance.detail_verification_state_label'), $this->verificationStateLabel($connection), ), ]; if (is_string($freshness['last_refreshed_at'] ?? null) && trim((string) $freshness['last_refreshed_at']) !== '') { $details[] = $this->technicalDetail( __('localization.provider_guidance.detail_permission_evidence_label'), Carbon::parse((string) $freshness['last_refreshed_at'])->diffForHumans(), ); } else { $details[] = $this->technicalDetail( __('localization.provider_guidance.detail_permission_evidence_label'), __('localization.provider_guidance.verification_not_run_detail'), ); } if ($connection instanceof ProviderConnection) { $consent = $connection->consent_status; $details[] = $this->technicalDetail( __('localization.provider_guidance.detail_consent_state_label'), $consent instanceof ProviderConsentStatus ? Str::headline($consent->value) : Str::headline(trim((string) $consent)), ); } if ($latestVerificationRun instanceof OperationRun) { $details[] = $this->technicalDetail( __('localization.provider_guidance.detail_last_verification_operation_label'), OperationRunLinks::identifier($latestVerificationRun), ); } return $details; } private function verificationStateLabel(?ProviderConnection $connection): string { return match ($this->verificationState($connection)) { ProviderVerificationStatus::Healthy->value => __('localization.provider_guidance.verification_ready_detail'), ProviderVerificationStatus::Degraded->value => __('localization.provider_guidance.verification_degraded_detail'), ProviderVerificationStatus::Blocked->value => __('localization.provider_guidance.verification_blocked_detail'), ProviderVerificationStatus::Error->value => __('localization.provider_guidance.verification_failed_detail'), ProviderVerificationStatus::Pending->value => __('localization.provider_guidance.verification_in_progress_detail'), default => __('localization.provider_guidance.verification_not_run_detail'), }; } /** * @return array */ private function requiredPermissionsAction(ManagedEnvironment $environment): array { return [ 'label' => __('localization.provider_guidance.action_open_required_permissions'), 'url' => RequiredPermissionsLinks::requiredPermissions($environment), 'action_name' => null, 'external' => false, 'disabled' => false, ]; } /** * @return array */ private function adminConsentAction(ManagedEnvironment $environment): array { $url = RequiredPermissionsLinks::adminConsentPrimaryUrl($environment); $directConsent = RequiredPermissionsLinks::adminConsentUrl($environment) !== null; return [ 'label' => $directConsent ? __('localization.provider_guidance.action_open_admin_consent') : __('localization.provider_guidance.action_open_admin_consent_guide'), 'url' => $url, 'action_name' => null, 'external' => true, 'disabled' => false, ]; } /** * @return array */ private function providerConnectionsAction(ManagedEnvironment $environment): array { return [ 'label' => __('localization.provider_guidance.action_open_provider_connections'), 'url' => ManagedEnvironmentLinks::providerConnectionsUrl($environment), 'action_name' => null, 'external' => false, 'disabled' => false, ]; } /** * @return array */ private function providerConnectionReviewAction( ManagedEnvironment $environment, ?ProviderConnection $connection, string $surface, ): array { if ($connection instanceof ProviderConnection) { return $this->providerConnectionAction( environment: $environment, connection: $connection, surface: $surface, label: in_array($surface, [self::SURFACE_PROVIDER_CONNECTIONS_VIEW, self::SURFACE_PROVIDER_CONNECTIONS_EDIT], true) ? __('localization.provider_guidance.action_edit_provider_connection') : __('localization.provider_guidance.action_open_provider_connection'), ); } return $this->providerConnectionsAction($environment); } /** * @return array */ private function providerConnectionAction( ManagedEnvironment $environment, ProviderConnection $connection, string $surface, ?string $label = null, ): array { $page = $surface === self::SURFACE_PROVIDER_CONNECTIONS_EDIT ? 'view' : 'edit'; if ($surface === self::SURFACE_PROVIDER_CONNECTIONS_INDEX || $surface === self::SURFACE_REQUIRED_PERMISSIONS) { $page = 'view'; } return [ 'label' => $label ?? __('localization.provider_guidance.action_open_provider_connection'), 'url' => ManagedEnvironmentLinks::providerConnectionUrl($connection, $page, $environment), 'action_name' => null, 'external' => false, 'disabled' => false, ]; } /** * @return array */ private function verificationOperationAction(OperationRun $run, ManagedEnvironment $environment): array { return [ 'label' => __('localization.provider_guidance.action_open_verification_operation'), 'url' => OperationRunLinks::view($run, $environment), 'action_name' => null, 'external' => false, 'disabled' => false, ]; } /** * @return array */ private function environmentDashboardAction(ManagedEnvironment $environment): array { return [ 'label' => __('localization.provider_guidance.action_open_environment_dashboard'), 'url' => ManagedEnvironmentLinks::viewUrl($environment), 'action_name' => null, 'external' => false, 'disabled' => false, ]; } /** * @param list $technicalDetails * @param list> $secondaryActions * @return array */ private function case( string $key, string $severity, string $status, string $title, string $reason, string $impact, array $primaryAction, array $secondaryActions, array $technicalDetails, ): array { return [ 'key' => $key, 'severity' => $severity, 'status' => $status, 'title' => $title, 'reason' => $reason, 'impact' => $impact, 'primary_action' => $primaryAction, 'secondary_actions' => array_values($secondaryActions), 'technical_details' => array_values($technicalDetails), ]; } /** * @return array{label:string,value:string} */ private function technicalDetail(string $label, string $value): array { return [ 'label' => $label, 'value' => $value, ]; } }