135 lines
5.4 KiB
PHP
135 lines
5.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\ReviewPacks;
|
|
|
|
use App\Models\EnvironmentReview;
|
|
use App\Models\ManagedEnvironment;
|
|
use App\Models\ReviewPack;
|
|
use App\Models\User;
|
|
use App\Support\Auth\Capabilities;
|
|
use App\Support\ReviewPackStatus;
|
|
|
|
final class CustomerOutputGate
|
|
{
|
|
public const string INTERNAL_PREVIEW_QUERY_KEY = 'internal_preview';
|
|
|
|
public const string STATE_READY = 'ready';
|
|
|
|
public const string STATE_NEEDS_ATTENTION = 'needs_attention';
|
|
|
|
public const string STATE_BLOCKED = 'blocked';
|
|
|
|
public const string STATE_INTERNAL_ONLY = 'internal_only';
|
|
|
|
public const string STATE_NOT_AVAILABLE = 'not_available';
|
|
|
|
public const string STATE_EXPIRED = 'expired';
|
|
|
|
public const string STATE_UNKNOWN = 'unknown';
|
|
|
|
public function decisionForReviewPack(ReviewPack $reviewPack, ?User $actor = null): CustomerOutputGateDecision
|
|
{
|
|
$reviewPack->loadMissing([
|
|
'tenant',
|
|
'environmentReview.evidenceSnapshot',
|
|
'environmentReview.currentExportReviewPack',
|
|
'environmentReview.sections',
|
|
]);
|
|
|
|
$tenant = $reviewPack->tenant;
|
|
$review = $reviewPack->environmentReview;
|
|
$hasReadyArtifact = $this->hasReadyArtifact($reviewPack);
|
|
$canViewReviewPack = $tenant instanceof ManagedEnvironment
|
|
&& $actor instanceof User
|
|
&& $actor->can(Capabilities::REVIEW_PACK_VIEW, $tenant);
|
|
$canManageReviewPack = $tenant instanceof ManagedEnvironment
|
|
&& $actor instanceof User
|
|
&& $actor->can(Capabilities::REVIEW_PACK_MANAGE, $tenant);
|
|
|
|
if ($this->isExpired($reviewPack)) {
|
|
return new CustomerOutputGateDecision(
|
|
state: self::STATE_EXPIRED,
|
|
guidanceState: ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY,
|
|
hasReadyArtifact: false,
|
|
canStreamCustomerOutput: false,
|
|
canStreamInternalPreview: false,
|
|
reason: __('localization.review.expired'),
|
|
);
|
|
}
|
|
|
|
if (! $hasReadyArtifact) {
|
|
return new CustomerOutputGateDecision(
|
|
state: self::STATE_NOT_AVAILABLE,
|
|
guidanceState: ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY,
|
|
hasReadyArtifact: false,
|
|
canStreamCustomerOutput: false,
|
|
canStreamInternalPreview: false,
|
|
reason: __('localization.review.export_not_ready'),
|
|
);
|
|
}
|
|
|
|
if (! $review instanceof EnvironmentReview || ! $this->isCurrentReviewExport($reviewPack, $review)) {
|
|
return new CustomerOutputGateDecision(
|
|
state: self::STATE_UNKNOWN,
|
|
guidanceState: ReviewPackOutputResolutionGuidance::STATE_UNKNOWN,
|
|
hasReadyArtifact: true,
|
|
canStreamCustomerOutput: false,
|
|
canStreamInternalPreview: $canManageReviewPack,
|
|
reason: __('localization.review.requires_review'),
|
|
);
|
|
}
|
|
|
|
$readiness = ReviewPackOutputResolutionGuidance::readinessForReview($review);
|
|
$guidance = ReviewPackOutputResolutionGuidance::fromReadiness($readiness);
|
|
$guidanceState = (string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN);
|
|
$canStreamCustomerOutput = $guidanceState === ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY
|
|
&& $canViewReviewPack;
|
|
|
|
return new CustomerOutputGateDecision(
|
|
state: $this->stateForGuidance($guidanceState),
|
|
guidanceState: $guidanceState,
|
|
hasReadyArtifact: true,
|
|
canStreamCustomerOutput: $canStreamCustomerOutput,
|
|
canStreamInternalPreview: $canManageReviewPack,
|
|
reason: (string) ($guidance['primary_reason'] ?? __('localization.review.requires_review')),
|
|
readiness: $readiness,
|
|
guidance: $guidance,
|
|
);
|
|
}
|
|
|
|
private function hasReadyArtifact(ReviewPack $reviewPack): bool
|
|
{
|
|
if ($reviewPack->status !== ReviewPackStatus::Ready->value) {
|
|
return false;
|
|
}
|
|
|
|
return filled($reviewPack->file_path) && filled($reviewPack->file_disk);
|
|
}
|
|
|
|
private function isExpired(ReviewPack $reviewPack): bool
|
|
{
|
|
return $reviewPack->status === ReviewPackStatus::Expired->value
|
|
|| ($reviewPack->expires_at !== null && $reviewPack->expires_at->isPast());
|
|
}
|
|
|
|
private function isCurrentReviewExport(ReviewPack $reviewPack, EnvironmentReview $review): bool
|
|
{
|
|
return (int) $review->managed_environment_id === (int) $reviewPack->managed_environment_id
|
|
&& (int) ($review->current_export_review_pack_id ?? 0) === (int) $reviewPack->getKey();
|
|
}
|
|
|
|
private function stateForGuidance(string $guidanceState): string
|
|
{
|
|
return match ($guidanceState) {
|
|
ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY => self::STATE_READY,
|
|
ReviewPackOutputResolutionGuidance::STATE_PUBLICATION_BLOCKED => self::STATE_BLOCKED,
|
|
ReviewPackOutputResolutionGuidance::STATE_INTERNAL_ONLY => self::STATE_INTERNAL_ONLY,
|
|
ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY => self::STATE_NOT_AVAILABLE,
|
|
ReviewPackOutputResolutionGuidance::STATE_PUBLISHED_WITH_LIMITATIONS => self::STATE_NEEDS_ATTENTION,
|
|
default => self::STATE_UNKNOWN,
|
|
};
|
|
}
|
|
}
|