This PR introduces the Operation Run Actionability System. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #439
766 lines
32 KiB
PHP
766 lines
32 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\ResolutionGuidance\Adapters;
|
|
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\OperationRun;
|
|
use App\Models\ProviderConnection;
|
|
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
|
|
use App\Services\Providers\ProviderConnectionResolution;
|
|
use App\Services\Providers\ProviderConnectionResolver;
|
|
use App\Support\Links\RequiredPermissionsLinks;
|
|
use App\Support\ManagedEnvironmentLinks;
|
|
use App\Support\Navigation\CanonicalNavigationContext;
|
|
use App\Support\OperationRunLinks;
|
|
use App\Support\Providers\ProviderConsentStatus;
|
|
use App\Support\Providers\ProviderReasonCodes;
|
|
use App\Support\Providers\ProviderVerificationStatus;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Str;
|
|
|
|
final class ProviderReadinessResolutionAdapter
|
|
{
|
|
public const string SURFACE_PROVIDER_CONNECTIONS_INDEX = 'provider_connections.index';
|
|
|
|
public const string SURFACE_PROVIDER_CONNECTIONS_VIEW = 'provider_connections.view';
|
|
|
|
public const string SURFACE_PROVIDER_CONNECTIONS_EDIT = 'provider_connections.edit';
|
|
|
|
public const string SURFACE_REQUIRED_PERMISSIONS = 'required_permissions.page';
|
|
|
|
public function __construct(
|
|
private readonly ManagedEnvironmentRequiredPermissionsViewModelBuilder $requiredPermissions,
|
|
private readonly ProviderConnectionResolver $connections,
|
|
) {}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<array<string, mixed>>
|
|
*/
|
|
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<array{label:string,value:string}>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
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<string, mixed>
|
|
*/
|
|
private function verificationOperationAction(OperationRun $run, ManagedEnvironment $environment): array
|
|
{
|
|
return [
|
|
'label' => __('localization.provider_guidance.action_open_verification_operation'),
|
|
'url' => OperationRunLinks::view($run, $environment, CanonicalNavigationContext::fromRequest(request())),
|
|
'action_name' => null,
|
|
'external' => false,
|
|
'disabled' => false,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array<string, mixed>
|
|
*/
|
|
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<array{label:string,value:string}> $technicalDetails
|
|
* @param list<array<string, mixed>> $secondaryActions
|
|
* @return array<string, mixed>
|
|
*/
|
|
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,
|
|
];
|
|
}
|
|
}
|