TenantAtlas/apps/platform/app/Support/ResolutionGuidance/Adapters/ReviewPackOutputResolutionAdapter.php
Ahmed Darrazi 02cc12f73f
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 59s
feat: operator resolution guidance framework v1 (spec 350)
Implemented the first version of the operator resolution guidance framework. Added new foundation classes (ResolutionCase, ResolutionAction) and a ReviewPackOutputResolutionAdapter. Updated the Customer Review Workspace and Environment Review Resource to use the new adapter. Added extensive test coverage for the framework and UI integrations.
2026-06-03 15:36:33 +02:00

204 lines
7.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Support\ResolutionGuidance\Adapters;
use App\Models\EnvironmentReview;
use App\Models\EvidenceSnapshot;
use App\Models\ReviewPack;
use App\Support\ResolutionGuidance\ResolutionAction;
use App\Support\ResolutionGuidance\ResolutionCase;
use App\Support\ReviewPacks\ReviewPackOutputResolutionGuidance;
final class ReviewPackOutputResolutionAdapter
{
/**
* @param array<string, mixed> $guidance
* @return array{
* key:string,
* scope:array<string, int|string>,
* severity:string,
* status:string,
* title:string,
* reason:string,
* impact:string,
* primary_action:array{
* key:string,
* label:string,
* type:string,
* url:?string,
* icon:string,
* kind:string,
* capability:?string,
* requires_confirmation:bool,
* audit_event:?string,
* operation_run_type:?string,
* disabled_reason:?string
* },
* secondary_actions:list<array{
* key:string,
* label:string,
* type:string,
* url:?string,
* icon:string,
* kind:string,
* capability:?string,
* requires_confirmation:bool,
* audit_event:?string,
* operation_run_type:?string,
* disabled_reason:?string
* }>,
* source_refs:list<array{type:string,id:int|string}>,
* evidence_refs:list<array{type:string,id:int|string}>,
* technical_details:array<string, string>
* }
*/
public static function fromGuidance(EnvironmentReview $review, array $guidance, string $sourceSurface): array
{
$scope = array_filter([
'type' => 'review_pack',
'workspace_id' => (int) $review->workspace_id,
'managed_environment_id' => (int) $review->managed_environment_id,
'environment_review_id' => (int) $review->getKey(),
'review_pack_id' => $review->currentExportReviewPack instanceof ReviewPack
? (int) $review->currentExportReviewPack->getKey()
: '',
'source_surface' => $sourceSurface,
], static fn (mixed $value): bool => $value !== null && $value !== '');
$primaryAction = ResolutionAction::fromArray(
is_array($guidance['primary_action'] ?? null) ? $guidance['primary_action'] : null,
self::caseKey((string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN)).'.primary_action',
__('localization.review.review_output_limitations'),
);
return ResolutionCase::make(
key: self::caseKey((string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN)),
scope: $scope,
severity: self::severity((string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN)),
status: self::status((string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN)),
title: (string) ($guidance['label'] ?? __('localization.review.requires_review')),
reason: (string) ($guidance['primary_reason'] ?? __('localization.review.review_pack_with_limitations_description')),
impact: (string) ($guidance['impact'] ?? __('localization.review.published_with_limitations_impact')),
primaryAction: $primaryAction,
secondaryActions: self::secondaryActions($guidance),
sourceRefs: self::sourceRefs($review),
evidenceRefs: self::evidenceRefs($review),
technicalDetails: self::technicalDetails($guidance),
);
}
/**
* @param array<string, mixed> $guidance
* @return list<array{
* key:string,
* label:string,
* type:string,
* url:?string,
* icon:string,
* kind:string,
* capability:?string,
* requires_confirmation:bool,
* audit_event:?string,
* operation_run_type:?string,
* disabled_reason:?string
* }>
*/
private static function secondaryActions(array $guidance): array
{
$secondaryActions = is_array($guidance['secondary_actions'] ?? null) ? $guidance['secondary_actions'] : [];
return array_values(array_map(
static fn (array $action, int $index): array => ResolutionAction::fromArray(
$action,
self::caseKey((string) ($guidance['state'] ?? ReviewPackOutputResolutionGuidance::STATE_UNKNOWN)).'.secondary_action_'.$index,
__('localization.review.review_output_limitations'),
),
array_values(array_filter($secondaryActions, static fn (mixed $action): bool => is_array($action))),
array_keys(array_values(array_filter($secondaryActions, static fn (mixed $action): bool => is_array($action)))),
));
}
/**
* @return list<array{type:string,id:int|string}>
*/
private static function sourceRefs(EnvironmentReview $review): array
{
$refs = [
[
'type' => 'environment_review',
'id' => (int) $review->getKey(),
],
];
if ($review->currentExportReviewPack instanceof ReviewPack) {
$refs[] = [
'type' => 'review_pack',
'id' => (int) $review->currentExportReviewPack->getKey(),
];
}
if ($review->operationRun !== null) {
$refs[] = [
'type' => 'operation_run',
'id' => (int) $review->operationRun->getKey(),
];
}
return $refs;
}
/**
* @return list<array{type:string,id:int|string}>
*/
private static function evidenceRefs(EnvironmentReview $review): array
{
if (! $review->evidenceSnapshot instanceof EvidenceSnapshot) {
return [];
}
return [[
'type' => 'evidence_snapshot',
'id' => (int) $review->evidenceSnapshot->getKey(),
]];
}
/**
* @param array<string, mixed> $guidance
* @return array<string, string>
*/
private static function technicalDetails(array $guidance): array
{
return array_filter(
is_array($guidance['technical_details'] ?? null) ? $guidance['technical_details'] : [],
static fn (mixed $value): bool => is_string($value) && $value !== '',
);
}
private static function caseKey(string $state): string
{
return 'review_output.'.$state;
}
private static function severity(string $state): string
{
return match ($state) {
ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY => 'success',
ReviewPackOutputResolutionGuidance::STATE_PUBLICATION_BLOCKED => 'critical',
default => 'warning',
};
}
private static function status(string $state): string
{
return match ($state) {
ReviewPackOutputResolutionGuidance::STATE_CUSTOMER_SAFE_READY => 'ready',
ReviewPackOutputResolutionGuidance::STATE_PUBLICATION_BLOCKED,
ReviewPackOutputResolutionGuidance::STATE_EXPORT_NOT_READY => 'blocked',
ReviewPackOutputResolutionGuidance::STATE_UNKNOWN => 'unknown',
default => 'action_required',
};
}
}