Implemented the first version of review output resolve actions. Included a ReviewOutputResolveActionMapper, commands to seed browser fixtures, updated CustomerReviewWorkspace, EnvironmentReviewResource, UI enforcement, and related views. Also added extensive unit, feature, and browser tests, and updated the design coverage matrix. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #422
214 lines
7.5 KiB
PHP
214 lines
7.5 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\ReviewOutputResolveActionMapper;
|
|
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,
|
|
* action_name:?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,
|
|
* action_name:?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 $context = [],
|
|
): 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 !== '');
|
|
|
|
$mappedActions = ReviewOutputResolveActionMapper::map(
|
|
review: $review,
|
|
guidance: $guidance,
|
|
sourceSurface: $sourceSurface,
|
|
urls: is_array($context['urls'] ?? null) ? $context['urls'] : [],
|
|
execution: is_array($context['execution'] ?? null) ? $context['execution'] : [],
|
|
);
|
|
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: $mappedActions['primary_action'],
|
|
secondaryActions: $mappedActions['secondary_actions'],
|
|
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,
|
|
* action_name:?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',
|
|
};
|
|
}
|
|
}
|