refactor: consolidate internal tenant model naming #355
@ -40,8 +40,9 @@ public static function getUrl(?string $name = null, array $parameters = [], bool
|
||||
return url('/admin');
|
||||
}
|
||||
|
||||
$parameters['tenant'] ??= $resolvedTenant;
|
||||
$parameters['environment'] ??= $resolvedTenant;
|
||||
$parameters['workspace'] ??= $workspace;
|
||||
unset($parameters['tenant']);
|
||||
|
||||
return parent::getUrl($name, $parameters, $isAbsolute, $panelId, null, $shouldGuessMissingParameters);
|
||||
}
|
||||
@ -52,7 +53,7 @@ protected static function workspaceScopedSlug(string $slug, ?Panel $panel = null
|
||||
return $slug;
|
||||
}
|
||||
|
||||
$prefix = 'workspaces/{workspace}/environments/{tenant}/';
|
||||
$prefix = 'workspaces/{workspace}/environments/{environment}/';
|
||||
|
||||
return str_starts_with($slug, $prefix)
|
||||
? $slug
|
||||
@ -148,4 +149,4 @@ protected static function workspaceScopedTenantRelationshipName(): string
|
||||
? $relationshipName
|
||||
: 'tenant';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ChooseTenant extends Page
|
||||
class ChooseEnvironment extends Page
|
||||
{
|
||||
protected static string $layout = 'filament-panels::components.layout.simple';
|
||||
|
||||
@ -26,9 +26,9 @@ class ChooseTenant extends Page
|
||||
|
||||
protected static bool $isDiscovered = false;
|
||||
|
||||
protected static ?string $slug = 'choose-tenant';
|
||||
protected static ?string $slug = 'choose-environment';
|
||||
|
||||
protected string $view = 'filament.pages.choose-tenant';
|
||||
protected string $view = 'filament.pages.choose-environment';
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
@ -66,7 +66,7 @@ public function getTenants(): Collection
|
||||
return app(TenantOperabilityService::class)->filterSelectable(collect($tenants));
|
||||
}
|
||||
|
||||
public function selectTenant(int $tenantId): void
|
||||
public function selectEnvironment(int $tenantId): void
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||
use App\Services\PortfolioCompare\CrossTenantPromotionExecutionService;
|
||||
use App\Services\PortfolioCompare\CrossEnvironmentPromotionExecutionService;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\ManagedEnvironmentLinks;
|
||||
use App\Support\Navigation\CanonicalNavigationContext;
|
||||
@ -19,9 +19,9 @@
|
||||
use App\Support\OperationRunLinks;
|
||||
use App\Support\OpsUx\OpsUxBrowserEvents;
|
||||
use App\Support\OpsUx\ProviderOperationStartResultPresenter;
|
||||
use App\Support\PortfolioCompare\CrossTenantComparePreviewBuilder;
|
||||
use App\Support\PortfolioCompare\CrossTenantCompareSelection;
|
||||
use App\Support\PortfolioCompare\CrossTenantPromotionPreflight;
|
||||
use App\Support\PortfolioCompare\CrossEnvironmentComparePreviewBuilder;
|
||||
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
|
||||
use App\Support\PortfolioCompare\CrossEnvironmentPromotionPreflight;
|
||||
use App\Support\Rbac\WorkspaceUiEnforcement;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
||||
@ -42,13 +42,13 @@
|
||||
use Illuminate\Support\Str;
|
||||
use UnitEnum;
|
||||
|
||||
class CrossTenantComparePage extends Page implements HasForms
|
||||
class CrossEnvironmentComparePage extends Page implements HasForms
|
||||
{
|
||||
use InteractsWithForms;
|
||||
|
||||
private const string SOURCE_TENANT_QUERY_KEY = 'source_tenant_id';
|
||||
private const string SOURCE_ENVIRONMENT_QUERY_KEY = 'source_environment_id';
|
||||
|
||||
private const string TARGET_TENANT_QUERY_KEY = 'target_tenant_id';
|
||||
private const string TARGET_ENVIRONMENT_QUERY_KEY = 'target_environment_id';
|
||||
|
||||
private const string POLICY_TYPE_QUERY_KEY = 'policy_type';
|
||||
|
||||
@ -60,15 +60,15 @@ class CrossTenantComparePage extends Page implements HasForms
|
||||
|
||||
protected static string|UnitEnum|null $navigationGroup = 'Governance';
|
||||
|
||||
protected static ?string $title = 'Cross-ManagedEnvironment Compare';
|
||||
protected static ?string $title = 'Cross-environment compare';
|
||||
|
||||
protected static ?string $slug = 'cross-tenant-compare';
|
||||
protected static ?string $slug = 'cross-environment-compare';
|
||||
|
||||
protected string $view = 'filament.pages.cross-tenant-compare';
|
||||
protected string $view = 'filament.pages.cross-environment-compare';
|
||||
|
||||
public ?string $sourceTenantId = null;
|
||||
public ?string $sourceEnvironmentId = null;
|
||||
|
||||
public ?string $targetTenantId = null;
|
||||
public ?string $targetEnvironmentId = null;
|
||||
|
||||
/**
|
||||
* @var list<string>
|
||||
@ -95,12 +95,12 @@ class CrossTenantComparePage extends Page implements HasForms
|
||||
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
|
||||
{
|
||||
return ActionSurfaceDeclaration::forPage(ActionSurfaceProfile::ListOnlyReadOnly)
|
||||
->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions preserve return navigation, tenant drill-downs, and one dominant promotion-preflight action.')
|
||||
->satisfy(ActionSurfaceSlot::ListHeader, 'Header actions preserve return navigation, environment drill-downs, and one dominant promotion-preflight action.')
|
||||
->exempt(ActionSurfaceSlot::InspectAffordance, 'The compare page uses explicit selection controls instead of row-click inspection.')
|
||||
->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Cross-tenant compare renders focused subject summaries instead of row-level overflow actions.')
|
||||
->exempt(ActionSurfaceSlot::ListRowMoreMenu, 'Cross-environment compare renders focused subject summaries instead of row-level overflow actions.')
|
||||
->exempt(ActionSurfaceSlot::ListBulkMoreGroup, 'The compare page has no bulk actions.')
|
||||
->satisfy(ActionSurfaceSlot::ListEmptyState, 'The compare page explains when a selection is incomplete or invalid before any preview exists.')
|
||||
->exempt(ActionSurfaceSlot::DetailHeader, 'Cross-tenant compare is a workspace decision page, not a record detail header.');
|
||||
->exempt(ActionSurfaceSlot::DetailHeader, 'Cross-environment compare is a workspace decision page, not a record detail header.');
|
||||
}
|
||||
|
||||
public function mount(): void
|
||||
@ -124,24 +124,24 @@ public function form(Schema $schema): Schema
|
||||
'xl' => 3,
|
||||
])
|
||||
->schema([
|
||||
Select::make('sourceTenantId')
|
||||
->label('Source tenant')
|
||||
->options(fn (): array => $this->tenantOptions())
|
||||
Select::make('sourceEnvironmentId')
|
||||
->label('Source environment')
|
||||
->options(fn (): array => $this->environmentOptions())
|
||||
->searchable()
|
||||
->preload()
|
||||
->native(false)
|
||||
->placeholder('Select a source tenant')
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-source'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-tenant-source']),
|
||||
Select::make('targetTenantId')
|
||||
->label('Target tenant')
|
||||
->options(fn (): array => $this->tenantOptions())
|
||||
->placeholder('Select a source environment')
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-source'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-environment-source']),
|
||||
Select::make('targetEnvironmentId')
|
||||
->label('Target environment')
|
||||
->options(fn (): array => $this->environmentOptions())
|
||||
->searchable()
|
||||
->preload()
|
||||
->native(false)
|
||||
->placeholder('Select a target tenant')
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-target'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-tenant-target']),
|
||||
->placeholder('Select a target environment')
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-target'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-environment-target']),
|
||||
Select::make('selectedPolicyTypes')
|
||||
->label('Governed subjects')
|
||||
->options(fn (): array => $this->policyTypeOptions())
|
||||
@ -151,10 +151,10 @@ public function form(Schema $schema): Schema
|
||||
->native(false)
|
||||
->placeholder('All governed subjects')
|
||||
->helperText(fn (): ?string => $this->policyTypeOptions() === []
|
||||
? 'Governed subject filters appear after authorized tenant inventory exists in the active workspace.'
|
||||
? 'Governed subject filters appear after authorized environment inventory exists in the active workspace.'
|
||||
: null)
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-tenant-policy-types'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-tenant-policy-types']),
|
||||
->extraFieldWrapperAttributes(['data-testid' => 'cross-environment-policy-types'])
|
||||
->extraInputAttributes(['data-testid' => 'cross-environment-policy-types']),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
@ -176,24 +176,24 @@ protected function getHeaderActions(): array
|
||||
->url($navigationContext->backLinkUrl);
|
||||
}
|
||||
|
||||
$sourceTenant = $this->selectedSourceTenant();
|
||||
$sourceEnvironment = $this->selectedSourceEnvironment();
|
||||
|
||||
if ($sourceTenant instanceof ManagedEnvironment) {
|
||||
$actions[] = Action::make('open_source_tenant')
|
||||
->label('Open source tenant')
|
||||
if ($sourceEnvironment instanceof ManagedEnvironment) {
|
||||
$actions[] = Action::make('open_source_environment')
|
||||
->label('Open source environment')
|
||||
->icon('heroicon-o-arrow-top-right-on-square')
|
||||
->color('gray')
|
||||
->url(ManagedEnvironmentLinks::viewUrl($sourceTenant));
|
||||
->url(ManagedEnvironmentLinks::viewUrl($sourceEnvironment));
|
||||
}
|
||||
|
||||
$targetTenant = $this->selectedTargetTenant();
|
||||
$targetEnvironment = $this->selectedTargetEnvironment();
|
||||
|
||||
if ($targetTenant instanceof ManagedEnvironment) {
|
||||
$actions[] = Action::make('open_target_tenant')
|
||||
->label('Open target tenant')
|
||||
if ($targetEnvironment instanceof ManagedEnvironment) {
|
||||
$actions[] = Action::make('open_target_environment')
|
||||
->label('Open target environment')
|
||||
->icon('heroicon-o-arrow-top-right-on-square')
|
||||
->color('gray')
|
||||
->url(ManagedEnvironmentLinks::viewUrl($targetTenant));
|
||||
->url(ManagedEnvironmentLinks::viewUrl($targetEnvironment));
|
||||
}
|
||||
|
||||
$preflightAction = Action::make('generatePromotionPreflight')
|
||||
@ -254,15 +254,15 @@ public function applySelection(): void
|
||||
$this->selectionMessage = null;
|
||||
$this->preflight = null;
|
||||
|
||||
$this->sourceTenantId = $this->normalizeTenantIdentifier($this->sourceTenantId);
|
||||
$this->targetTenantId = $this->normalizeTenantIdentifier($this->targetTenantId);
|
||||
$this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier($this->sourceEnvironmentId);
|
||||
$this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier($this->targetEnvironmentId);
|
||||
$this->selectedPolicyTypes = $this->normalizePolicyTypes($this->selectedPolicyTypes);
|
||||
|
||||
if ($this->sourceTenantId !== null
|
||||
&& $this->targetTenantId !== null
|
||||
&& $this->sourceTenantId === $this->targetTenantId) {
|
||||
$this->selectionMessage = 'Choose two different tenants.';
|
||||
$this->addError('targetTenantId', $this->selectionMessage);
|
||||
if ($this->sourceEnvironmentId !== null
|
||||
&& $this->targetEnvironmentId !== null
|
||||
&& $this->sourceEnvironmentId === $this->targetEnvironmentId) {
|
||||
$this->selectionMessage = 'Choose two different environments.';
|
||||
$this->addError('targetEnvironmentId', $this->selectionMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -285,20 +285,20 @@ public function generatePromotionPreflight(): void
|
||||
|
||||
$selection = $this->compareSelection();
|
||||
|
||||
if (! $selection instanceof CrossTenantCompareSelection) {
|
||||
if (! $selection instanceof CrossEnvironmentCompareSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->preflight = app(CrossTenantPromotionPreflight::class)->build($this->preview);
|
||||
$this->preflight = app(CrossEnvironmentPromotionPreflight::class)->build($this->preview);
|
||||
|
||||
$workspace = $this->workspace();
|
||||
$user = auth()->user();
|
||||
|
||||
if ($workspace instanceof Workspace && $user instanceof User) {
|
||||
app(WorkspaceAuditLogger::class)->logCrossTenantPromotionPreflightGenerated(
|
||||
app(WorkspaceAuditLogger::class)->logCrossEnvironmentPromotionPreflightGenerated(
|
||||
workspace: $workspace,
|
||||
sourceTenant: $selection->sourceTenant,
|
||||
targetTenant: $selection->targetTenant,
|
||||
sourceEnvironment: $selection->sourceEnvironment,
|
||||
targetEnvironment: $selection->targetEnvironment,
|
||||
preflight: $this->preflight,
|
||||
actor: $user,
|
||||
);
|
||||
@ -323,7 +323,7 @@ public function executePromotion(): void
|
||||
$selection = $this->compareSelection();
|
||||
$user = auth()->user();
|
||||
|
||||
if (! $selection instanceof CrossTenantCompareSelection || ! $user instanceof User) {
|
||||
if (! $selection instanceof CrossEnvironmentCompareSelection || ! $user instanceof User) {
|
||||
Notification::make()
|
||||
->title('Promotion execution unavailable')
|
||||
->body('Refresh the compare selection before executing promotion.')
|
||||
@ -334,7 +334,7 @@ public function executePromotion(): void
|
||||
}
|
||||
|
||||
try {
|
||||
$result = app(CrossTenantPromotionExecutionService::class)->start(
|
||||
$result = app(CrossEnvironmentPromotionExecutionService::class)->start(
|
||||
selection: $selection,
|
||||
preview: $this->preview,
|
||||
preflight: $this->preflight,
|
||||
@ -376,8 +376,8 @@ public function executePromotion(): void
|
||||
public function clearSelectionUrl(): string
|
||||
{
|
||||
return static::getUrl($this->routeParameters([
|
||||
self::SOURCE_TENANT_QUERY_KEY => null,
|
||||
self::TARGET_TENANT_QUERY_KEY => null,
|
||||
self::SOURCE_ENVIRONMENT_QUERY_KEY => null,
|
||||
self::TARGET_ENVIRONMENT_QUERY_KEY => null,
|
||||
self::POLICY_TYPE_QUERY_KEY => null,
|
||||
]), panel: 'admin');
|
||||
}
|
||||
@ -388,18 +388,18 @@ public function selectionUrl(): string
|
||||
}
|
||||
|
||||
public static function launchUrl(
|
||||
?ManagedEnvironment $sourceTenant = null,
|
||||
?ManagedEnvironment $targetTenant = null,
|
||||
?ManagedEnvironment $sourceEnvironment = null,
|
||||
?ManagedEnvironment $targetEnvironment = null,
|
||||
?CanonicalNavigationContext $navigationContext = null,
|
||||
): string {
|
||||
$parameters = [];
|
||||
|
||||
if ($sourceTenant instanceof ManagedEnvironment) {
|
||||
$parameters[self::SOURCE_TENANT_QUERY_KEY] = (int) $sourceTenant->getKey();
|
||||
if ($sourceEnvironment instanceof ManagedEnvironment) {
|
||||
$parameters[self::SOURCE_ENVIRONMENT_QUERY_KEY] = (int) $sourceEnvironment->getKey();
|
||||
}
|
||||
|
||||
if ($targetTenant instanceof ManagedEnvironment) {
|
||||
$parameters[self::TARGET_TENANT_QUERY_KEY] = (int) $targetTenant->getKey();
|
||||
if ($targetEnvironment instanceof ManagedEnvironment) {
|
||||
$parameters[self::TARGET_ENVIRONMENT_QUERY_KEY] = (int) $targetEnvironment->getKey();
|
||||
}
|
||||
|
||||
if ($navigationContext instanceof CanonicalNavigationContext) {
|
||||
@ -411,8 +411,8 @@ public static function launchUrl(
|
||||
|
||||
public function hasActiveSelection(): bool
|
||||
{
|
||||
return $this->sourceTenantId !== null
|
||||
|| $this->targetTenantId !== null
|
||||
return $this->sourceEnvironmentId !== null
|
||||
|| $this->targetEnvironmentId !== null
|
||||
|| $this->selectedPolicyTypes !== [];
|
||||
}
|
||||
|
||||
@ -438,26 +438,26 @@ public function reasonLabel(string $reasonCode): string
|
||||
return Str::headline(str_replace('_', ' ', $reasonCode));
|
||||
}
|
||||
|
||||
public function sourceTenantUrl(): ?string
|
||||
public function sourceEnvironmentUrl(): ?string
|
||||
{
|
||||
$tenant = $this->selectedSourceTenant();
|
||||
$environment = $this->selectedSourceEnvironment();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
if (! $environment instanceof ManagedEnvironment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ManagedEnvironmentLinks::viewUrl($tenant);
|
||||
return ManagedEnvironmentLinks::viewUrl($environment);
|
||||
}
|
||||
|
||||
public function targetTenantUrl(): ?string
|
||||
public function targetEnvironmentUrl(): ?string
|
||||
{
|
||||
$tenant = $this->selectedTargetTenant();
|
||||
$environment = $this->selectedTargetEnvironment();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
if (! $environment instanceof ManagedEnvironment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ManagedEnvironmentLinks::viewUrl($tenant);
|
||||
return ManagedEnvironmentLinks::viewUrl($environment);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -466,16 +466,16 @@ public function targetTenantUrl(): ?string
|
||||
private function formState(): array
|
||||
{
|
||||
return [
|
||||
'sourceTenantId' => $this->sourceTenantId,
|
||||
'targetTenantId' => $this->targetTenantId,
|
||||
'sourceEnvironmentId' => $this->sourceEnvironmentId,
|
||||
'targetEnvironmentId' => $this->targetEnvironmentId,
|
||||
'selectedPolicyTypes' => $this->selectedPolicyTypes,
|
||||
];
|
||||
}
|
||||
|
||||
private function hydrateSelectionFromRequest(): void
|
||||
{
|
||||
$this->sourceTenantId = $this->normalizeTenantIdentifier(request()->query(self::SOURCE_TENANT_QUERY_KEY));
|
||||
$this->targetTenantId = $this->normalizeTenantIdentifier(request()->query(self::TARGET_TENANT_QUERY_KEY));
|
||||
$this->sourceEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::SOURCE_ENVIRONMENT_QUERY_KEY));
|
||||
$this->targetEnvironmentId = $this->normalizeEnvironmentIdentifier(request()->query(self::TARGET_ENVIRONMENT_QUERY_KEY));
|
||||
$this->selectedPolicyTypes = $this->normalizePolicyTypes(request()->query(self::POLICY_TYPE_QUERY_KEY, []));
|
||||
}
|
||||
|
||||
@ -487,11 +487,11 @@ private function refreshPreview(): void
|
||||
|
||||
$selection = $this->compareSelection();
|
||||
|
||||
if (! $selection instanceof CrossTenantCompareSelection) {
|
||||
if (! $selection instanceof CrossEnvironmentCompareSelection) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->preview = app(CrossTenantComparePreviewBuilder::class)->build($selection);
|
||||
$this->preview = app(CrossEnvironmentComparePreviewBuilder::class)->build($selection);
|
||||
}
|
||||
|
||||
private function authorizePageAccess(): void
|
||||
@ -554,61 +554,61 @@ private function authorizePromotionExecution(): void
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$targetTenant = $this->selectedTargetTenant();
|
||||
$targetEnvironment = $this->selectedTargetEnvironment();
|
||||
|
||||
if (! $targetTenant instanceof ManagedEnvironment) {
|
||||
if (! $targetEnvironment instanceof ManagedEnvironment) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/** @var CapabilityResolver $resolver */
|
||||
$resolver = app(CapabilityResolver::class);
|
||||
|
||||
if (! $resolver->can($user, $targetTenant, Capabilities::TENANT_MANAGE)) {
|
||||
if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
|
||||
private function compareSelection(): ?CrossTenantCompareSelection
|
||||
private function compareSelection(): ?CrossEnvironmentCompareSelection
|
||||
{
|
||||
$sourceTenant = $this->selectedSourceTenant();
|
||||
$targetTenant = $this->selectedTargetTenant();
|
||||
$sourceEnvironment = $this->selectedSourceEnvironment();
|
||||
$targetEnvironment = $this->selectedTargetEnvironment();
|
||||
|
||||
if (! $sourceTenant instanceof ManagedEnvironment || ! $targetTenant instanceof ManagedEnvironment) {
|
||||
if (! $sourceEnvironment instanceof ManagedEnvironment || ! $targetEnvironment instanceof ManagedEnvironment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((int) $sourceTenant->getKey() === (int) $targetTenant->getKey()) {
|
||||
$this->selectionMessage = 'Choose two different tenants.';
|
||||
if ((int) $sourceEnvironment->getKey() === (int) $targetEnvironment->getKey()) {
|
||||
$this->selectionMessage = 'Choose two different environments.';
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return new CrossTenantCompareSelection(
|
||||
sourceTenant: $sourceTenant,
|
||||
targetTenant: $targetTenant,
|
||||
return new CrossEnvironmentCompareSelection(
|
||||
sourceEnvironment: $sourceEnvironment,
|
||||
targetEnvironment: $targetEnvironment,
|
||||
policyTypes: $this->selectedPolicyTypes,
|
||||
);
|
||||
}
|
||||
|
||||
private function selectedSourceTenant(): ?ManagedEnvironment
|
||||
private function selectedSourceEnvironment(): ?ManagedEnvironment
|
||||
{
|
||||
if ($this->sourceTenantId === null) {
|
||||
if ($this->sourceEnvironmentId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveAuthorizedTenant($this->sourceTenantId);
|
||||
return $this->resolveAuthorizedEnvironment($this->sourceEnvironmentId);
|
||||
}
|
||||
|
||||
private function selectedTargetTenant(): ?ManagedEnvironment
|
||||
private function selectedTargetEnvironment(): ?ManagedEnvironment
|
||||
{
|
||||
if ($this->targetTenantId === null) {
|
||||
if ($this->targetEnvironmentId === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->resolveAuthorizedTenant($this->targetTenantId);
|
||||
return $this->resolveAuthorizedEnvironment($this->targetEnvironmentId);
|
||||
}
|
||||
|
||||
private function resolveAuthorizedTenant(string $tenantId): ManagedEnvironment
|
||||
private function resolveAuthorizedEnvironment(string $environmentId): ManagedEnvironment
|
||||
{
|
||||
$workspace = $this->workspace();
|
||||
$user = auth()->user();
|
||||
@ -617,29 +617,29 @@ private function resolveAuthorizedTenant(string $tenantId): ManagedEnvironment
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$tenant = ManagedEnvironment::query()
|
||||
$environment = ManagedEnvironment::query()
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->whereKey((int) $tenantId)
|
||||
->whereKey((int) $environmentId)
|
||||
->first();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
if (! $environment instanceof ManagedEnvironment) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
/** @var CapabilityResolver $resolver */
|
||||
$resolver = app(CapabilityResolver::class);
|
||||
|
||||
if (! $user->canAccessTenant($tenant) || ! $resolver->can($user, $tenant, Capabilities::TENANT_VIEW)) {
|
||||
if (! $user->canAccessTenant($environment) || ! $resolver->can($user, $environment, Capabilities::TENANT_VIEW)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return $tenant;
|
||||
return $environment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function tenantOptions(): array
|
||||
private function environmentOptions(): array
|
||||
{
|
||||
$workspace = $this->workspace();
|
||||
$user = auth()->user();
|
||||
@ -651,17 +651,17 @@ private function tenantOptions(): array
|
||||
/** @var CapabilityResolver $resolver */
|
||||
$resolver = app(CapabilityResolver::class);
|
||||
|
||||
$tenants = $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
|
||||
$environments = $user->accessibleManagedEnvironmentsQuery((int) $workspace->getKey())
|
||||
->select('managed_environments.*')
|
||||
->orderBy('managed_environments.name')
|
||||
->get();
|
||||
|
||||
$resolver->primeMemberships($user, $tenants->modelKeys());
|
||||
$resolver->primeMemberships($user, $environments->modelKeys());
|
||||
|
||||
return $tenants
|
||||
->filter(fn (ManagedEnvironment $tenant): bool => $resolver->can($user, $tenant, Capabilities::TENANT_VIEW))
|
||||
->mapWithKeys(fn (ManagedEnvironment $tenant): array => [
|
||||
(string) $tenant->getKey() => (string) $tenant->name,
|
||||
return $environments
|
||||
->filter(fn (ManagedEnvironment $environment): bool => $resolver->can($user, $environment, Capabilities::TENANT_VIEW))
|
||||
->mapWithKeys(fn (ManagedEnvironment $environment): array => [
|
||||
(string) $environment->getKey() => (string) $environment->name,
|
||||
])
|
||||
->all();
|
||||
}
|
||||
@ -671,14 +671,14 @@ private function tenantOptions(): array
|
||||
*/
|
||||
private function policyTypeOptions(): array
|
||||
{
|
||||
$tenantIds = array_map(static fn (string $tenantId): int => (int) $tenantId, array_keys($this->tenantOptions()));
|
||||
$environmentIds = array_map(static fn (string $environmentId): int => (int) $environmentId, array_keys($this->environmentOptions()));
|
||||
|
||||
if ($tenantIds === []) {
|
||||
if ($environmentIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return InventoryItem::query()
|
||||
->whereIn('managed_environment_id', $tenantIds)
|
||||
->whereIn('managed_environment_id', $environmentIds)
|
||||
->whereNotNull('policy_type')
|
||||
->where('policy_type', '!=', '')
|
||||
->distinct()
|
||||
@ -697,7 +697,7 @@ private function preflightDisabledReason(): ?string
|
||||
}
|
||||
|
||||
if (! is_array($this->preview)) {
|
||||
return 'Select an authorized source and target tenant to generate a promotion preflight.';
|
||||
return 'Select an authorized source and target environment to generate a promotion preflight.';
|
||||
}
|
||||
|
||||
if ((int) data_get($this->preview, 'summary.total', 0) === 0) {
|
||||
@ -737,14 +737,14 @@ private function executePromotionDisabledReason(): ?string
|
||||
return 'You need workspace baseline manage access to execute promotion.';
|
||||
}
|
||||
|
||||
$targetTenant = $this->selectedTargetTenant();
|
||||
$targetEnvironment = $this->selectedTargetEnvironment();
|
||||
|
||||
if ($targetTenant instanceof ManagedEnvironment) {
|
||||
if ($targetEnvironment instanceof ManagedEnvironment) {
|
||||
/** @var CapabilityResolver $resolver */
|
||||
$resolver = app(CapabilityResolver::class);
|
||||
|
||||
if (! $resolver->can($user, $targetTenant, Capabilities::TENANT_MANAGE)) {
|
||||
return 'You need target tenant manage access to execute promotion.';
|
||||
if (! $resolver->can($user, $targetEnvironment, Capabilities::TENANT_MANAGE)) {
|
||||
return 'You need target environment manage access to execute promotion.';
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -760,13 +760,13 @@ private function executePromotionConfirmationDescription(): string
|
||||
$manualMappingRequired = (int) data_get($this->preflight, 'summary.manual_mapping_required', 0);
|
||||
$excluded = $blocked + $manualMappingRequired;
|
||||
|
||||
$sourceTenantName = $selection?->sourceTenant->name ?? 'Source tenant';
|
||||
$targetTenantName = $selection?->targetTenant->name ?? 'Target tenant';
|
||||
$sourceEnvironmentName = $selection?->sourceEnvironment->name ?? 'Source environment';
|
||||
$targetEnvironmentName = $selection?->targetEnvironment->name ?? 'Target environment';
|
||||
|
||||
return sprintf(
|
||||
'Queue one promotion run from %s to %s for %d ready governed subject%s. %d subject%s remain excluded on the compare page.',
|
||||
$sourceTenantName,
|
||||
$targetTenantName,
|
||||
$sourceEnvironmentName,
|
||||
$targetEnvironmentName,
|
||||
$ready,
|
||||
$ready === 1 ? '' : 's',
|
||||
$excluded,
|
||||
@ -777,7 +777,7 @@ private function executePromotionConfirmationDescription(): string
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function normalizeTenantIdentifier(mixed $value): ?string
|
||||
private function normalizeEnvironmentIdentifier(mixed $value): ?string
|
||||
{
|
||||
if (! is_string($value) && ! is_int($value)) {
|
||||
return null;
|
||||
@ -815,8 +815,8 @@ private function normalizePolicyTypes(mixed $value): array
|
||||
private function routeParameters(array $overrides = []): array
|
||||
{
|
||||
$parameters = [
|
||||
self::SOURCE_TENANT_QUERY_KEY => $this->sourceTenantId,
|
||||
self::TARGET_TENANT_QUERY_KEY => $this->targetTenantId,
|
||||
self::SOURCE_ENVIRONMENT_QUERY_KEY => $this->sourceEnvironmentId,
|
||||
self::TARGET_ENVIRONMENT_QUERY_KEY => $this->targetEnvironmentId,
|
||||
self::POLICY_TYPE_QUERY_KEY => $this->selectedPolicyTypes,
|
||||
];
|
||||
|
||||
@ -5,9 +5,9 @@
|
||||
namespace App\Filament\Pages;
|
||||
|
||||
use App\Filament\Pages\Governance\GovernanceInbox;
|
||||
use App\Filament\Widgets\Tenant\TenantTriageArrivalContinuity;
|
||||
use App\Filament\Widgets\Dashboard\TenantDashboardContextChips;
|
||||
use App\Filament\Widgets\Dashboard\TenantDashboardOverview;
|
||||
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentTriageArrivalContinuity;
|
||||
use App\Filament\Widgets\Dashboard\EnvironmentDashboardContextChips;
|
||||
use App\Filament\Widgets\Dashboard\EnvironmentDashboardOverview;
|
||||
use App\Models\SupportRequest;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
@ -21,8 +21,8 @@
|
||||
use App\Support\SupportDiagnostics\SupportDiagnosticBundleBuilder;
|
||||
use App\Support\SupportRequests\ExternalSupportDeskHandoffService;
|
||||
use App\Support\SupportRequests\SupportRequestSubmissionService;
|
||||
use App\Support\TenantDashboard\TenantDashboardSummary;
|
||||
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
|
||||
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummary;
|
||||
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Facades\Filament;
|
||||
@ -43,7 +43,7 @@
|
||||
|
||||
use App\Filament\Widgets\Dashboard\DashboardKpis;
|
||||
|
||||
class TenantDashboard extends Dashboard
|
||||
class EnvironmentDashboard extends Dashboard
|
||||
{
|
||||
protected Width|string|null $maxContentWidth = Width::Full;
|
||||
|
||||
@ -52,7 +52,7 @@ class TenantDashboard extends Dashboard
|
||||
*/
|
||||
public array $supportDiagnosticsAuditKeys = [];
|
||||
|
||||
private ?TenantDashboardSummary $dashboardSummary = null;
|
||||
private ?EnvironmentDashboardSummary $dashboardSummary = null;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
@ -69,7 +69,7 @@ public function getTitle(): string | Htmlable
|
||||
|
||||
$summary = $this->dashboardSummary();
|
||||
|
||||
if (! $summary instanceof TenantDashboardSummary) {
|
||||
if (! $summary instanceof EnvironmentDashboardSummary) {
|
||||
return (string) $tenant->name;
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@ public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return [
|
||||
TenantDashboardContextChips::class,
|
||||
EnvironmentDashboardContextChips::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -127,9 +127,9 @@ public function getHeaderWidgetsColumns(): int|array
|
||||
public function getWidgets(): array
|
||||
{
|
||||
return [
|
||||
TenantTriageArrivalContinuity::class,
|
||||
ManagedEnvironmentTriageArrivalContinuity::class,
|
||||
DashboardKpis::class,
|
||||
TenantDashboardOverview::class,
|
||||
EnvironmentDashboardOverview::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -204,7 +204,7 @@ private function primaryFollowUpHeaderPayload(): ?array
|
||||
{
|
||||
$summary = $this->dashboardSummary();
|
||||
|
||||
if (! $summary instanceof TenantDashboardSummary) {
|
||||
if (! $summary instanceof EnvironmentDashboardSummary) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ private function secondaryHeaderPayload(): ?array
|
||||
{
|
||||
$summary = $this->dashboardSummary();
|
||||
|
||||
if (! $summary instanceof TenantDashboardSummary) {
|
||||
if (! $summary instanceof EnvironmentDashboardSummary) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -304,9 +304,9 @@ private function summaryHeaderAction(string $name, array $payload, string $color
|
||||
return $action;
|
||||
}
|
||||
|
||||
private function dashboardSummary(): ?TenantDashboardSummary
|
||||
private function dashboardSummary(): ?EnvironmentDashboardSummary
|
||||
{
|
||||
if ($this->dashboardSummary instanceof TenantDashboardSummary) {
|
||||
if ($this->dashboardSummary instanceof EnvironmentDashboardSummary) {
|
||||
return $this->dashboardSummary;
|
||||
}
|
||||
|
||||
@ -317,7 +317,7 @@ private function dashboardSummary(): ?TenantDashboardSummary
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->dashboardSummary = app(TenantDashboardSummaryBuilder::class)->build($tenant, $user);
|
||||
$this->dashboardSummary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, $user);
|
||||
|
||||
return $this->dashboardSummary;
|
||||
}
|
||||
@ -6,8 +6,8 @@
|
||||
|
||||
use App\Filament\Concerns\ResolvesPanelTenantContext;
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\TenantDiagnosticsService;
|
||||
use App\Services\Auth\TenantMembershipManager;
|
||||
use App\Services\Auth\ManagedEnvironmentDiagnosticsService;
|
||||
use App\Services\Auth\ManagedEnvironmentMembershipManager;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\Rbac\UiTooltips;
|
||||
@ -17,7 +17,7 @@
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Pages\Page;
|
||||
|
||||
class TenantDiagnostics extends Page
|
||||
class EnvironmentDiagnostics extends Page
|
||||
{
|
||||
use ResolvesPanelTenantContext;
|
||||
|
||||
@ -25,7 +25,7 @@ class TenantDiagnostics extends Page
|
||||
|
||||
protected static ?string $slug = 'diagnostics';
|
||||
|
||||
protected string $view = 'filament.pages.tenant-diagnostics';
|
||||
protected string $view = 'filament.pages.environment-diagnostics';
|
||||
|
||||
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
|
||||
{
|
||||
@ -44,14 +44,14 @@ public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
|
||||
public function mount(): void
|
||||
{
|
||||
$tenant = static::resolveTenantContextForCurrentPanelOrFail();
|
||||
$this->missingOwner = app(TenantDiagnosticsService::class)->tenantHasNoOwners($tenant);
|
||||
$this->missingOwner = app(ManagedEnvironmentDiagnosticsService::class)->tenantHasNoOwners($tenant);
|
||||
|
||||
$user = auth()->user();
|
||||
if (! $user instanceof User) {
|
||||
abort(403, 'Not allowed');
|
||||
}
|
||||
|
||||
$this->hasDuplicateMembershipsForCurrentUser = app(TenantDiagnosticsService::class)
|
||||
$this->hasDuplicateMembershipsForCurrentUser = app(ManagedEnvironmentDiagnosticsService::class)
|
||||
->userHasDuplicateMemberships($tenant, $user);
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ public function bootstrapOwner(): void
|
||||
abort(403, 'Not allowed');
|
||||
}
|
||||
|
||||
app(TenantMembershipManager::class)->grantScope($tenant, $user, $user, sourceRef: 'diagnostic');
|
||||
app(ManagedEnvironmentMembershipManager::class)->grantScope($tenant, $user, $user, sourceRef: 'diagnostic');
|
||||
|
||||
$this->mount();
|
||||
}
|
||||
@ -110,7 +110,7 @@ public function mergeDuplicateMemberships(): void
|
||||
abort(403, 'Not allowed');
|
||||
}
|
||||
|
||||
app(TenantDiagnosticsService::class)->mergeDuplicateMembershipsForUser($tenant, $user, $user);
|
||||
app(ManagedEnvironmentDiagnosticsService::class)->mergeDuplicateMembershipsForUser($tenant, $user, $user);
|
||||
|
||||
$this->mount();
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
use App\Models\WorkspaceMembership;
|
||||
use App\Services\Intune\TenantRequiredPermissionsViewModelBuilder;
|
||||
use App\Services\Intune\ManagedEnvironmentRequiredPermissionsViewModelBuilder;
|
||||
use App\Support\Badges\BadgeDomain;
|
||||
use App\Support\Badges\BadgeRenderer;
|
||||
use App\Support\Filament\TablePaginationProfiles;
|
||||
@ -29,7 +29,7 @@
|
||||
use Livewire\Attributes\Locked;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
class TenantRequiredPermissions extends Page implements HasTable
|
||||
class EnvironmentRequiredPermissions extends Page implements HasTable
|
||||
{
|
||||
use InteractsWithTable;
|
||||
|
||||
@ -37,11 +37,11 @@ class TenantRequiredPermissions extends Page implements HasTable
|
||||
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
protected static ?string $slug = 'workspaces/{workspace}/environments/{tenant}/required-permissions';
|
||||
protected static ?string $slug = 'workspaces/{workspace}/environments/{environment}/required-permissions';
|
||||
|
||||
protected static ?string $title = 'Required permissions';
|
||||
|
||||
protected string $view = 'filament.pages.tenant-required-permissions';
|
||||
protected string $view = 'filament.pages.environment-required-permissions';
|
||||
|
||||
public static function actionSurfaceDeclaration(): ActionSurfaceDeclaration
|
||||
{
|
||||
@ -73,9 +73,9 @@ public function currentTenant(): ?ManagedEnvironment
|
||||
return $this->trustedScopedTenant();
|
||||
}
|
||||
|
||||
public function mount(ManagedEnvironment|string|null $tenant = null): void
|
||||
public function mount(ManagedEnvironment|string|null $environment = null): void
|
||||
{
|
||||
$tenant = static::resolveScopedTenant($tenant);
|
||||
$tenant = static::resolveScopedTenant($environment);
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment || ! static::hasScopedTenantAccess($tenant)) {
|
||||
abort(404);
|
||||
@ -151,10 +151,10 @@ public function table(Table $table): Table
|
||||
TextColumn::make('status')
|
||||
->label('Status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus))
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->sortable(),
|
||||
TextColumn::make('features_label')
|
||||
->label('Features')
|
||||
@ -235,7 +235,7 @@ protected static function resolveScopedTenant(ManagedEnvironment|string|null $te
|
||||
->first();
|
||||
}
|
||||
|
||||
$routeTenant = request()->route('tenant');
|
||||
$routeTenant = request()->route('environment') ?? request()->route('tenant');
|
||||
|
||||
if ($routeTenant instanceof ManagedEnvironment) {
|
||||
return $routeTenant;
|
||||
@ -333,7 +333,7 @@ private function trustedScopedTenant(): ?ManagedEnvironment
|
||||
*/
|
||||
private function filterState(array $filters = [], ?string $search = null): array
|
||||
{
|
||||
return TenantRequiredPermissionsViewModelBuilder::normalizeFilterState([
|
||||
return ManagedEnvironmentRequiredPermissionsViewModelBuilder::normalizeFilterState([
|
||||
'status' => $filters['status']['value'] ?? data_get($this->tableFilters, 'status.value'),
|
||||
'type' => $filters['type']['value'] ?? data_get($this->tableFilters, 'type.value'),
|
||||
'features' => $filters['features']['values'] ?? data_get($this->tableFilters, 'features.values', []),
|
||||
@ -359,7 +359,7 @@ private function viewModelForState(array $state): array
|
||||
return $this->cachedViewModel;
|
||||
}
|
||||
|
||||
$builder = app(TenantRequiredPermissionsViewModelBuilder::class);
|
||||
$builder = app(ManagedEnvironmentRequiredPermissionsViewModelBuilder::class);
|
||||
|
||||
$this->cachedViewModelStateKey = $stateKey ?: null;
|
||||
$this->cachedViewModel = $builder->build($tenant, $state);
|
||||
@ -514,7 +514,7 @@ private function seedTableStateFromQuery(): void
|
||||
|
||||
$queryFeatures = request()->query('features', []);
|
||||
|
||||
$state = TenantRequiredPermissionsViewModelBuilder::normalizeFilterState([
|
||||
$state = ManagedEnvironmentRequiredPermissionsViewModelBuilder::normalizeFilterState([
|
||||
'status' => request()->query('status', 'missing'),
|
||||
'type' => request()->query('type', 'all'),
|
||||
'features' => is_array($queryFeatures) ? $queryFeatures : [],
|
||||
@ -288,10 +288,10 @@ public function emptyState(): array
|
||||
'title' => 'No visible assigned findings right now',
|
||||
'body' => 'Nothing currently assigned to you needs attention across the visible environment scope. Choose an environment to continue working elsewhere in the workspace.',
|
||||
'icon' => 'heroicon-o-clipboard-document-check',
|
||||
'action_name' => 'choose_tenant_empty',
|
||||
'action_name' => 'choose_environment_empty',
|
||||
'action_label' => 'Choose an environment',
|
||||
'action_kind' => 'url',
|
||||
'action_url' => route('filament.admin.pages.choose-tenant'),
|
||||
'action_url' => route('filament.admin.pages.choose-environment'),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||
use App\Services\TenantReviews\TenantReviewRegisterService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\GovernanceInbox\GovernanceInboxSectionBuilder;
|
||||
use App\Support\Navigation\CanonicalNavigationContext;
|
||||
@ -333,7 +333,7 @@ private function reviewTenants(): array
|
||||
return $this->reviewTenants = [];
|
||||
}
|
||||
|
||||
$service = app(TenantReviewRegisterService::class);
|
||||
$service = app(EnvironmentReviewRegisterService::class);
|
||||
|
||||
if (! $service->canAccessWorkspace($user, $workspace)) {
|
||||
return $this->reviewTenants = [];
|
||||
|
||||
@ -7,11 +7,11 @@
|
||||
use App\Filament\Resources\EvidenceSnapshotResource;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Support\Badges\BadgeCatalog;
|
||||
use App\Support\Badges\BadgeDomain;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
||||
@ -398,13 +398,13 @@ private function latestAccessibleSnapshots(): Collection
|
||||
*/
|
||||
private function currentReviewTenantIds(Collection $snapshots): array
|
||||
{
|
||||
return TenantReview::query()
|
||||
return EnvironmentReview::query()
|
||||
->where('workspace_id', $this->workspaceId())
|
||||
->whereIn('managed_environment_id', $snapshots->pluck('managed_environment_id')->map(static fn (mixed $tenantId): int => (int) $tenantId)->all())
|
||||
->whereIn('status', [
|
||||
TenantReviewStatus::Draft->value,
|
||||
TenantReviewStatus::Ready->value,
|
||||
TenantReviewStatus::Published->value,
|
||||
EnvironmentReviewStatus::Draft->value,
|
||||
EnvironmentReviewStatus::Ready->value,
|
||||
EnvironmentReviewStatus::Published->value,
|
||||
])
|
||||
->pluck('managed_environment_id')
|
||||
->mapWithKeys(static fn (mixed $tenantId): array => [(int) $tenantId => true])
|
||||
|
||||
@ -93,6 +93,6 @@ public function createWorkspace(array $data): void
|
||||
->success()
|
||||
->send();
|
||||
|
||||
$this->redirect(ChooseTenant::getUrl());
|
||||
$this->redirect(ChooseEnvironment::getUrl());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,16 +4,16 @@
|
||||
|
||||
namespace App\Filament\Pages\Reviews;
|
||||
|
||||
use App\Filament\Resources\TenantReviewResource;
|
||||
use App\Filament\Resources\EnvironmentReviewResource;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\FindingException;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\TenantReviews\TenantReviewRegisterService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Findings\FindingOutcomeSemantics;
|
||||
@ -21,7 +21,7 @@
|
||||
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
||||
use App\Support\Navigation\CanonicalNavigationContext;
|
||||
use App\Support\ReviewPackStatus;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
||||
@ -236,7 +236,7 @@ public function authorizedTenants(): array
|
||||
return $this->authorizedTenants = [];
|
||||
}
|
||||
|
||||
return $this->authorizedTenants = app(TenantReviewRegisterService::class)->authorizedTenants($user, $workspace);
|
||||
return $this->authorizedTenants = app(EnvironmentReviewRegisterService::class)->authorizedTenants($user, $workspace);
|
||||
}
|
||||
|
||||
private function authorizePageAccess(): void
|
||||
@ -252,7 +252,7 @@ private function authorizePageAccess(): void
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
$service = app(TenantReviewRegisterService::class);
|
||||
$service = app(EnvironmentReviewRegisterService::class);
|
||||
|
||||
if (! $service->canAccessWorkspace($user, $workspace)) {
|
||||
throw new NotFoundHttpException;
|
||||
@ -300,7 +300,7 @@ private function workspaceQuery(): Builder
|
||||
return ManagedEnvironment::query()->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
return app(TenantReviewRegisterService::class)->customerWorkspaceTenantQuery($user, $workspace);
|
||||
return app(EnvironmentReviewRegisterService::class)->customerWorkspaceTenantQuery($user, $workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -377,18 +377,18 @@ private function workspace(): ?Workspace
|
||||
: null;
|
||||
}
|
||||
|
||||
private function latestPublishedReview(ManagedEnvironment $tenant): ?TenantReview
|
||||
private function latestPublishedReview(ManagedEnvironment $tenant): ?EnvironmentReview
|
||||
{
|
||||
$review = $tenant->tenantReviews->first();
|
||||
$review = $tenant->environmentReviews->first();
|
||||
|
||||
return $review instanceof TenantReview ? $review : null;
|
||||
return $review instanceof EnvironmentReview ? $review : null;
|
||||
}
|
||||
|
||||
private function latestReviewUrl(ManagedEnvironment $tenant): ?string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -404,7 +404,7 @@ private function latestReviewUrl(ManagedEnvironment $tenant): ?string
|
||||
static fn (mixed $value): bool => $value !== null && $value !== '',
|
||||
);
|
||||
|
||||
return $this->appendQuery(TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant), $query);
|
||||
return $this->appendQuery(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant), $query);
|
||||
}
|
||||
|
||||
private function latestPublishedAt(ManagedEnvironment $tenant): ?\Illuminate\Support\Carbon
|
||||
@ -416,8 +416,8 @@ private function reviewTruth(ManagedEnvironment $tenant): ?ArtifactTruthEnvelope
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
return $review instanceof TenantReview
|
||||
? app(ArtifactTruthPresenter::class)->forTenantReview($review)
|
||||
return $review instanceof EnvironmentReview
|
||||
? app(ArtifactTruthPresenter::class)->forEnvironmentReview($review)
|
||||
: null;
|
||||
}
|
||||
|
||||
@ -427,7 +427,7 @@ private function reviewOutcome(ManagedEnvironment $tenant): ?CompressedGovernanc
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
$truth = $this->reviewTruth($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview || ! $truth instanceof ArtifactTruthEnvelope) {
|
||||
if (! $review instanceof EnvironmentReview || ! $truth instanceof ArtifactTruthEnvelope) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -439,7 +439,7 @@ private function latestReviewStateLabel(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review');
|
||||
}
|
||||
|
||||
@ -452,7 +452,7 @@ private function latestReviewStateColor(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return 'gray';
|
||||
}
|
||||
|
||||
@ -481,7 +481,7 @@ private function reviewOutcomeDescription(ManagedEnvironment $tenant): ?string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review_available');
|
||||
}
|
||||
|
||||
@ -524,7 +524,7 @@ private function governancePackageSummary(ManagedEnvironment $tenant): array
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@ -541,7 +541,7 @@ private function governancePackageAvailability(ManagedEnvironment $tenant): arra
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return [
|
||||
'state' => 'unavailable',
|
||||
'label' => __('localization.review.governance_package_unavailable'),
|
||||
@ -553,8 +553,8 @@ private function governancePackageAvailability(ManagedEnvironment $tenant): arra
|
||||
$user = auth()->user();
|
||||
$limitations = is_array($review->controlInterpretation()['limitations'] ?? null) ? $review->controlInterpretation()['limitations'] : [];
|
||||
$isPartialReview = in_array((string) $review->completeness_state, [
|
||||
TenantReviewCompletenessState::Partial->value,
|
||||
TenantReviewCompletenessState::Stale->value,
|
||||
EnvironmentReviewCompletenessState::Partial->value,
|
||||
EnvironmentReviewCompletenessState::Stale->value,
|
||||
], true) || $limitations !== [];
|
||||
|
||||
if (! $pack instanceof ReviewPack) {
|
||||
@ -652,7 +652,7 @@ private function controlReadinessDescription(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review_available');
|
||||
}
|
||||
|
||||
@ -720,7 +720,7 @@ private function workspaceReviewNeedsAttention(ManagedEnvironment $tenant): bool
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -739,7 +739,7 @@ private function evidenceStatusState(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
@ -802,7 +802,7 @@ private function primaryControlSummary(ManagedEnvironment $tenant): ?array
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -818,7 +818,7 @@ private function primaryControlSummary(ManagedEnvironment $tenant): ?array
|
||||
->first();
|
||||
}
|
||||
|
||||
private function controlLimitationSummary(TenantReview $review): ?string
|
||||
private function controlLimitationSummary(EnvironmentReview $review): ?string
|
||||
{
|
||||
$counts = $review->controlInterpretationLimitationCounts();
|
||||
|
||||
@ -842,7 +842,7 @@ private function findingSummary(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review_available');
|
||||
}
|
||||
|
||||
@ -869,7 +869,7 @@ private function acceptedRiskSummary(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review_available');
|
||||
}
|
||||
|
||||
@ -897,7 +897,7 @@ private function evidenceProofAvailability(ManagedEnvironment $tenant): string
|
||||
{
|
||||
$review = $this->latestPublishedReview($tenant);
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
return __('localization.review.no_published_review_available');
|
||||
}
|
||||
|
||||
@ -941,10 +941,10 @@ private function visibleInterpretationVersions(): array
|
||||
return [];
|
||||
}
|
||||
|
||||
return app(TenantReviewRegisterService::class)
|
||||
return app(EnvironmentReviewRegisterService::class)
|
||||
->latestPublishedQuery($user, $workspace)
|
||||
->get()
|
||||
->map(static fn (TenantReview $review): ?string => $review->controlInterpretationVersion())
|
||||
->map(static fn (EnvironmentReview $review): ?string => $review->controlInterpretationVersion())
|
||||
->filter()
|
||||
->unique()
|
||||
->values()
|
||||
@ -965,7 +965,7 @@ private function currentTenantFilterInterpretationVersion(): ?string
|
||||
return null;
|
||||
}
|
||||
|
||||
return $tenant->tenantReviews()->published()
|
||||
return $tenant->environmentReviews()->published()
|
||||
->latest('published_at')
|
||||
->latest('generated_at')
|
||||
->latest('id')
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
|
||||
namespace App\Filament\Pages\Reviews;
|
||||
|
||||
use App\Filament\Resources\TenantReviewResource;
|
||||
use App\Filament\Resources\EnvironmentReviewResource;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\ReviewPackService;
|
||||
use App\Services\TenantReviews\TenantReviewRegisterService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewRegisterService;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Badges\BadgeCatalog;
|
||||
use App\Support\Badges\BadgeDomain;
|
||||
@ -19,7 +19,7 @@
|
||||
use App\Support\Filament\CanonicalAdminTenantFilterState;
|
||||
use App\Support\Filament\FilterPresets;
|
||||
use App\Support\Filament\TablePaginationProfiles;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
||||
@ -112,15 +112,15 @@ public function table(Table $table): Table
|
||||
->persistFiltersInSession()
|
||||
->persistSearchInSession()
|
||||
->persistSortInSession()
|
||||
->recordUrl(fn (TenantReview $record): string => TenantReviewResource::tenantScopedUrl('view', ['record' => $record], $record->tenant))
|
||||
->recordUrl(fn (EnvironmentReview $record): string => EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record], $record->tenant))
|
||||
->columns([
|
||||
TextColumn::make('tenant.name')->label('ManagedEnvironment')->searchable(),
|
||||
TextColumn::make('status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus)),
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)),
|
||||
TextColumn::make('outcome')
|
||||
->label('Outcome')
|
||||
->badge()
|
||||
@ -154,7 +154,7 @@ public function table(Table $table): Table
|
||||
]),
|
||||
SelectFilter::make('completeness_state')
|
||||
->label('Completeness')
|
||||
->options(BadgeCatalog::options(BadgeDomain::TenantReviewCompleteness, TenantReviewCompletenessState::values())),
|
||||
->options(BadgeCatalog::options(BadgeDomain::EnvironmentReviewCompleteness, EnvironmentReviewCompletenessState::values())),
|
||||
SelectFilter::make('published_state')
|
||||
->label('Published state')
|
||||
->options([
|
||||
@ -174,11 +174,11 @@ public function table(Table $table): Table
|
||||
Action::make('export_executive_pack')
|
||||
->label('Export executive pack')
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->visible(fn (TenantReview $record): bool => auth()->user() instanceof User
|
||||
&& auth()->user()->can(Capabilities::TENANT_REVIEW_MANAGE, $record->tenant)
|
||||
->visible(fn (EnvironmentReview $record): bool => auth()->user() instanceof User
|
||||
&& auth()->user()->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $record->tenant)
|
||||
&& in_array($record->status, ['ready', 'published'], true))
|
||||
->disabled(fn (TenantReview $record): bool => (bool) (app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant)['is_blocked'] ?? false))
|
||||
->tooltip(function (TenantReview $record): ?string {
|
||||
->disabled(fn (EnvironmentReview $record): bool => (bool) (app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant)['is_blocked'] ?? false))
|
||||
->tooltip(function (EnvironmentReview $record): ?string {
|
||||
$decision = app(ReviewPackService::class)->reviewPackGenerationDecisionForTenant($record->tenant);
|
||||
|
||||
if ((bool) ($decision['is_blocked'] ?? false)) {
|
||||
@ -195,7 +195,7 @@ public function table(Table $table): Table
|
||||
|
||||
return null;
|
||||
})
|
||||
->action(fn (TenantReview $record): mixed => TenantReviewResource::executeExport($record)),
|
||||
->action(fn (EnvironmentReview $record): mixed => EnvironmentReviewResource::executeExport($record)),
|
||||
])
|
||||
->bulkActions([])
|
||||
->emptyStateHeading('No review records match this view')
|
||||
@ -225,7 +225,7 @@ public function authorizedTenants(): array
|
||||
return $this->authorizedTenants = [];
|
||||
}
|
||||
|
||||
return $this->authorizedTenants = app(TenantReviewRegisterService::class)->authorizedTenants($user, $workspace);
|
||||
return $this->authorizedTenants = app(EnvironmentReviewRegisterService::class)->authorizedTenants($user, $workspace);
|
||||
}
|
||||
|
||||
private function authorizePageAccess(): void
|
||||
@ -241,7 +241,7 @@ private function authorizePageAccess(): void
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
$service = app(TenantReviewRegisterService::class);
|
||||
$service = app(EnvironmentReviewRegisterService::class);
|
||||
|
||||
if (! $service->canAccessWorkspace($user, $workspace)) {
|
||||
throw new NotFoundHttpException;
|
||||
@ -258,10 +258,10 @@ private function registerQuery(): Builder
|
||||
$workspace = $this->workspace();
|
||||
|
||||
if (! $user instanceof User || ! $workspace instanceof Workspace) {
|
||||
return TenantReview::query()->whereRaw('1 = 0');
|
||||
return EnvironmentReview::query()->whereRaw('1 = 0');
|
||||
}
|
||||
|
||||
return app(TenantReviewRegisterService::class)->query($user, $workspace);
|
||||
return app(EnvironmentReviewRegisterService::class)->query($user, $workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -341,36 +341,36 @@ private function workspace(): ?Workspace
|
||||
: null;
|
||||
}
|
||||
|
||||
private function reviewTruth(TenantReview $record, bool $fresh = false): ArtifactTruthEnvelope
|
||||
private function reviewTruth(EnvironmentReview $record, bool $fresh = false): ArtifactTruthEnvelope
|
||||
{
|
||||
$presenter = app(ArtifactTruthPresenter::class);
|
||||
|
||||
return $fresh
|
||||
? $presenter->forTenantReviewFresh($record)
|
||||
: $presenter->forTenantReview($record);
|
||||
? $presenter->forEnvironmentReviewFresh($record)
|
||||
: $presenter->forEnvironmentReview($record);
|
||||
}
|
||||
|
||||
private function reviewOutcomeLabel(TenantReview $record): string
|
||||
private function reviewOutcomeLabel(EnvironmentReview $record): string
|
||||
{
|
||||
return $this->reviewOutcome($record)->primaryLabel;
|
||||
}
|
||||
|
||||
private function reviewOutcomeBadgeColor(TenantReview $record): string
|
||||
private function reviewOutcomeBadgeColor(EnvironmentReview $record): string
|
||||
{
|
||||
return $this->reviewOutcome($record)->primaryBadge->color;
|
||||
}
|
||||
|
||||
private function reviewOutcomeBadgeIcon(TenantReview $record): ?string
|
||||
private function reviewOutcomeBadgeIcon(EnvironmentReview $record): ?string
|
||||
{
|
||||
return $this->reviewOutcome($record)->primaryBadge->icon;
|
||||
}
|
||||
|
||||
private function reviewOutcomeBadgeIconColor(TenantReview $record): ?string
|
||||
private function reviewOutcomeBadgeIconColor(EnvironmentReview $record): ?string
|
||||
{
|
||||
return $this->reviewOutcome($record)->primaryBadge->iconColor;
|
||||
}
|
||||
|
||||
private function reviewOutcomeDescription(TenantReview $record): ?string
|
||||
private function reviewOutcomeDescription(EnvironmentReview $record): ?string
|
||||
{
|
||||
$primaryReason = $this->reviewOutcome($record)->primaryReason;
|
||||
$findingOutcomeSummary = $this->findingOutcomeSummary($record);
|
||||
@ -382,12 +382,12 @@ private function reviewOutcomeDescription(TenantReview $record): ?string
|
||||
return trim($primaryReason.' Terminal outcomes: '.$findingOutcomeSummary.'.');
|
||||
}
|
||||
|
||||
private function reviewOutcomeNextStep(TenantReview $record): string
|
||||
private function reviewOutcomeNextStep(EnvironmentReview $record): string
|
||||
{
|
||||
return $this->reviewOutcome($record)->nextActionText;
|
||||
}
|
||||
|
||||
private function reviewOutcome(TenantReview $record, bool $fresh = false): CompressedGovernanceOutcome
|
||||
private function reviewOutcome(EnvironmentReview $record, bool $fresh = false): CompressedGovernanceOutcome
|
||||
{
|
||||
$presenter = app(ArtifactTruthPresenter::class);
|
||||
$truth = $fresh
|
||||
@ -401,7 +401,7 @@ private function reviewOutcome(TenantReview $record, bool $fresh = false): Compr
|
||||
);
|
||||
}
|
||||
|
||||
private function findingOutcomeSummary(TenantReview $record): ?string
|
||||
private function findingOutcomeSummary(EnvironmentReview $record): ?string
|
||||
{
|
||||
$summary = is_array($record->summary) ? $record->summary : [];
|
||||
$outcomeCounts = $summary['finding_outcomes'] ?? [];
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
use App\Models\User;
|
||||
use App\Models\WorkspaceMembership;
|
||||
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
|
||||
use App\Services\Auth\TenantMembershipManager;
|
||||
use App\Services\Auth\ManagedEnvironmentMembershipManager;
|
||||
use App\Support\Workspaces\WorkspaceContext;
|
||||
use Filament\Forms;
|
||||
use Filament\Pages\Tenancy\RegisterTenant as BaseRegisterTenant;
|
||||
@ -105,7 +105,7 @@ protected function handleRegistration(array $data): Model
|
||||
->allowedManagedEnvironmentIdsForWorkspace($user, $workspaceId);
|
||||
|
||||
if (is_array($explicitScopes)) {
|
||||
app(TenantMembershipManager::class)->grantScope(
|
||||
app(ManagedEnvironmentMembershipManager::class)->grantScope(
|
||||
tenant: $tenant,
|
||||
actor: $user,
|
||||
member: $user,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
||||
|
||||
namespace App\Filament\Pages\Workspaces;
|
||||
|
||||
use App\Filament\Pages\ChooseTenant;
|
||||
use App\Filament\Pages\ChooseEnvironment;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
@ -17,7 +17,7 @@
|
||||
use Filament\Pages\Page;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ManagedTenantsLanding extends Page
|
||||
class ManagedEnvironmentsLanding extends Page
|
||||
{
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
@ -25,7 +25,7 @@ class ManagedTenantsLanding extends Page
|
||||
|
||||
protected static string $layout = 'filament-panels::components.layout.simple';
|
||||
|
||||
protected string $view = 'filament.pages.workspaces.managed-tenants-landing';
|
||||
protected string $view = 'filament.pages.workspaces.managed-environments-landing';
|
||||
|
||||
public Workspace $workspace;
|
||||
|
||||
@ -90,9 +90,9 @@ public function getTenants(): Collection
|
||||
->values();
|
||||
}
|
||||
|
||||
public function goToChooseTenant(): void
|
||||
public function goToChooseEnvironment(): void
|
||||
{
|
||||
$this->redirect(route('admin.workspace.managed-tenants.index', ['workspace' => $this->workspace]));
|
||||
$this->redirect(route('admin.workspace.managed-environments.index', ['workspace' => $this->workspace]));
|
||||
}
|
||||
|
||||
public function openTenant(int $tenantId): void
|
||||
@ -64,8 +64,8 @@ private function captureAction(): Action
|
||||
: 'Capture baseline';
|
||||
|
||||
$modalDescription = $captureMode === BaselineCaptureMode::FullContent
|
||||
? 'Select the source tenant. This will capture content evidence on demand (redacted) and may take longer depending on scope.'
|
||||
: 'Select the source tenant whose current inventory will be captured as the baseline snapshot.';
|
||||
? 'Select the source environment. This will capture content evidence on demand (redacted) and may take longer depending on scope.'
|
||||
: 'Select the source environment whose current inventory will be captured as the baseline snapshot.';
|
||||
|
||||
$action = Action::make('capture')
|
||||
->label($label)
|
||||
@ -76,8 +76,8 @@ private function captureAction(): Action
|
||||
->modalHeading($label)
|
||||
->modalDescription($modalDescription)
|
||||
->form([
|
||||
Select::make('source_tenant_id')
|
||||
->label('Source ManagedEnvironment')
|
||||
Select::make('source_environment_id')
|
||||
->label('Source managed environment')
|
||||
->options(fn (): array => $this->getWorkspaceTenantOptions())
|
||||
->required()
|
||||
->searchable(),
|
||||
@ -91,11 +91,11 @@ private function captureAction(): Action
|
||||
|
||||
/** @var BaselineProfile $profile */
|
||||
$profile = $this->getRecord();
|
||||
$sourceTenant = ManagedEnvironment::query()->find((int) $data['source_tenant_id']);
|
||||
$sourceTenant = ManagedEnvironment::query()->find((int) $data['source_environment_id']);
|
||||
|
||||
if (! $sourceTenant instanceof ManagedEnvironment) {
|
||||
Notification::make()
|
||||
->title('Source tenant not found')
|
||||
->title('Source environment not found')
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
@ -175,8 +175,8 @@ private function compareNowAction(): Action
|
||||
: 'Compare now';
|
||||
|
||||
$modalDescription = $captureMode === BaselineCaptureMode::FullContent
|
||||
? 'Select the target tenant. This will refresh content evidence on demand (redacted) before comparing.'
|
||||
: 'Select the target tenant to compare its current inventory against the effective current baseline snapshot.';
|
||||
? 'Select the target environment. This will refresh content evidence on demand (redacted) before comparing.'
|
||||
: 'Select the target environment to compare its current inventory against the effective current baseline snapshot.';
|
||||
|
||||
return Action::make('compareNow')
|
||||
->label($label)
|
||||
@ -187,8 +187,8 @@ private function compareNowAction(): Action
|
||||
->modalHeading($label)
|
||||
->modalDescription($modalDescription)
|
||||
->form([
|
||||
Select::make('target_tenant_id')
|
||||
->label('Target ManagedEnvironment')
|
||||
Select::make('target_environment_id')
|
||||
->label('Target managed environment')
|
||||
->options(fn (): array => $this->getEligibleCompareTenantOptions())
|
||||
->required()
|
||||
->searchable(),
|
||||
@ -204,11 +204,11 @@ private function compareNowAction(): Action
|
||||
/** @var BaselineProfile $profile */
|
||||
$profile = $this->getRecord();
|
||||
|
||||
$targetTenant = ManagedEnvironment::query()->find((int) $data['target_tenant_id']);
|
||||
$targetTenant = ManagedEnvironment::query()->find((int) $data['target_environment_id']);
|
||||
|
||||
if (! $targetTenant instanceof ManagedEnvironment || (int) $targetTenant->workspace_id !== (int) $profile->workspace_id) {
|
||||
Notification::make()
|
||||
->title('Target tenant not found')
|
||||
->title('Target environment not found')
|
||||
->danger()
|
||||
->send();
|
||||
|
||||
|
||||
@ -8,16 +8,16 @@
|
||||
use App\Filament\Concerns\ResolvesPanelTenantContext;
|
||||
use App\Filament\Concerns\WorkspaceScopedTenantRoutes;
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
use App\Filament\Resources\TenantReviewResource\Pages;
|
||||
use App\Filament\Resources\EnvironmentReviewResource\Pages;
|
||||
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\TenantReviewSection;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\EnvironmentReviewSection;
|
||||
use App\Models\User;
|
||||
use App\Services\ReviewPackService;
|
||||
use App\Services\TenantReviews\TenantReviewService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewService;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Auth\UiTooltips as AuthUiTooltips;
|
||||
use App\Support\Badges\BadgeCatalog;
|
||||
@ -31,8 +31,8 @@
|
||||
use App\Support\ReasonTranslation\ReasonPresenter;
|
||||
use App\Support\ReviewPackStatus;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceInspectAffordance;
|
||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceProfile;
|
||||
@ -62,7 +62,7 @@
|
||||
use Illuminate\Support\Str;
|
||||
use UnitEnum;
|
||||
|
||||
class TenantReviewResource extends Resource
|
||||
class EnvironmentReviewResource extends Resource
|
||||
{
|
||||
use InteractsWithTenantOwnedRecords;
|
||||
use ResolvesPanelTenantContext;
|
||||
@ -70,7 +70,7 @@ class TenantReviewResource extends Resource
|
||||
|
||||
protected static bool $isDiscovered = false;
|
||||
|
||||
protected static ?string $model = TenantReview::class;
|
||||
protected static ?string $model = EnvironmentReview::class;
|
||||
|
||||
protected static ?string $slug = 'reviews';
|
||||
|
||||
@ -95,7 +95,7 @@ public static function shouldRegisterNavigation(): bool
|
||||
public static function getSlug(?Panel $panel = null): string
|
||||
{
|
||||
$slug = $panel?->getId() === 'admin'
|
||||
? 'tenant-reviews'
|
||||
? 'environment-reviews'
|
||||
: parent::getSlug($panel);
|
||||
|
||||
return static::workspaceScopedSlug($slug, $panel);
|
||||
@ -134,7 +134,7 @@ public static function canViewAny(): bool
|
||||
return false;
|
||||
}
|
||||
|
||||
return $user->can(Capabilities::TENANT_REVIEW_VIEW, $tenant);
|
||||
return $user->can(Capabilities::ENVIRONMENT_REVIEW_VIEW, $tenant);
|
||||
}
|
||||
|
||||
public static function canView(Model $record): bool
|
||||
@ -142,7 +142,7 @@ public static function canView(Model $record): bool
|
||||
$tenant = static::resolveTenantContextForCurrentPanel();
|
||||
$user = auth()->user();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User || ! $record instanceof TenantReview) {
|
||||
if (! $tenant instanceof ManagedEnvironment || ! $user instanceof User || ! $record instanceof EnvironmentReview) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -194,7 +194,7 @@ public static function infolist(Schema $schema): Schema
|
||||
ViewEntry::make('artifact_truth')
|
||||
->hiddenLabel()
|
||||
->view('filament.infolists.entries.governance-artifact-truth')
|
||||
->state(fn (TenantReview $record): array => static::truthState($record))
|
||||
->state(fn (EnvironmentReview $record): array => static::truthState($record))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull(),
|
||||
@ -202,30 +202,30 @@ public static function infolist(Schema $schema): Schema
|
||||
->schema([
|
||||
TextEntry::make('status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus)),
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus)),
|
||||
TextEntry::make('completeness_state')
|
||||
->label(__('localization.review.completeness'))
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewCompleteness))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewCompleteness))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewCompleteness))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewCompleteness)),
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)),
|
||||
TextEntry::make('tenant.name')->label(__('localization.review.tenant')),
|
||||
TextEntry::make('generated_at')->dateTime()->placeholder('—'),
|
||||
TextEntry::make('published_at')->dateTime()->placeholder('—'),
|
||||
TextEntry::make('evidenceSnapshot.id')
|
||||
->label(__('localization.review.evidence_snapshot'))
|
||||
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
|
||||
->url(fn (TenantReview $record): ?string => $record->evidenceSnapshot
|
||||
->url(fn (EnvironmentReview $record): ?string => $record->evidenceSnapshot
|
||||
? EvidenceSnapshotResource::getUrl('view', ['record' => $record->evidenceSnapshot], tenant: $record->tenant)
|
||||
: null),
|
||||
TextEntry::make('currentExportReviewPack.id')
|
||||
->label(__('localization.review.current_export'))
|
||||
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
|
||||
->url(fn (TenantReview $record): ?string => $record->currentExportReviewPack
|
||||
->url(fn (EnvironmentReview $record): ?string => $record->currentExportReviewPack
|
||||
? ReviewPackResource::getUrl('view', ['record' => $record->currentExportReviewPack], tenant: $record->tenant)
|
||||
: null),
|
||||
TextEntry::make('fingerprint')
|
||||
@ -242,32 +242,32 @@ public static function infolist(Schema $schema): Schema
|
||||
->schema([
|
||||
ViewEntry::make('review_summary')
|
||||
->hiddenLabel()
|
||||
->view('filament.infolists.entries.tenant-review-summary')
|
||||
->state(fn (TenantReview $record): array => static::summaryPresentation($record))
|
||||
->view('filament.infolists.entries.environment-review-summary')
|
||||
->state(fn (EnvironmentReview $record): array => static::summaryPresentation($record))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->columnSpanFull(),
|
||||
Section::make(__('localization.review.sections'))
|
||||
->schema([
|
||||
RepeatableEntry::make('sections')
|
||||
->state(fn (TenantReview $record): array => static::visibleSections($record))
|
||||
->state(fn (EnvironmentReview $record): array => static::visibleSections($record))
|
||||
->hiddenLabel()
|
||||
->schema([
|
||||
TextEntry::make('title'),
|
||||
TextEntry::make('completeness_state')
|
||||
->label(__('localization.review.completeness'))
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewCompleteness))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewCompleteness))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewCompleteness))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewCompleteness)),
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewCompleteness))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewCompleteness)),
|
||||
TextEntry::make('measured_at')->dateTime()->placeholder('—'),
|
||||
Section::make(__('localization.review.details'))
|
||||
->schema([
|
||||
ViewEntry::make('section_payload')
|
||||
->hiddenLabel()
|
||||
->view('filament.infolists.entries.tenant-review-section')
|
||||
->state(fn (TenantReviewSection $record): array => static::sectionPresentation($record))
|
||||
->view('filament.infolists.entries.environment-review-section')
|
||||
->state(fn (EnvironmentReviewSection $record): array => static::sectionPresentation($record))
|
||||
->columnSpanFull(),
|
||||
])
|
||||
->collapsible()
|
||||
@ -281,12 +281,12 @@ public static function infolist(Schema $schema): Schema
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, TenantReviewSection>
|
||||
* @return array<int, EnvironmentReviewSection>
|
||||
*/
|
||||
private static function visibleSections(TenantReview $record): array
|
||||
private static function visibleSections(EnvironmentReview $record): array
|
||||
{
|
||||
return $record->sections
|
||||
->reject(fn (TenantReviewSection $section): bool => static::isCustomerWorkspaceMode() && $section->isControlInterpretation())
|
||||
->reject(fn (EnvironmentReviewSection $section): bool => static::isCustomerWorkspaceMode() && $section->isControlInterpretation())
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
@ -297,43 +297,43 @@ public static function table(Table $table): Table
|
||||
Actions\Action::make('export_executive_pack')
|
||||
->label(__('localization.review.export_executive_pack'))
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->visible(fn (TenantReview $record): bool => in_array($record->status, [
|
||||
TenantReviewStatus::Ready->value,
|
||||
TenantReviewStatus::Published->value,
|
||||
->visible(fn (EnvironmentReview $record): bool => in_array($record->status, [
|
||||
EnvironmentReviewStatus::Ready->value,
|
||||
EnvironmentReviewStatus::Published->value,
|
||||
], true))
|
||||
->disabled(fn (TenantReview $record): bool => static::reviewPackGenerationBlocked($record->tenant))
|
||||
->action(fn (TenantReview $record): mixed => static::executeExport($record)),
|
||||
fn (TenantReview $record): TenantReview => $record,
|
||||
->disabled(fn (EnvironmentReview $record): bool => static::reviewPackGenerationBlocked($record->tenant))
|
||||
->action(fn (EnvironmentReview $record): mixed => static::executeExport($record)),
|
||||
fn (EnvironmentReview $record): EnvironmentReview => $record,
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->preserveVisibility()
|
||||
->preserveDisabled()
|
||||
->apply();
|
||||
|
||||
$exportExecutivePackAction->tooltip(fn (TenantReview $record): ?string => static::reviewPackGenerationActionTooltip($record->tenant));
|
||||
$exportExecutivePackAction->tooltip(fn (EnvironmentReview $record): ?string => static::reviewPackGenerationActionTooltip($record->tenant));
|
||||
|
||||
return $table
|
||||
->defaultSort('generated_at', 'desc')
|
||||
->persistFiltersInSession()
|
||||
->persistSearchInSession()
|
||||
->persistSortInSession()
|
||||
->recordUrl(fn (TenantReview $record): string => static::tenantScopedUrl('view', ['record' => $record], $record->tenant))
|
||||
->recordUrl(fn (EnvironmentReview $record): string => static::tenantScopedUrl('view', ['record' => $record], $record->tenant))
|
||||
->columns([
|
||||
Tables\Columns\TextColumn::make('status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus))
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus))
|
||||
->sortable(),
|
||||
Tables\Columns\TextColumn::make('outcome')
|
||||
->label(__('localization.review.outcome'))
|
||||
->badge()
|
||||
->getStateUsing(fn (TenantReview $record): string => static::compressedOutcome($record)->primaryLabel)
|
||||
->color(fn (TenantReview $record): string => static::compressedOutcome($record)->primaryBadge->color)
|
||||
->icon(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryBadge->icon)
|
||||
->iconColor(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryBadge->iconColor)
|
||||
->description(fn (TenantReview $record): ?string => static::compressedOutcome($record)->primaryReason)
|
||||
->getStateUsing(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->primaryLabel)
|
||||
->color(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->primaryBadge->color)
|
||||
->icon(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryBadge->icon)
|
||||
->iconColor(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryBadge->iconColor)
|
||||
->description(fn (EnvironmentReview $record): ?string => static::compressedOutcome($record)->primaryReason)
|
||||
->wrap(),
|
||||
Tables\Columns\TextColumn::make('generated_at')->dateTime()->placeholder('—')->sortable(),
|
||||
Tables\Columns\TextColumn::make('published_at')->dateTime()->placeholder('—')->sortable(),
|
||||
@ -342,7 +342,7 @@ public static function table(Table $table): Table
|
||||
->boolean(),
|
||||
Tables\Columns\TextColumn::make('next_step')
|
||||
->label(__('localization.review.next_step'))
|
||||
->getStateUsing(fn (TenantReview $record): string => static::compressedOutcome($record)->nextActionText)
|
||||
->getStateUsing(fn (EnvironmentReview $record): string => static::compressedOutcome($record)->nextActionText)
|
||||
->wrap(),
|
||||
Tables\Columns\TextColumn::make('fingerprint')
|
||||
->toggleable(isToggledHiddenByDefault: true)
|
||||
@ -350,18 +350,18 @@ public static function table(Table $table): Table
|
||||
])
|
||||
->filters([
|
||||
Tables\Filters\SelectFilter::make('status')
|
||||
->options(collect(TenantReviewStatus::cases())
|
||||
->mapWithKeys(fn (TenantReviewStatus $status): array => [$status->value => Str::headline($status->value)])
|
||||
->options(collect(EnvironmentReviewStatus::cases())
|
||||
->mapWithKeys(fn (EnvironmentReviewStatus $status): array => [$status->value => Str::headline($status->value)])
|
||||
->all()),
|
||||
Tables\Filters\SelectFilter::make('completeness_state')
|
||||
->options(BadgeCatalog::options(BadgeDomain::TenantReviewCompleteness, TenantReviewCompletenessState::values())),
|
||||
->options(BadgeCatalog::options(BadgeDomain::EnvironmentReviewCompleteness, EnvironmentReviewCompletenessState::values())),
|
||||
\App\Support\Filament\FilterPresets::dateRange('review_date', __('localization.review.review_date'), 'generated_at'),
|
||||
])
|
||||
->actions([
|
||||
$exportExecutivePackAction,
|
||||
])
|
||||
->bulkActions([])
|
||||
->emptyStateHeading(__('localization.review.no_tenant_reviews_yet'))
|
||||
->emptyStateHeading(__('localization.review.no_environment_reviews_yet'))
|
||||
->emptyStateDescription(__('localization.review.create_first_review_description'))
|
||||
->emptyStateActions([
|
||||
static::makeCreateReviewAction(
|
||||
@ -375,8 +375,8 @@ public static function table(Table $table): Table
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListTenantReviews::route('/'),
|
||||
'view' => Pages\ViewTenantReview::route('/{record}'),
|
||||
'index' => Pages\ListEnvironmentReviews::route('/'),
|
||||
'view' => Pages\ViewEnvironmentReview::route('/{record}'),
|
||||
];
|
||||
}
|
||||
|
||||
@ -406,7 +406,7 @@ public static function makeCreateReviewAction(
|
||||
])
|
||||
->action(fn (array $data): mixed => static::executeCreateReview($data)),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->apply();
|
||||
}
|
||||
|
||||
@ -428,7 +428,7 @@ public static function executeCreateReview(array $data): void
|
||||
abort(404);
|
||||
}
|
||||
|
||||
if (! $user->can(Capabilities::TENANT_REVIEW_MANAGE, $tenant)) {
|
||||
if (! $user->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $tenant)) {
|
||||
abort(403);
|
||||
}
|
||||
|
||||
@ -447,7 +447,7 @@ public static function executeCreateReview(array $data): void
|
||||
}
|
||||
|
||||
try {
|
||||
$review = app(TenantReviewService::class)->create($tenant, $snapshot, $user);
|
||||
$review = app(EnvironmentReviewService::class)->create($tenant, $snapshot, $user);
|
||||
} catch (\Throwable $throwable) {
|
||||
Notification::make()->danger()->title(__('localization.review.unable_create_review'))->body($throwable->getMessage())->send();
|
||||
|
||||
@ -471,7 +471,7 @@ public static function executeCreateReview(array $data): void
|
||||
return;
|
||||
}
|
||||
|
||||
$toast = OperationUxPresenter::queuedToast(OperationRunType::TenantReviewCompose->value)
|
||||
$toast = OperationUxPresenter::queuedToast(OperationRunType::EnvironmentReviewCompose->value)
|
||||
->body(__('localization.review.review_composing_background'));
|
||||
|
||||
if ($review->operation_run_id) {
|
||||
@ -535,7 +535,7 @@ public static function reviewPackGenerationActionTooltip(?ManagedEnvironment $te
|
||||
$tenant ??= static::panelTenantContext();
|
||||
$user = auth()->user();
|
||||
|
||||
if ($tenant instanceof ManagedEnvironment && $user instanceof User && ! $user->can(Capabilities::TENANT_REVIEW_MANAGE, $tenant)) {
|
||||
if ($tenant instanceof ManagedEnvironment && $user instanceof User && ! $user->can(Capabilities::ENVIRONMENT_REVIEW_MANAGE, $tenant)) {
|
||||
return AuthUiTooltips::insufficientPermission();
|
||||
}
|
||||
|
||||
@ -543,7 +543,7 @@ public static function reviewPackGenerationActionTooltip(?ManagedEnvironment $te
|
||||
?? static::reviewPackGenerationWarningReason($tenant);
|
||||
}
|
||||
|
||||
public static function executeExport(TenantReview $review): void
|
||||
public static function executeExport(EnvironmentReview $review): void
|
||||
{
|
||||
$review->loadMissing(['tenant', 'currentExportReviewPack']);
|
||||
$user = auth()->user();
|
||||
@ -654,13 +654,13 @@ private static function evidenceSnapshotOptions(): array
|
||||
|
||||
private static function reviewCompletenessCountLabel(string $state): string
|
||||
{
|
||||
return BadgeCatalog::spec(BadgeDomain::TenantReviewCompleteness, $state)->label;
|
||||
return BadgeCatalog::spec(BadgeDomain::EnvironmentReviewCompleteness, $state)->label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function summaryPresentation(TenantReview $record): array
|
||||
private static function summaryPresentation(EnvironmentReview $record): array
|
||||
{
|
||||
$summary = is_array($record->summary) ? $record->summary : [];
|
||||
$truthEnvelope = static::truthEnvelope($record);
|
||||
@ -706,7 +706,7 @@ private static function summaryPresentation(TenantReview $record): array
|
||||
* @param array<string, mixed> $packagePresentation
|
||||
* @return array<int, array{label:string,value:string}>
|
||||
*/
|
||||
private static function customerWorkspaceMetrics(TenantReview $record, array $summary, array $packagePresentation): array
|
||||
private static function customerWorkspaceMetrics(EnvironmentReview $record, array $summary, array $packagePresentation): array
|
||||
{
|
||||
$acceptedRisk = is_array($summary['risk_acceptance'] ?? null) ? $summary['risk_acceptance'] : [];
|
||||
|
||||
@ -719,9 +719,9 @@ private static function customerWorkspaceMetrics(TenantReview $record, array $su
|
||||
];
|
||||
}
|
||||
|
||||
private static function customerReviewStatusLabel(TenantReview $record): string
|
||||
private static function customerReviewStatusLabel(EnvironmentReview $record): string
|
||||
{
|
||||
if ($record->isPublished() && (string) $record->completeness_state === TenantReviewCompletenessState::Complete->value) {
|
||||
if ($record->isPublished() && (string) $record->completeness_state === EnvironmentReviewCompletenessState::Complete->value) {
|
||||
return __('localization.review.review_completed');
|
||||
}
|
||||
|
||||
@ -732,7 +732,7 @@ private static function customerReviewStatusLabel(TenantReview $record): string
|
||||
return Str::headline((string) $record->status);
|
||||
}
|
||||
|
||||
private static function customerEvidenceStatusLabel(TenantReview $record): string
|
||||
private static function customerEvidenceStatusLabel(EnvironmentReview $record): string
|
||||
{
|
||||
$snapshot = $record->evidenceSnapshot;
|
||||
$tenant = $record->tenant;
|
||||
@ -775,7 +775,7 @@ private static function customerAcceptedRiskStatusLabel(array $acceptedRisk): st
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function governancePackagePresentation(TenantReview $record): array
|
||||
private static function governancePackagePresentation(EnvironmentReview $record): array
|
||||
{
|
||||
$summary = is_array($record->summary) ? $record->summary : [];
|
||||
$package = is_array($summary['governance_package'] ?? null) ? $summary['governance_package'] : [];
|
||||
@ -793,7 +793,7 @@ private static function governancePackagePresentation(TenantReview $record): arr
|
||||
/**
|
||||
* @return array{state:string,label:string,description:string}
|
||||
*/
|
||||
private static function governancePackageAvailability(TenantReview $record): array
|
||||
private static function governancePackageAvailability(EnvironmentReview $record): array
|
||||
{
|
||||
$pack = $record->currentExportReviewPack;
|
||||
$tenant = $record->tenant;
|
||||
@ -801,8 +801,8 @@ private static function governancePackageAvailability(TenantReview $record): arr
|
||||
$controlInterpretation = $record->controlInterpretation();
|
||||
$limitations = is_array($controlInterpretation['limitations'] ?? null) ? $controlInterpretation['limitations'] : [];
|
||||
$isPartialReview = in_array((string) $record->completeness_state, [
|
||||
TenantReviewCompletenessState::Partial->value,
|
||||
TenantReviewCompletenessState::Stale->value,
|
||||
EnvironmentReviewCompletenessState::Partial->value,
|
||||
EnvironmentReviewCompletenessState::Stale->value,
|
||||
], true) || $limitations !== [];
|
||||
|
||||
if (! $pack instanceof ReviewPack) {
|
||||
@ -855,7 +855,7 @@ private static function governancePackageAvailability(TenantReview $record): arr
|
||||
/**
|
||||
* @return array<int, array{title:string,label:string,url:?string,description:string}>
|
||||
*/
|
||||
private static function summaryContextLinks(TenantReview $record, bool $customerWorkspaceMode = false): array
|
||||
private static function summaryContextLinks(EnvironmentReview $record, bool $customerWorkspaceMode = false): array
|
||||
{
|
||||
$links = [];
|
||||
|
||||
@ -913,15 +913,15 @@ private static function summaryContextLinks(TenantReview $record, bool $customer
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function sectionPresentation(TenantReviewSection $section): array
|
||||
private static function sectionPresentation(EnvironmentReviewSection $section): array
|
||||
{
|
||||
$summary = is_array($section->summary_payload) ? $section->summary_payload : [];
|
||||
$render = is_array($section->render_payload) ? $section->render_payload : [];
|
||||
$review = $section->tenantReview;
|
||||
$review = $section->environmentReview;
|
||||
$tenant = $section->tenant;
|
||||
$links = [];
|
||||
|
||||
if ($section->isControlInterpretation() && $review instanceof TenantReview && $tenant instanceof ManagedEnvironment && $review->evidenceSnapshot instanceof EvidenceSnapshot) {
|
||||
if ($section->isControlInterpretation() && $review instanceof EnvironmentReview && $tenant instanceof ManagedEnvironment && $review->evidenceSnapshot instanceof EvidenceSnapshot) {
|
||||
$user = auth()->user();
|
||||
|
||||
if ($user instanceof User && $user->can(Capabilities::EVIDENCE_VIEW, $tenant)) {
|
||||
@ -960,34 +960,34 @@ private static function sectionPresentation(TenantReviewSection $section): array
|
||||
];
|
||||
}
|
||||
|
||||
private static function truthEnvelope(TenantReview $record, bool $fresh = false): ArtifactTruthEnvelope
|
||||
private static function truthEnvelope(EnvironmentReview $record, bool $fresh = false): ArtifactTruthEnvelope
|
||||
{
|
||||
$presenter = app(ArtifactTruthPresenter::class);
|
||||
|
||||
return $fresh
|
||||
? $presenter->forTenantReviewFresh($record)
|
||||
: $presenter->forTenantReview($record);
|
||||
? $presenter->forEnvironmentReviewFresh($record)
|
||||
: $presenter->forEnvironmentReview($record);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function truthState(TenantReview $record, bool $fresh = false): array
|
||||
private static function truthState(EnvironmentReview $record, bool $fresh = false): array
|
||||
{
|
||||
$presenter = app(ArtifactTruthPresenter::class);
|
||||
|
||||
return $presenter->surfaceStateFor($record, SurfaceCompressionContext::tenantReview(), $fresh)
|
||||
return $presenter->surfaceStateFor($record, SurfaceCompressionContext::environmentReview(), $fresh)
|
||||
?? static::truthEnvelope($record, $fresh)->toArray(static::compressedOutcome($record, $fresh));
|
||||
}
|
||||
|
||||
private static function compressedOutcome(TenantReview $record, bool $fresh = false): CompressedGovernanceOutcome
|
||||
private static function compressedOutcome(EnvironmentReview $record, bool $fresh = false): CompressedGovernanceOutcome
|
||||
{
|
||||
$presenter = app(ArtifactTruthPresenter::class);
|
||||
|
||||
return $presenter->compressedOutcomeFor($record, SurfaceCompressionContext::tenantReview(), $fresh)
|
||||
return $presenter->compressedOutcomeFor($record, SurfaceCompressionContext::environmentReview(), $fresh)
|
||||
?? $presenter->compressedOutcomeFromEnvelope(
|
||||
static::truthEnvelope($record, $fresh),
|
||||
SurfaceCompressionContext::tenantReview(),
|
||||
SurfaceCompressionContext::environmentReview(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1013,7 +1013,7 @@ private static function isCustomerWorkspaceMode(): bool
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private static function customerWorkspaceEvidenceQuery(TenantReview $record): array
|
||||
private static function customerWorkspaceEvidenceQuery(EnvironmentReview $record): array
|
||||
{
|
||||
return array_filter([
|
||||
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
||||
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\EnvironmentReviewResource\Pages;
|
||||
|
||||
use App\Filament\Resources\EnvironmentReviewResource;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListEnvironmentReviews extends ListRecords
|
||||
{
|
||||
protected static string $resource = EnvironmentReviewResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EnvironmentReviewResource::makeCreateReviewAction(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -2,23 +2,23 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\TenantReviewResource\Pages;
|
||||
namespace App\Filament\Resources\EnvironmentReviewResource\Pages;
|
||||
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
use App\Filament\Resources\TenantReviewResource;
|
||||
use App\Filament\Resources\EnvironmentReviewResource;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Services\ReviewPackService;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\TenantReviews\TenantReviewLifecycleService;
|
||||
use App\Services\TenantReviews\TenantReviewService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewLifecycleService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewService;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\ReviewPackStatus;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Ui\GovernanceActions\GovernanceActionCatalog;
|
||||
use Filament\Actions;
|
||||
use Filament\Forms\Components\Textarea;
|
||||
@ -26,9 +26,9 @@
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ViewTenantReview extends ViewRecord
|
||||
class ViewEnvironmentReview extends ViewRecord
|
||||
{
|
||||
protected static string $resource = TenantReviewResource::class;
|
||||
protected static string $resource = EnvironmentReviewResource::class;
|
||||
|
||||
public function mount(int|string $record): void
|
||||
{
|
||||
@ -39,16 +39,16 @@ public function mount(int|string $record): void
|
||||
|
||||
protected function resolveRecord(int|string $key): Model
|
||||
{
|
||||
return TenantReviewResource::resolveScopedRecordOrFail($key);
|
||||
return EnvironmentReviewResource::resolveScopedRecordOrFail($key);
|
||||
}
|
||||
|
||||
protected function authorizeAccess(): void
|
||||
{
|
||||
$tenant = TenantReviewResource::panelTenantContext();
|
||||
$tenant = EnvironmentReviewResource::panelTenantContext();
|
||||
$record = $this->getRecord();
|
||||
$user = auth()->user();
|
||||
|
||||
if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $record instanceof TenantReview) {
|
||||
if (! $user instanceof User || ! $tenant instanceof ManagedEnvironment || ! $record instanceof EnvironmentReview) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
@ -108,11 +108,11 @@ private function primaryLifecycleActionName(): ?string
|
||||
return null;
|
||||
}
|
||||
|
||||
if ((string) $this->record->status === TenantReviewStatus::Published->value) {
|
||||
if ((string) $this->record->status === EnvironmentReviewStatus::Published->value) {
|
||||
return 'export_executive_pack';
|
||||
}
|
||||
|
||||
if ((string) $this->record->status === TenantReviewStatus::Ready->value) {
|
||||
if ((string) $this->record->status === EnvironmentReviewStatus::Ready->value) {
|
||||
return 'publish_review';
|
||||
}
|
||||
|
||||
@ -157,8 +157,8 @@ private function secondaryLifecycleActionNames(): array
|
||||
}
|
||||
|
||||
if (in_array((string) $this->record->status, [
|
||||
TenantReviewStatus::Ready->value,
|
||||
TenantReviewStatus::Published->value,
|
||||
EnvironmentReviewStatus::Ready->value,
|
||||
EnvironmentReviewStatus::Published->value,
|
||||
], true)) {
|
||||
$names[] = 'export_executive_pack';
|
||||
}
|
||||
@ -194,7 +194,7 @@ private function refreshReviewAction(): Actions\Action
|
||||
}
|
||||
|
||||
try {
|
||||
app(TenantReviewService::class)->refresh($this->record, $user);
|
||||
app(EnvironmentReviewService::class)->refresh($this->record, $user);
|
||||
} catch (\Throwable $throwable) {
|
||||
Notification::make()->danger()->title('Unable to refresh review')->body($throwable->getMessage())->send();
|
||||
|
||||
@ -204,7 +204,7 @@ private function refreshReviewAction(): Actions\Action
|
||||
Notification::make()->success()->title($rule->successTitle)->send();
|
||||
}),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->apply();
|
||||
}
|
||||
|
||||
@ -236,7 +236,7 @@ private function publishReviewAction(): Actions\Action
|
||||
}
|
||||
|
||||
try {
|
||||
app(TenantReviewLifecycleService::class)->publish(
|
||||
app(EnvironmentReviewLifecycleService::class)->publish(
|
||||
$this->record,
|
||||
$user,
|
||||
(string) ($data['publish_reason'] ?? ''),
|
||||
@ -251,7 +251,7 @@ private function publishReviewAction(): Actions\Action
|
||||
Notification::make()->success()->title($rule->successTitle)->send();
|
||||
}),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->preserveVisibility()
|
||||
->apply();
|
||||
}
|
||||
@ -264,18 +264,18 @@ private function exportExecutivePackAction(): Actions\Action
|
||||
->icon('heroicon-o-arrow-down-tray')
|
||||
->color('primary')
|
||||
->hidden(fn (): bool => ! in_array((string) $this->record->status, [
|
||||
TenantReviewStatus::Ready->value,
|
||||
TenantReviewStatus::Published->value,
|
||||
EnvironmentReviewStatus::Ready->value,
|
||||
EnvironmentReviewStatus::Published->value,
|
||||
], true))
|
||||
->disabled(fn (): bool => TenantReviewResource::reviewPackGenerationBlocked($this->record->tenant))
|
||||
->action(fn (): mixed => TenantReviewResource::executeExport($this->record)),
|
||||
->disabled(fn (): bool => EnvironmentReviewResource::reviewPackGenerationBlocked($this->record->tenant))
|
||||
->action(fn (): mixed => EnvironmentReviewResource::executeExport($this->record)),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->preserveVisibility()
|
||||
->preserveDisabled()
|
||||
->apply();
|
||||
|
||||
$action->tooltip(fn (): ?string => TenantReviewResource::reviewPackGenerationActionTooltip($this->record->tenant));
|
||||
$action->tooltip(fn (): ?string => EnvironmentReviewResource::reviewPackGenerationActionTooltip($this->record->tenant));
|
||||
|
||||
return $action;
|
||||
}
|
||||
@ -295,17 +295,17 @@ private function createNextReviewAction(): Actions\Action
|
||||
}
|
||||
|
||||
try {
|
||||
$nextReview = app(TenantReviewLifecycleService::class)->createNextReview($this->record, $user);
|
||||
$nextReview = app(EnvironmentReviewLifecycleService::class)->createNextReview($this->record, $user);
|
||||
} catch (\Throwable $throwable) {
|
||||
Notification::make()->danger()->title('Unable to create next review')->body($throwable->getMessage())->send();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->redirect(TenantReviewResource::tenantScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
|
||||
$this->redirect(EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $nextReview], $nextReview->tenant));
|
||||
}),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->preserveVisibility()
|
||||
->apply();
|
||||
}
|
||||
@ -337,7 +337,7 @@ private function archiveReviewAction(): Actions\Action
|
||||
abort(403);
|
||||
}
|
||||
|
||||
app(TenantReviewLifecycleService::class)->archive(
|
||||
app(EnvironmentReviewLifecycleService::class)->archive(
|
||||
$this->record,
|
||||
$user,
|
||||
(string) ($data['archive_reason'] ?? ''),
|
||||
@ -347,7 +347,7 @@ private function archiveReviewAction(): Actions\Action
|
||||
Notification::make()->success()->title($rule->successTitle)->send();
|
||||
}),
|
||||
)
|
||||
->requireCapability(Capabilities::TENANT_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
->preserveVisibility()
|
||||
->apply();
|
||||
}
|
||||
@ -443,7 +443,7 @@ private function auditCustomerWorkspaceOpen(): void
|
||||
|
||||
app(WorkspaceAuditLogger::class)->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: AuditActionId::TenantReviewOpened,
|
||||
action: AuditActionId::EnvironmentReviewOpened,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_id' => (int) $this->record->getKey(),
|
||||
@ -453,7 +453,7 @@ private function auditCustomerWorkspaceOpen(): void
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $this->record->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $this->record->getKey()),
|
||||
tenant: $tenant,
|
||||
@ -681,7 +681,7 @@ public static function approvalQueueUrl(?ManagedEnvironment $tenant = null): ?st
|
||||
}
|
||||
|
||||
return route('admin.finding-exceptions.open-queue', [
|
||||
'tenant' => (string) $tenant->external_id,
|
||||
'environment' => (string) $tenant->external_id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
use App\Filament\Resources\FindingExceptionResource;
|
||||
use App\Filament\Resources\FindingResource;
|
||||
use App\Filament\Widgets\Tenant\FindingExceptionStatsOverview;
|
||||
use App\Filament\Widgets\ManagedEnvironment\FindingExceptionStatsOverview;
|
||||
use App\Models\FindingException;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
|
||||
use App\Filament\Concerns\ResolvesPanelTenantContext;
|
||||
use App\Filament\Resources\FindingResource;
|
||||
use App\Filament\Widgets\Tenant\BaselineCompareCoverageBanner;
|
||||
use App\Filament\Widgets\Tenant\FindingStatsOverview;
|
||||
use App\Filament\Widgets\ManagedEnvironment\BaselineCompareCoverageBanner;
|
||||
use App\Filament\Widgets\ManagedEnvironment\FindingStatsOverview;
|
||||
use App\Models\Finding;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
namespace App\Filament\Resources;
|
||||
|
||||
use App\Filament\Pages\CrossTenantComparePage;
|
||||
use App\Filament\Resources\TenantResource\Pages;
|
||||
use App\Filament\Resources\TenantResource\RelationManagers;
|
||||
use App\Filament\Pages\CrossEnvironmentComparePage;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource\Pages;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource\RelationManagers;
|
||||
use App\Http\Controllers\RbacDelegatedAuthController;
|
||||
use App\Jobs\BulkTenantSyncJob;
|
||||
use App\Jobs\SyncPoliciesJob;
|
||||
@ -12,8 +12,8 @@
|
||||
use App\Models\EntraRoleDefinition;
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\TenantTriageReview;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentTriageReview;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
@ -26,7 +26,7 @@
|
||||
use App\Services\Intune\RbacOnboardingService;
|
||||
use App\Services\OperationRunService;
|
||||
use App\Services\Operations\BulkSelectionIdentity;
|
||||
use App\Services\PortfolioTriage\TenantTriageReviewService;
|
||||
use App\Services\PortfolioTriage\ManagedEnvironmentTriageReviewService;
|
||||
use App\Services\Providers\AdminConsentUrlFactory;
|
||||
use App\Services\Tenants\TenantActionPolicySurface;
|
||||
use App\Services\Tenants\TenantOperabilityService;
|
||||
@ -49,7 +49,7 @@
|
||||
use App\Support\OpsUx\OpsUxBrowserEvents;
|
||||
use App\Support\Navigation\CanonicalNavigationContext;
|
||||
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
|
||||
use App\Support\PortfolioTriage\TenantTriageReviewStateResolver;
|
||||
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\Navigation\RelatedContextEntry;
|
||||
use App\Support\Navigation\UnavailableRelationState;
|
||||
@ -93,7 +93,7 @@
|
||||
use Illuminate\Support\Str;
|
||||
use UnitEnum;
|
||||
|
||||
class TenantResource extends Resource
|
||||
class ManagedEnvironmentResource extends Resource
|
||||
{
|
||||
// ... [Properties Omitted for Brevity] ...
|
||||
protected static ?string $model = ManagedEnvironment::class;
|
||||
@ -119,7 +119,7 @@ class TenantResource extends Resource
|
||||
|
||||
private const string POSTURE_SNAPSHOT_REQUEST_KEY = 'tenant_resource.posture_snapshot';
|
||||
|
||||
private const string TRIAGE_REVIEW_SNAPSHOT_REQUEST_KEY = 'tenant_resource.triage_review_snapshot';
|
||||
private const string TRIAGE_REVIEW_SNAPSHOT_REQUEST_KEY = 'managed_environment_resource.triage_review_snapshot';
|
||||
|
||||
/**
|
||||
* @var array<string, true>
|
||||
@ -297,7 +297,7 @@ public static function makeTenantViewMarkReviewedAction(): Actions\Action
|
||||
->modalDescription(fn (ManagedEnvironment $record): string => static::triageReviewActionModalDescription(
|
||||
$record,
|
||||
static::tenantViewTriageState(),
|
||||
TenantTriageReview::STATE_REVIEWED,
|
||||
ManagedEnvironmentTriageReview::STATE_REVIEWED,
|
||||
))
|
||||
->visible(fn (ManagedEnvironment $record): bool => static::selectedActionTriageReviewRowForTenant(
|
||||
$record,
|
||||
@ -308,11 +308,11 @@ public static function makeTenantViewMarkReviewedAction(): Actions\Action
|
||||
->before(function (ManagedEnvironment $record): void {
|
||||
static::authorizeTriageReviewAction($record);
|
||||
})
|
||||
->action(function (ManagedEnvironment $record, TenantTriageReviewService $service): void {
|
||||
->action(function (ManagedEnvironment $record, ManagedEnvironmentTriageReviewService $service): void {
|
||||
static::handleTriageReviewMutation(
|
||||
tenant: $record,
|
||||
triageState: static::tenantViewTriageState(),
|
||||
targetManualState: TenantTriageReview::STATE_REVIEWED,
|
||||
targetManualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
|
||||
service: $service,
|
||||
);
|
||||
});
|
||||
@ -329,7 +329,7 @@ public static function makeTenantViewMarkFollowUpNeededAction(): Actions\Action
|
||||
->modalDescription(fn (ManagedEnvironment $record): string => static::triageReviewActionModalDescription(
|
||||
$record,
|
||||
static::tenantViewTriageState(),
|
||||
TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
))
|
||||
->visible(fn (ManagedEnvironment $record): bool => static::selectedActionTriageReviewRowForTenant(
|
||||
$record,
|
||||
@ -340,11 +340,11 @@ public static function makeTenantViewMarkFollowUpNeededAction(): Actions\Action
|
||||
->before(function (ManagedEnvironment $record): void {
|
||||
static::authorizeTriageReviewAction($record);
|
||||
})
|
||||
->action(function (ManagedEnvironment $record, TenantTriageReviewService $service): void {
|
||||
->action(function (ManagedEnvironment $record, ManagedEnvironmentTriageReviewService $service): void {
|
||||
static::handleTriageReviewMutation(
|
||||
tenant: $record,
|
||||
triageState: static::tenantViewTriageState(),
|
||||
targetManualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
targetManualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
service: $service,
|
||||
);
|
||||
});
|
||||
@ -826,10 +826,10 @@ public static function table(Table $table): Table
|
||||
$record,
|
||||
static::currentPortfolioTriageState($livewire),
|
||||
)['derived_state'] ?? null)
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantTriageReviewState))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantTriageReviewState))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantTriageReviewState))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantTriageReviewState))
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentTriageReviewState))
|
||||
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentTriageReviewState))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentTriageReviewState))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentTriageReviewState))
|
||||
->description(fn (ManagedEnvironment $record, mixed $livewire): ?string => static::triageReviewDescriptionForTenant(
|
||||
$record,
|
||||
static::currentPortfolioTriageState($livewire),
|
||||
@ -953,7 +953,7 @@ public static function table(Table $table): Table
|
||||
return '#';
|
||||
}
|
||||
|
||||
$triageState = $livewire instanceof Pages\ListTenants
|
||||
$triageState = $livewire instanceof Pages\ListManagedEnvironments
|
||||
? static::currentPortfolioTriageState($livewire)
|
||||
: [];
|
||||
|
||||
@ -976,12 +976,12 @@ public static function table(Table $table): Table
|
||||
->url(fn (ManagedEnvironment $record): string => static::relatedOnboardingDraftUrl($record) ?? route('admin.onboarding'))
|
||||
->visible(fn (ManagedEnvironment $record): bool => static::relatedOnboardingDraftAction($record, TenantActionSurface::TenantIndexRow) instanceof TenantActionDescriptor
|
||||
&& static::tenantIndexPrimaryAction($record)?->key !== 'related_onboarding'),
|
||||
Actions\Action::make('compareTenants')
|
||||
Actions\Action::make('compareEnvironments')
|
||||
->label('Compare tenants')
|
||||
->icon('heroicon-o-scale')
|
||||
->color('gray')
|
||||
->url(function (ManagedEnvironment $record, mixed $livewire): string {
|
||||
$triageState = $livewire instanceof Pages\ListTenants
|
||||
$triageState = $livewire instanceof Pages\ListManagedEnvironments
|
||||
? static::currentPortfolioTriageState($livewire)
|
||||
: [];
|
||||
|
||||
@ -994,9 +994,9 @@ public static function table(Table $table): Table
|
||||
$triageState = static::portfolioReturnFiltersFromRequest(request()->query());
|
||||
}
|
||||
|
||||
return static::crossTenantCompareOpenUrl($record, $triageState);
|
||||
return static::crossEnvironmentCompareOpenUrl($record, $triageState);
|
||||
})
|
||||
->visible(fn (ManagedEnvironment $record): bool => static::crossTenantCompareActionVisible($record)),
|
||||
->visible(fn (ManagedEnvironment $record): bool => static::crossEnvironmentCompareActionVisible($record)),
|
||||
UiEnforcement::forAction(
|
||||
Actions\Action::make('edit')
|
||||
->label('Edit')
|
||||
@ -1019,7 +1019,7 @@ public static function table(Table $table): Table
|
||||
->modalDescription(fn (ManagedEnvironment $record, mixed $livewire): string => static::triageReviewActionModalDescription(
|
||||
$record,
|
||||
static::currentPortfolioTriageState($livewire),
|
||||
TenantTriageReview::STATE_REVIEWED,
|
||||
ManagedEnvironmentTriageReview::STATE_REVIEWED,
|
||||
))
|
||||
->visible(fn (ManagedEnvironment $record, mixed $livewire): bool => static::selectedTriageReviewRowForTenant(
|
||||
$record,
|
||||
@ -1033,12 +1033,12 @@ public static function table(Table $table): Table
|
||||
->action(function (
|
||||
ManagedEnvironment $record,
|
||||
mixed $livewire,
|
||||
TenantTriageReviewService $service,
|
||||
ManagedEnvironmentTriageReviewService $service,
|
||||
): void {
|
||||
static::handleTriageReviewMutation(
|
||||
tenant: $record,
|
||||
triageState: static::currentPortfolioTriageState($livewire),
|
||||
targetManualState: TenantTriageReview::STATE_REVIEWED,
|
||||
targetManualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
|
||||
service: $service,
|
||||
);
|
||||
}),
|
||||
@ -1051,7 +1051,7 @@ public static function table(Table $table): Table
|
||||
->modalDescription(fn (ManagedEnvironment $record, mixed $livewire): string => static::triageReviewActionModalDescription(
|
||||
$record,
|
||||
static::currentPortfolioTriageState($livewire),
|
||||
TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
))
|
||||
->visible(fn (ManagedEnvironment $record, mixed $livewire): bool => static::selectedTriageReviewRowForTenant(
|
||||
$record,
|
||||
@ -1065,12 +1065,12 @@ public static function table(Table $table): Table
|
||||
->action(function (
|
||||
ManagedEnvironment $record,
|
||||
mixed $livewire,
|
||||
TenantTriageReviewService $service,
|
||||
ManagedEnvironmentTriageReviewService $service,
|
||||
): void {
|
||||
static::handleTriageReviewMutation(
|
||||
tenant: $record,
|
||||
triageState: static::currentPortfolioTriageState($livewire),
|
||||
targetManualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
targetManualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
service: $service,
|
||||
);
|
||||
}),
|
||||
@ -1149,13 +1149,13 @@ public static function table(Table $table): Table
|
||||
->visible(fn (): bool => auth()->user() instanceof User)
|
||||
->authorize(fn (): bool => auth()->user() instanceof User)
|
||||
->extraAttributes(fn (mixed $livewire): array => [
|
||||
'x-bind:aria-disabled' => static::crossTenantCompareBulkClientDisabledExpression($livewire).' ? true : null',
|
||||
'x-bind:disabled' => static::crossTenantCompareBulkClientDisabledExpression($livewire),
|
||||
'x-bind:title' => static::crossTenantCompareBulkClientTooltipExpression($livewire),
|
||||
'x-bind:class' => "{ 'fi-disabled': ".static::crossTenantCompareBulkClientDisabledExpression($livewire).' }',
|
||||
'x-bind:aria-disabled' => static::crossEnvironmentCompareBulkClientDisabledExpression($livewire).' ? true : null',
|
||||
'x-bind:disabled' => static::crossEnvironmentCompareBulkClientDisabledExpression($livewire),
|
||||
'x-bind:title' => static::crossEnvironmentCompareBulkClientTooltipExpression($livewire),
|
||||
'x-bind:class' => "{ 'fi-disabled': ".static::crossEnvironmentCompareBulkClientDisabledExpression($livewire).' }',
|
||||
])
|
||||
->action(function (Collection $records, mixed $livewire): void {
|
||||
$disabledReason = static::crossTenantCompareBulkDisabledReason($records);
|
||||
$disabledReason = static::crossEnvironmentCompareBulkDisabledReason($records);
|
||||
|
||||
if ($disabledReason !== null) {
|
||||
Notification::make()
|
||||
@ -1167,7 +1167,7 @@ public static function table(Table $table): Table
|
||||
}
|
||||
|
||||
if (method_exists($livewire, 'redirect')) {
|
||||
$livewire->redirect(static::crossTenantCompareBulkOpenUrl($records, $livewire), navigate: true);
|
||||
$livewire->redirect(static::crossEnvironmentCompareBulkOpenUrl($records, $livewire), navigate: true);
|
||||
}
|
||||
}),
|
||||
Actions\BulkAction::make('syncSelected')
|
||||
@ -1324,7 +1324,7 @@ public static function sanitizeReviewStates(mixed $value): array
|
||||
{
|
||||
return static::sanitizeRequestedValues(
|
||||
$value,
|
||||
TenantTriageReview::DERIVED_STATES,
|
||||
ManagedEnvironmentTriageReview::DERIVED_STATES,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1366,10 +1366,10 @@ public static function tenantDashboardOpenUrl(ManagedEnvironment $record, array
|
||||
* triage_sort?: string|null
|
||||
* } $triageState
|
||||
*/
|
||||
public static function crossTenantCompareOpenUrl(ManagedEnvironment $record, array $triageState = []): string
|
||||
public static function crossEnvironmentCompareOpenUrl(ManagedEnvironment $record, array $triageState = []): string
|
||||
{
|
||||
return static::crossTenantCompareOpenUrlForSelection(
|
||||
targetTenant: $record,
|
||||
return static::crossEnvironmentCompareOpenUrlForSelection(
|
||||
targetEnvironment: $record,
|
||||
triageState: $triageState,
|
||||
);
|
||||
}
|
||||
@ -1382,10 +1382,10 @@ public static function crossTenantCompareOpenUrl(ManagedEnvironment $record, arr
|
||||
* triage_sort?: string|null
|
||||
* } $triageState
|
||||
*/
|
||||
public static function crossTenantCompareOpenUrlForSelection(
|
||||
ManagedEnvironment $targetTenant,
|
||||
public static function crossEnvironmentCompareOpenUrlForSelection(
|
||||
ManagedEnvironment $targetEnvironment,
|
||||
array $triageState = [],
|
||||
?ManagedEnvironment $sourceTenant = null,
|
||||
?ManagedEnvironment $sourceEnvironment = null,
|
||||
): string {
|
||||
$normalizedState = static::portfolioReturnFilters(
|
||||
static::sanitizeBackupPostures($triageState['backup_posture'] ?? []),
|
||||
@ -1394,12 +1394,12 @@ public static function crossTenantCompareOpenUrlForSelection(
|
||||
static::sanitizeTriageSort($triageState['triage_sort'] ?? null),
|
||||
);
|
||||
|
||||
return CrossTenantComparePage::launchUrl(
|
||||
sourceTenant: $sourceTenant,
|
||||
targetTenant: $targetTenant,
|
||||
return CrossEnvironmentComparePage::launchUrl(
|
||||
sourceEnvironment: $sourceEnvironment,
|
||||
targetEnvironment: $targetEnvironment,
|
||||
navigationContext: CanonicalNavigationContext::forTenantRegistry(
|
||||
backLinkUrl: static::getUrl(panel: 'admin', parameters: $normalizedState),
|
||||
tenantId: $sourceTenant instanceof ManagedEnvironment ? null : (int) $targetTenant->getKey(),
|
||||
tenantId: $sourceEnvironment instanceof ManagedEnvironment ? null : (int) $targetEnvironment->getKey(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -1494,7 +1494,7 @@ private static function portfolioReturnFiltersFromRequest(array $query): array
|
||||
);
|
||||
}
|
||||
|
||||
private static function crossTenantCompareActionVisible(ManagedEnvironment $record): bool
|
||||
private static function crossEnvironmentCompareActionVisible(ManagedEnvironment $record): bool
|
||||
{
|
||||
if (! $record->isActive()) {
|
||||
return false;
|
||||
@ -1533,7 +1533,7 @@ private static function crossTenantCompareActionVisible(ManagedEnvironment $reco
|
||||
&& $tenantResolver->can($user, $record, Capabilities::TENANT_VIEW);
|
||||
}
|
||||
|
||||
private static function crossTenantCompareBulkDisabledReason(Collection $records): ?string
|
||||
private static function crossEnvironmentCompareBulkDisabledReason(Collection $records): ?string
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
@ -1541,20 +1541,20 @@ private static function crossTenantCompareBulkDisabledReason(Collection $records
|
||||
return UiTooltips::insufficientPermission();
|
||||
}
|
||||
|
||||
$tenants = $records
|
||||
$environments = $records
|
||||
->filter(fn ($record): bool => $record instanceof ManagedEnvironment)
|
||||
->values();
|
||||
|
||||
if ($records->count() !== 2 || $tenants->count() !== 2) {
|
||||
return 'Select exactly two tenants to compare.';
|
||||
if ($records->count() !== 2 || $environments->count() !== 2) {
|
||||
return 'Select exactly two environments to compare.';
|
||||
}
|
||||
|
||||
if ($tenants->contains(fn (ManagedEnvironment $tenant): bool => ! $tenant->isActive())) {
|
||||
return 'Only active tenants can be compared.';
|
||||
if ($environments->contains(fn (ManagedEnvironment $environment): bool => ! $environment->isActive())) {
|
||||
return 'Only active environments can be compared.';
|
||||
}
|
||||
|
||||
$workspaceIds = $tenants
|
||||
->map(fn (ManagedEnvironment $tenant): int => (int) $tenant->workspace_id)
|
||||
$workspaceIds = $environments
|
||||
->map(fn (ManagedEnvironment $environment): int => (int) $environment->workspace_id)
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
@ -1578,32 +1578,32 @@ private static function crossTenantCompareBulkDisabledReason(Collection $records
|
||||
return UiTooltips::insufficientPermission();
|
||||
}
|
||||
|
||||
/** @var CapabilityResolver $tenantResolver */
|
||||
$tenantResolver = app(CapabilityResolver::class);
|
||||
/** @var CapabilityResolver $environmentResolver */
|
||||
$environmentResolver = app(CapabilityResolver::class);
|
||||
|
||||
$isDenied = $tenants->contains(fn (ManagedEnvironment $tenant): bool => ! $user->canAccessTenant($tenant)
|
||||
|| ! $tenantResolver->can($user, $tenant, Capabilities::TENANT_VIEW));
|
||||
$isDenied = $environments->contains(fn (ManagedEnvironment $environment): bool => ! $user->canAccessTenant($environment)
|
||||
|| ! $environmentResolver->can($user, $environment, Capabilities::TENANT_VIEW));
|
||||
|
||||
return $isDenied ? UiTooltips::insufficientPermission() : null;
|
||||
}
|
||||
|
||||
private static function crossTenantCompareBulkClientDisabledExpression(mixed $livewire): string
|
||||
private static function crossEnvironmentCompareBulkClientDisabledExpression(mixed $livewire): string
|
||||
{
|
||||
$containsInactiveSelection = static::crossTenantCompareBulkContainsInactiveSelectionExpression($livewire);
|
||||
$containsInactiveSelection = static::crossEnvironmentCompareBulkContainsInactiveSelectionExpression($livewire);
|
||||
|
||||
return "getSelectedRecordsCount() !== 2 || {$containsInactiveSelection}";
|
||||
}
|
||||
|
||||
private static function crossTenantCompareBulkClientTooltipExpression(mixed $livewire): string
|
||||
private static function crossEnvironmentCompareBulkClientTooltipExpression(mixed $livewire): string
|
||||
{
|
||||
$containsInactiveSelection = static::crossTenantCompareBulkContainsInactiveSelectionExpression($livewire);
|
||||
$containsInactiveSelection = static::crossEnvironmentCompareBulkContainsInactiveSelectionExpression($livewire);
|
||||
|
||||
return "getSelectedRecordsCount() !== 2 ? 'Select exactly two tenants to compare.' : ({$containsInactiveSelection} ? 'Only active tenants can be compared.' : null)";
|
||||
return "getSelectedRecordsCount() !== 2 ? 'Select exactly two environments to compare.' : ({$containsInactiveSelection} ? 'Only active environments can be compared.' : null)";
|
||||
}
|
||||
|
||||
private static function crossTenantCompareBulkContainsInactiveSelectionExpression(mixed $livewire): string
|
||||
private static function crossEnvironmentCompareBulkContainsInactiveSelectionExpression(mixed $livewire): string
|
||||
{
|
||||
$inactiveRecordKeys = \Illuminate\Support\Js::from(static::crossTenantCompareInactiveSelectionRecordKeys($livewire));
|
||||
$inactiveRecordKeys = \Illuminate\Support\Js::from(static::crossEnvironmentCompareInactiveSelectionRecordKeys($livewire));
|
||||
|
||||
return "[...selectedRecords].some((key) => {$inactiveRecordKeys}.includes(key))";
|
||||
}
|
||||
@ -1611,7 +1611,7 @@ private static function crossTenantCompareBulkContainsInactiveSelectionExpressio
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private static function crossTenantCompareInactiveSelectionRecordKeys(mixed $livewire): array
|
||||
private static function crossEnvironmentCompareInactiveSelectionRecordKeys(mixed $livewire): array
|
||||
{
|
||||
if (! $livewire instanceof HasTable || ! method_exists($livewire, 'getTableRecordKey')) {
|
||||
return [];
|
||||
@ -1630,9 +1630,9 @@ private static function crossTenantCompareInactiveSelectionRecordKeys(mixed $liv
|
||||
->all();
|
||||
}
|
||||
|
||||
private static function crossTenantCompareBulkOpenUrl(Collection $records, mixed $livewire): string
|
||||
private static function crossEnvironmentCompareBulkOpenUrl(Collection $records, mixed $livewire): string
|
||||
{
|
||||
$triageState = $livewire instanceof Pages\ListTenants
|
||||
$triageState = $livewire instanceof Pages\ListManagedEnvironments
|
||||
? static::currentPortfolioTriageState($livewire)
|
||||
: [];
|
||||
|
||||
@ -1649,10 +1649,10 @@ private static function crossTenantCompareBulkOpenUrl(Collection $records, mixed
|
||||
->filter(fn ($record): bool => $record instanceof ManagedEnvironment)
|
||||
->values();
|
||||
|
||||
return static::crossTenantCompareOpenUrlForSelection(
|
||||
targetTenant: $tenants->get(1),
|
||||
return static::crossEnvironmentCompareOpenUrlForSelection(
|
||||
targetEnvironment: $tenants->get(1),
|
||||
triageState: $triageState,
|
||||
sourceTenant: $tenants->get(0),
|
||||
sourceEnvironment: $tenants->get(0),
|
||||
);
|
||||
}
|
||||
|
||||
@ -1808,9 +1808,9 @@ private static function recoveryEvidenceForTenant(ManagedEnvironment $tenant): ?
|
||||
*/
|
||||
private static function reviewStateOptions(): array
|
||||
{
|
||||
return collect(TenantTriageReview::DERIVED_STATES)
|
||||
return collect(ManagedEnvironmentTriageReview::DERIVED_STATES)
|
||||
->mapWithKeys(static fn (string $state): array => [
|
||||
$state => BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $state)->label,
|
||||
$state => BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $state)->label,
|
||||
])
|
||||
->all();
|
||||
}
|
||||
@ -1854,7 +1854,7 @@ private static function triageReviewSnapshot(): array
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
$resolved = app(TenantTriageReviewStateResolver::class)->resolveMany(
|
||||
$resolved = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
|
||||
workspaceId: $workspaceId,
|
||||
tenantIds: $snapshot['tenant_ids'],
|
||||
backupHealthByTenant: $snapshot['backup_health'],
|
||||
@ -2035,8 +2035,8 @@ private static function triageReviewActionModalDescription(
|
||||
return 'This triage slice no longer points at a current visible concern.';
|
||||
}
|
||||
|
||||
$currentLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $row['derived_state'])->label;
|
||||
$targetLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $targetManualState)->label;
|
||||
$currentLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $row['derived_state'])->label;
|
||||
$targetLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $targetManualState)->label;
|
||||
|
||||
return implode("\n\n", [
|
||||
'Concern family: '.$row['concern_family_label'],
|
||||
@ -2071,7 +2071,7 @@ private static function triageReviewActionIsDisabled(ManagedEnvironment $tenant)
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE);
|
||||
return ! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE);
|
||||
}
|
||||
|
||||
private static function triageReviewActionTooltip(ManagedEnvironment $tenant): ?string
|
||||
@ -2084,7 +2084,7 @@ private static function triageReviewActionTooltip(ManagedEnvironment $tenant): ?
|
||||
|
||||
$resolver = app(CapabilityResolver::class);
|
||||
|
||||
if ($resolver->isMember($user, $tenant) && ! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)) {
|
||||
if ($resolver->isMember($user, $tenant) && ! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)) {
|
||||
return UiTooltips::insufficientPermission();
|
||||
}
|
||||
|
||||
@ -2105,7 +2105,7 @@ private static function authorizeTriageReviewAction(ManagedEnvironment $tenant):
|
||||
abort(404);
|
||||
}
|
||||
|
||||
if (! $resolver->can($user, $tenant, Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)) {
|
||||
if (! $resolver->can($user, $tenant, Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)) {
|
||||
abort(403);
|
||||
}
|
||||
}
|
||||
@ -2122,7 +2122,7 @@ private static function handleTriageReviewMutation(
|
||||
ManagedEnvironment $tenant,
|
||||
array $triageState,
|
||||
string $targetManualState,
|
||||
TenantTriageReviewService $service,
|
||||
ManagedEnvironmentTriageReviewService $service,
|
||||
): void {
|
||||
$row = static::selectedActionTriageReviewRowForTenant($tenant, $triageState);
|
||||
|
||||
@ -2141,14 +2141,14 @@ private static function handleTriageReviewMutation(
|
||||
$recoveryEvidence = static::recoveryEvidenceForTenant($tenant);
|
||||
|
||||
$review = match ($targetManualState) {
|
||||
TenantTriageReview::STATE_REVIEWED => $service->markReviewed(
|
||||
ManagedEnvironmentTriageReview::STATE_REVIEWED => $service->markReviewed(
|
||||
tenant: $tenant,
|
||||
concernFamily: (string) $row['concern_family'],
|
||||
backupHealth: $backupHealth,
|
||||
recoveryEvidence: $recoveryEvidence,
|
||||
actor: $actor instanceof User ? $actor : null,
|
||||
),
|
||||
TenantTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
|
||||
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
|
||||
tenant: $tenant,
|
||||
concernFamily: (string) $row['concern_family'],
|
||||
backupHealth: $backupHealth,
|
||||
@ -2158,7 +2158,7 @@ private static function handleTriageReviewMutation(
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (! $review instanceof TenantTriageReview) {
|
||||
if (! $review instanceof ManagedEnvironmentTriageReview) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2169,7 +2169,7 @@ private static function handleTriageReviewMutation(
|
||||
->body(sprintf(
|
||||
'%s is now %s for %s.',
|
||||
$tenant->name,
|
||||
BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $review->current_state)->label,
|
||||
BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $review->current_state)->label,
|
||||
static::triageConcernFamilyLabel((string) $review->concern_family),
|
||||
))
|
||||
->success()
|
||||
@ -2197,7 +2197,7 @@ private static function selectedActionTriageReviewRowForTenant(ManagedEnvironmen
|
||||
$backupHealth = app(TenantBackupHealthResolver::class)->assess($tenant);
|
||||
$recoveryEvidence = app(RestoreSafetyResolver::class)->dashboardRecoveryEvidence($tenant);
|
||||
|
||||
$rows = app(TenantTriageReviewStateResolver::class)->resolveMany(
|
||||
$rows = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
|
||||
workspaceId: $workspaceId,
|
||||
tenantIds: [$tenantId],
|
||||
backupHealthByTenant: [$tenantId => $backupHealth],
|
||||
@ -2640,10 +2640,10 @@ public static function infolist(Schema $schema): Schema
|
||||
->formatStateUsing(fn ($state) => is_array($state) ? implode(', ', $state) : (string) $state),
|
||||
Infolists\Components\TextEntry::make('status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantPermissionStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantPermissionStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantPermissionStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantPermissionStatus)),
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::ManagedEnvironmentPermissionStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::ManagedEnvironmentPermissionStatus)),
|
||||
])
|
||||
->columnSpanFull(),
|
||||
])
|
||||
@ -2730,7 +2730,7 @@ public static function tenantIndexPrimaryAction(ManagedEnvironment $tenant): ?Te
|
||||
return $catalog[1] ?? null;
|
||||
}
|
||||
|
||||
public static function relatedOnboardingDraft(ManagedEnvironment $tenant): ?TenantOnboardingSession
|
||||
public static function relatedOnboardingDraft(ManagedEnvironment $tenant): ?ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
return static::tenantActionPolicy()->relatedOnboardingDraft($tenant);
|
||||
}
|
||||
@ -2749,7 +2749,7 @@ public static function relatedOnboardingDraftUrl(ManagedEnvironment $tenant): ?s
|
||||
{
|
||||
$draft = static::relatedOnboardingDraft($tenant);
|
||||
|
||||
if (! $draft instanceof TenantOnboardingSession) {
|
||||
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -3119,17 +3119,17 @@ private static function validatedLifecycleReason(string $reason, string $field):
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => Pages\ListTenants::route('/'),
|
||||
'view' => Pages\ViewTenant::route('/{record}'),
|
||||
'edit' => Pages\EditTenant::route('/{record}/edit'),
|
||||
'memberships' => Pages\ManageTenantMemberships::route('/{record}/memberships'),
|
||||
'index' => Pages\ListManagedEnvironments::route('/'),
|
||||
'view' => Pages\ViewManagedEnvironment::route('/{record}'),
|
||||
'edit' => Pages\EditManagedEnvironment::route('/{record}/edit'),
|
||||
'memberships' => Pages\ManageEnvironmentAccessScopes::route('/{record}/memberships'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
RelationManagers\TenantMembershipsRelationManager::class,
|
||||
RelationManagers\ManagedEnvironmentMembershipsRelationManager::class,
|
||||
];
|
||||
}
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\Pages;
|
||||
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
|
||||
|
||||
use App\Filament\Resources\TenantResource;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\Tenants\TenantActionSurface;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditTenant extends EditRecord
|
||||
class EditManagedEnvironment extends EditRecord
|
||||
{
|
||||
protected static string $resource = TenantResource::class;
|
||||
protected static string $resource = ManagedEnvironmentResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_values(array_filter([
|
||||
Actions\ActionGroup::make([
|
||||
TenantResource::makeRestoreTenantAction(
|
||||
ManagedEnvironmentResource::makeRestoreTenantAction(
|
||||
TenantActionSurface::TenantEditHeader,
|
||||
'You do not have permission to restore tenants.',
|
||||
),
|
||||
TenantResource::makeArchiveTenantAction(
|
||||
ManagedEnvironmentResource::makeArchiveTenantAction(
|
||||
TenantActionSurface::TenantEditHeader,
|
||||
'You do not have permission to archive tenants.',
|
||||
),
|
||||
@ -30,7 +30,7 @@ protected function getHeaderActions(): array
|
||||
->color('gray')
|
||||
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
|
||||
&& in_array(
|
||||
TenantResource::lifecycleActionDescriptor($this->getRecord(), TenantActionSurface::TenantEditHeader)?->key,
|
||||
ManagedEnvironmentResource::lifecycleActionDescriptor($this->getRecord(), TenantActionSurface::TenantEditHeader)?->key,
|
||||
['archive', 'restore'],
|
||||
true,
|
||||
)),
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\Pages;
|
||||
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
|
||||
|
||||
use App\Filament\Resources\TenantResource;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Onboarding\OnboardingDraftResolver;
|
||||
@ -13,9 +13,9 @@
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListTenants extends ListRecords
|
||||
class ListManagedEnvironments extends ListRecords
|
||||
{
|
||||
protected static string $resource = TenantResource::class;
|
||||
protected static string $resource = ManagedEnvironmentResource::class;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
@ -64,7 +64,7 @@ protected function getTableEmptyStateDescription(): ?string
|
||||
|
||||
private function makeOnboardingEntryAction(): Actions\Action
|
||||
{
|
||||
$descriptor = TenantResource::tenantActionPolicy()->onboardingEntryDescriptor($this->accessibleResumableDraftCount());
|
||||
$descriptor = ManagedEnvironmentResource::tenantActionPolicy()->onboardingEntryDescriptor($this->accessibleResumableDraftCount());
|
||||
|
||||
return Actions\Action::make('add_tenant')
|
||||
->label($descriptor->label)
|
||||
@ -92,10 +92,10 @@ private function applyRequestedTriageIntent(): void
|
||||
return;
|
||||
}
|
||||
|
||||
$backupPostures = TenantResource::sanitizeBackupPostures(request()->query('backup_posture'));
|
||||
$recoveryEvidence = TenantResource::sanitizeRecoveryEvidenceStates(request()->query('recovery_evidence'));
|
||||
$reviewStates = TenantResource::sanitizeReviewStates(request()->query('review_state'));
|
||||
$triageSort = TenantResource::sanitizeTriageSort(request()->query('triage_sort'));
|
||||
$backupPostures = ManagedEnvironmentResource::sanitizeBackupPostures(request()->query('backup_posture'));
|
||||
$recoveryEvidence = ManagedEnvironmentResource::sanitizeRecoveryEvidenceStates(request()->query('recovery_evidence'));
|
||||
$reviewStates = ManagedEnvironmentResource::sanitizeReviewStates(request()->query('review_state'));
|
||||
$triageSort = ManagedEnvironmentResource::sanitizeTriageSort(request()->query('triage_sort'));
|
||||
|
||||
foreach (['backup_posture', 'recovery_evidence', 'review_state', 'triage_sort'] as $filterName) {
|
||||
data_forget($this->tableFilters, $filterName);
|
||||
@ -139,10 +139,10 @@ private function hasActiveTriageEmptyState(): bool
|
||||
public function currentPortfolioTriageReturnState(): array
|
||||
{
|
||||
return [
|
||||
'backup_posture' => TenantResource::sanitizeBackupPostures(data_get($this->tableFilters, 'backup_posture.values', [])),
|
||||
'recovery_evidence' => TenantResource::sanitizeRecoveryEvidenceStates(data_get($this->tableFilters, 'recovery_evidence.values', [])),
|
||||
'review_state' => TenantResource::sanitizeReviewStates(data_get($this->tableFilters, 'review_state.values', [])),
|
||||
'triage_sort' => TenantResource::sanitizeTriageSort(data_get($this->tableFilters, 'triage_sort.value')),
|
||||
'backup_posture' => ManagedEnvironmentResource::sanitizeBackupPostures(data_get($this->tableFilters, 'backup_posture.values', [])),
|
||||
'recovery_evidence' => ManagedEnvironmentResource::sanitizeRecoveryEvidenceStates(data_get($this->tableFilters, 'recovery_evidence.values', [])),
|
||||
'review_state' => ManagedEnvironmentResource::sanitizeReviewStates(data_get($this->tableFilters, 'review_state.values', [])),
|
||||
'triage_sort' => ManagedEnvironmentResource::sanitizeTriageSort(data_get($this->tableFilters, 'triage_sort.value')),
|
||||
];
|
||||
}
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\Pages;
|
||||
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\ManagedEnvironmentLinks;
|
||||
use Filament\Actions\Action;
|
||||
|
||||
class ManageTenantMemberships extends ViewTenant
|
||||
class ManageEnvironmentAccessScopes extends ViewManagedEnvironment
|
||||
{
|
||||
protected static ?string $title = 'Manage environment access scope';
|
||||
|
||||
public function mount(int|string|ManagedEnvironment $tenant): void
|
||||
public function mount(int|string|ManagedEnvironment $environment): void
|
||||
{
|
||||
parent::mount($tenant instanceof ManagedEnvironment ? (string) $tenant->getRouteKey() : $tenant);
|
||||
parent::mount($environment instanceof ManagedEnvironment ? (string) $environment->getRouteKey() : $environment);
|
||||
}
|
||||
|
||||
public function getSubheading(): ?string
|
||||
@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\Pages;
|
||||
namespace App\Filament\Resources\ManagedEnvironmentResource\Pages;
|
||||
|
||||
use App\Filament\Resources\TenantResource;
|
||||
use App\Filament\Widgets\Tenant\AdminRolesSummaryWidget;
|
||||
use App\Filament\Widgets\Tenant\RecentOperationsSummary;
|
||||
use App\Filament\Widgets\Tenant\TenantArchivedBanner;
|
||||
use App\Filament\Widgets\Tenant\TenantVerificationReport;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource;
|
||||
use App\Filament\Widgets\ManagedEnvironment\AdminRolesSummaryWidget;
|
||||
use App\Filament\Widgets\ManagedEnvironment\RecentOperationsSummary;
|
||||
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentArchivedBanner;
|
||||
use App\Filament\Widgets\ManagedEnvironment\ManagedEnvironmentVerificationReport;
|
||||
use App\Jobs\RefreshTenantRbacHealthJob;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\User;
|
||||
@ -22,9 +22,9 @@
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewTenant extends ViewRecord
|
||||
class ViewManagedEnvironment extends ViewRecord
|
||||
{
|
||||
protected static string $resource = TenantResource::class;
|
||||
protected static string $resource = ManagedEnvironmentResource::class;
|
||||
|
||||
public static function verificationHeaderActionLabel(): string
|
||||
{
|
||||
@ -44,9 +44,9 @@ public function getHeaderWidgetsColumns(): int|array
|
||||
protected function getHeaderWidgets(): array
|
||||
{
|
||||
return [
|
||||
TenantArchivedBanner::class,
|
||||
ManagedEnvironmentArchivedBanner::class,
|
||||
RecentOperationsSummary::class,
|
||||
TenantVerificationReport::class,
|
||||
ManagedEnvironmentVerificationReport::class,
|
||||
AdminRolesSummaryWidget::class,
|
||||
];
|
||||
}
|
||||
@ -54,27 +54,27 @@ protected function getHeaderWidgets(): array
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return array_values(array_filter([
|
||||
TenantResource::makeMembershipsAction(),
|
||||
ManagedEnvironmentResource::makeMembershipsAction(),
|
||||
Actions\ActionGroup::make([
|
||||
TenantResource::makeAdminConsentAction(),
|
||||
TenantResource::makeOpenInEntraAction(),
|
||||
ManagedEnvironmentResource::makeAdminConsentAction(),
|
||||
ManagedEnvironmentResource::makeOpenInEntraAction(),
|
||||
])
|
||||
->label('External links')
|
||||
->icon('heroicon-o-arrow-top-right-on-square')
|
||||
->color('gray')
|
||||
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
|
||||
&& TenantResource::tenantViewExternalGroupVisible($this->getRecord())),
|
||||
&& ManagedEnvironmentResource::tenantViewExternalGroupVisible($this->getRecord())),
|
||||
Actions\ActionGroup::make([
|
||||
TenantResource::makeSyncTenantAction(),
|
||||
TenantResource::makeVerifyConfigurationAction('tenant_view_header'),
|
||||
TenantResource::rbacAction(),
|
||||
ManagedEnvironmentResource::makeSyncTenantAction(),
|
||||
ManagedEnvironmentResource::makeVerifyConfigurationAction('tenant_view_header'),
|
||||
ManagedEnvironmentResource::rbacAction(),
|
||||
UiEnforcement::forAction(
|
||||
Actions\Action::make('refresh_rbac')
|
||||
->label('Refresh RBAC status')
|
||||
->icon('heroicon-o-arrow-path')
|
||||
->color('primary')
|
||||
->requiresConfirmation()
|
||||
->visible(fn (ManagedEnvironment $record): bool => TenantResource::tenantSetupMutationVisible($record))
|
||||
->visible(fn (ManagedEnvironment $record): bool => ManagedEnvironmentResource::tenantSetupMutationVisible($record))
|
||||
->action(function (ManagedEnvironment $record): void {
|
||||
$user = auth()->user();
|
||||
|
||||
@ -140,27 +140,27 @@ protected function getHeaderActions(): array
|
||||
->icon('heroicon-o-wrench-screwdriver')
|
||||
->color('gray')
|
||||
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
|
||||
&& TenantResource::tenantViewSetupGroupVisible($this->getRecord())),
|
||||
&& ManagedEnvironmentResource::tenantViewSetupGroupVisible($this->getRecord())),
|
||||
Actions\ActionGroup::make([
|
||||
TenantResource::makeTenantViewMarkReviewedAction(),
|
||||
TenantResource::makeTenantViewMarkFollowUpNeededAction(),
|
||||
ManagedEnvironmentResource::makeTenantViewMarkReviewedAction(),
|
||||
ManagedEnvironmentResource::makeTenantViewMarkFollowUpNeededAction(),
|
||||
])
|
||||
->label('Triage')
|
||||
->icon('heroicon-o-check-circle')
|
||||
->color('gray')
|
||||
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
|
||||
&& TenantResource::tenantViewTriageGroupVisible($this->getRecord())),
|
||||
&& ManagedEnvironmentResource::tenantViewTriageGroupVisible($this->getRecord())),
|
||||
Actions\ActionGroup::make([
|
||||
TenantResource::makeRestoreTenantAction(TenantActionSurface::TenantViewHeader),
|
||||
TenantResource::makeRestoreTenantToWorkspaceAction(),
|
||||
TenantResource::makeRemoveTenantFromWorkspaceAction(),
|
||||
TenantResource::makeArchiveTenantAction(TenantActionSurface::TenantViewHeader),
|
||||
ManagedEnvironmentResource::makeRestoreTenantAction(TenantActionSurface::TenantViewHeader),
|
||||
ManagedEnvironmentResource::makeRestoreTenantToWorkspaceAction(),
|
||||
ManagedEnvironmentResource::makeRemoveTenantFromWorkspaceAction(),
|
||||
ManagedEnvironmentResource::makeArchiveTenantAction(TenantActionSurface::TenantViewHeader),
|
||||
])
|
||||
->label('Lifecycle')
|
||||
->icon('heroicon-o-archive-box')
|
||||
->color('gray')
|
||||
->visible(fn (): bool => $this->getRecord() instanceof ManagedEnvironment
|
||||
&& TenantResource::tenantViewLifecycleGroupVisible($this->getRecord())),
|
||||
&& ManagedEnvironmentResource::tenantViewLifecycleGroupVisible($this->getRecord())),
|
||||
]));
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\TenantResource\RelationManagers;
|
||||
namespace App\Filament\Resources\ManagedEnvironmentResource\RelationManagers;
|
||||
|
||||
use App\Filament\Resources\TenantResource\Pages\ManageTenantMemberships;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource\Pages\ManageEnvironmentAccessScopes;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\ManagedEnvironmentMembership;
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Services\Auth\TenantMembershipManager;
|
||||
use App\Services\Auth\ManagedEnvironmentMembershipManager;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\Ui\ActionSurface\ActionSurfaceDeclaration;
|
||||
@ -22,7 +22,7 @@
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class TenantMembershipsRelationManager extends RelationManager
|
||||
class ManagedEnvironmentMembershipsRelationManager extends RelationManager
|
||||
{
|
||||
protected static string $relationship = 'memberships';
|
||||
|
||||
@ -42,7 +42,7 @@ public static function canViewForRecord(Model $ownerRecord, string $pageClass):
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($pageClass !== ManageTenantMemberships::class) {
|
||||
if ($pageClass !== ManageEnvironmentAccessScopes::class) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ public function table(Table $table): Table
|
||||
->searchable()
|
||||
->options(fn (): array => $this->workspaceMemberOptions()),
|
||||
])
|
||||
->action(function (array $data, TenantMembershipManager $manager): void {
|
||||
->action(function (array $data, ManagedEnvironmentMembershipManager $manager): void {
|
||||
$tenant = $this->getOwnerRecord();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
@ -155,7 +155,7 @@ public function table(Table $table): Table
|
||||
->color('danger')
|
||||
->icon('heroicon-o-x-mark')
|
||||
->requiresConfirmation()
|
||||
->action(function (ManagedEnvironmentMembership $record, TenantMembershipManager $manager): void {
|
||||
->action(function (ManagedEnvironmentMembership $record, ManagedEnvironmentMembershipManager $manager): void {
|
||||
$tenant = $this->getOwnerRecord();
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
@ -200,11 +200,11 @@ public static function infolist(Schema $schema): Schema
|
||||
Section::make('Metadata')
|
||||
->schema([
|
||||
TextEntry::make('initiator.name')->label('Initiated by')->placeholder('—'),
|
||||
TextEntry::make('tenantReview.id')
|
||||
TextEntry::make('environmentReview.id')
|
||||
->label('ManagedEnvironment review')
|
||||
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
|
||||
->url(fn (ReviewPack $record): ?string => $record->tenantReview && $record->tenant
|
||||
? TenantReviewResource::tenantScopedUrl('view', ['record' => $record->tenantReview], $record->tenant)
|
||||
->url(fn (ReviewPack $record): ?string => $record->environmentReview && $record->tenant
|
||||
? EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $record->environmentReview], $record->tenant)
|
||||
: null)
|
||||
->placeholder('—'),
|
||||
TextEntry::make('customer_workspace')
|
||||
@ -217,10 +217,10 @@ public static function infolist(Schema $schema): Schema
|
||||
TextEntry::make('summary.review_status')
|
||||
->label('Review status')
|
||||
->badge()
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::TenantReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::TenantReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::TenantReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::TenantReviewStatus))
|
||||
->formatStateUsing(BadgeRenderer::label(BadgeDomain::EnvironmentReviewStatus))
|
||||
->color(BadgeRenderer::color(BadgeDomain::EnvironmentReviewStatus))
|
||||
->icon(BadgeRenderer::icon(BadgeDomain::EnvironmentReviewStatus))
|
||||
->iconColor(BadgeRenderer::iconColor(BadgeDomain::EnvironmentReviewStatus))
|
||||
->placeholder('—'),
|
||||
TextEntry::make('operationRun.id')
|
||||
->label('Operation')
|
||||
@ -306,7 +306,7 @@ public static function table(Table $table): Table
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->placeholder('—'),
|
||||
Tables\Columns\TextColumn::make('tenantReview.id')
|
||||
Tables\Columns\TextColumn::make('environmentReview.id')
|
||||
->label('Review')
|
||||
->formatStateUsing(fn (?int $state): string => $state ? '#'.$state : '—')
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
@ -429,7 +429,7 @@ public static function getEloquentQuery(): Builder
|
||||
}
|
||||
|
||||
return parent::getEloquentQuery()
|
||||
->with(['tenant', 'operationRun', 'evidenceSnapshot', 'tenantReview'])
|
||||
->with(['tenant', 'operationRun', 'evidenceSnapshot', 'environmentReview'])
|
||||
->where('managed_environment_id', (int) $tenant->getKey());
|
||||
}
|
||||
|
||||
@ -575,7 +575,7 @@ public static function executeGeneration(array $data): void
|
||||
return;
|
||||
}
|
||||
|
||||
OperationUxPresenter::queuedToast('tenant.review_pack.generate')->send();
|
||||
OperationUxPresenter::queuedToast('environment.review_pack.generate')->send();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Resources\TenantReviewResource\Pages;
|
||||
|
||||
use App\Filament\Resources\TenantReviewResource;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListTenantReviews extends ListRecords
|
||||
{
|
||||
protected static string $resource = TenantReviewResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
TenantReviewResource::makeCreateReviewAction(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -9,7 +9,7 @@
|
||||
use App\Models\PlatformUser;
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantPermission;
|
||||
use App\Models\ManagedEnvironmentPermission;
|
||||
use App\Support\Auth\PlatformCapabilities;
|
||||
use App\Support\CustomerHealth\WorkspaceHealthSummaryQuery;
|
||||
use App\Support\OperationCatalog;
|
||||
@ -69,11 +69,11 @@ public function providerConnections(): Collection
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, TenantPermission>
|
||||
* @return Collection<int, ManagedEnvironmentPermission>
|
||||
*/
|
||||
public function tenantPermissions(): Collection
|
||||
public function managedEnvironmentPermissions(): Collection
|
||||
{
|
||||
return TenantPermission::query()
|
||||
return ManagedEnvironmentPermission::query()
|
||||
->where('managed_environment_id', (int) $this->tenant->getKey())
|
||||
->orderBy('permission_key')
|
||||
->limit(20)
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\OpsUx\ActiveRuns;
|
||||
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
|
||||
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\StatsOverviewWidget;
|
||||
use Filament\Widgets\StatsOverviewWidget\Stat;
|
||||
@ -32,7 +32,7 @@ protected function getStats(): array
|
||||
return [];
|
||||
}
|
||||
|
||||
$summary = app(TenantDashboardSummaryBuilder::class)->build($tenant, auth()->user());
|
||||
$summary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, auth()->user());
|
||||
|
||||
$stats = [];
|
||||
|
||||
|
||||
@ -5,17 +5,17 @@
|
||||
namespace App\Filament\Widgets\Dashboard;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
|
||||
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantDashboardContextChips extends Widget
|
||||
class EnvironmentDashboardContextChips extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
protected string $view = 'filament.widgets.dashboard.tenant-dashboard-context-chips';
|
||||
protected string $view = 'filament.widgets.dashboard.environment-dashboard-context-chips';
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
@ -37,11 +37,11 @@ protected function getViewData(): array
|
||||
];
|
||||
}
|
||||
|
||||
$summary = app(TenantDashboardSummaryBuilder::class)->build($tenant, auth()->user());
|
||||
$summary = app(EnvironmentDashboardSummaryBuilder::class)->build($tenant, auth()->user());
|
||||
|
||||
return [
|
||||
'context' => $summary->context,
|
||||
'pollingInterval' => $summary->pollingInterval,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5,17 +5,17 @@
|
||||
namespace App\Filament\Widgets\Dashboard;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\TenantDashboard\TenantDashboardSummaryBuilder;
|
||||
use App\Support\EnvironmentDashboard\EnvironmentDashboardSummaryBuilder;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantDashboardOverview extends Widget
|
||||
class EnvironmentDashboardOverview extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
protected string $view = 'filament.widgets.dashboard.tenant-dashboard-overview';
|
||||
protected string $view = 'filament.widgets.dashboard.environment-dashboard-overview';
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
@ -49,8 +49,8 @@ protected function getViewData(): array
|
||||
];
|
||||
}
|
||||
|
||||
return app(TenantDashboardSummaryBuilder::class)
|
||||
return app(EnvironmentDashboardSummaryBuilder::class)
|
||||
->build($tenant)
|
||||
->toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Filament\Resources\StoredReportResource;
|
||||
use App\Jobs\ScanEntraAdminRolesJob;
|
||||
@ -22,7 +22,7 @@ class AdminRolesSummaryWidget extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.admin-roles-summary';
|
||||
protected string $view = 'filament.widgets.managed-environment.admin-roles-summary';
|
||||
|
||||
public ?ManagedEnvironment $record = null;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Filament\Pages\BaselineCompareLanding;
|
||||
use App\Models\ManagedEnvironment;
|
||||
@ -16,7 +16,7 @@ class BaselineCompareCoverageBanner extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.baseline-compare-coverage-banner';
|
||||
protected string $view = 'filament.widgets.managed-environment.baseline-compare-coverage-banner';
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Filament\Resources\FindingExceptionResource;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Filament\Resources\FindingResource;
|
||||
use Filament\Widgets\StatsOverviewWidget as BaseWidget;
|
||||
@ -2,18 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Support\Tenants\TenantLifecyclePresentation;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantArchivedBanner extends Widget
|
||||
class ManagedEnvironmentArchivedBanner extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.tenant-archived-banner';
|
||||
protected string $view = 'filament.widgets.managed-environment.managed-environment-archived-banner';
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Exceptions\Entitlements\WorkspaceEntitlementBlockedException;
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
@ -23,13 +23,13 @@
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantReviewPackCard extends Widget
|
||||
class ManagedEnvironmentReviewPackCard extends Widget
|
||||
{
|
||||
private const string ACTIVE_POLLING_INTERVAL = '10s';
|
||||
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.tenant-review-pack-card';
|
||||
protected string $view = 'filament.widgets.managed-environment.managed-environment-review-pack-card';
|
||||
|
||||
public ?ManagedEnvironment $record = null;
|
||||
|
||||
@ -176,7 +176,7 @@ protected function getViewData(): array
|
||||
: null;
|
||||
|
||||
$latestPack = ReviewPack::query()
|
||||
->with(['tenantReview', 'operationRun'])
|
||||
->with(['environmentReview', 'operationRun'])
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->orderByDesc('created_at')
|
||||
->orderByDesc('id')
|
||||
@ -210,8 +210,8 @@ protected function getViewData(): array
|
||||
}
|
||||
|
||||
$reviewUrl = null;
|
||||
if ($latestPack->tenantReview && $canView) {
|
||||
$reviewUrl = \App\Filament\Resources\TenantReviewResource::tenantScopedUrl('view', ['record' => $latestPack->tenantReview], $tenant);
|
||||
if ($latestPack->environmentReview && $canView) {
|
||||
$reviewUrl = \App\Filament\Resources\EnvironmentReviewResource::tenantScopedUrl('view', ['record' => $latestPack->environmentReview], $tenant);
|
||||
}
|
||||
|
||||
$failedReason = null;
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantTriageReview;
|
||||
use App\Models\ManagedEnvironmentTriageReview;
|
||||
use App\Models\User;
|
||||
use App\Services\PortfolioTriage\TenantTriageReviewService;
|
||||
use App\Services\PortfolioTriage\ManagedEnvironmentTriageReviewService;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use App\Support\BackupHealth\TenantBackupHealthResolver;
|
||||
use App\Support\Badges\BadgeDomain;
|
||||
@ -15,7 +15,7 @@
|
||||
use App\Support\PortfolioTriage\PortfolioArrivalContext;
|
||||
use App\Support\PortfolioTriage\PortfolioArrivalContextResolver;
|
||||
use App\Support\PortfolioTriage\PortfolioArrivalContextToken;
|
||||
use App\Support\PortfolioTriage\TenantTriageReviewStateResolver;
|
||||
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewStateResolver;
|
||||
use App\Support\Rbac\UiEnforcement;
|
||||
use App\Support\RestoreSafety\RestoreSafetyResolver;
|
||||
use Filament\Actions\Action;
|
||||
@ -27,7 +27,7 @@
|
||||
use Filament\Schemas\Contracts\HasSchemas;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantTriageArrivalContinuity extends Widget implements HasActions, HasSchemas
|
||||
class ManagedEnvironmentTriageArrivalContinuity extends Widget implements HasActions, HasSchemas
|
||||
{
|
||||
use InteractsWithActions;
|
||||
use InteractsWithSchemas;
|
||||
@ -59,7 +59,7 @@ class TenantTriageArrivalContinuity extends Widget implements HasActions, HasSch
|
||||
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.triage-arrival-continuity';
|
||||
protected string $view = 'filament.widgets.managed-environment.triage-arrival-continuity';
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
@ -100,14 +100,14 @@ public function markReviewedAction(): Action
|
||||
->color('success')
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Mark reviewed')
|
||||
->modalDescription($this->reviewModalDescription(TenantTriageReview::STATE_REVIEWED))
|
||||
->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_REVIEWED))
|
||||
->visible(fn (): bool => $this->canShowReviewActions())
|
||||
->action(function (TenantTriageReviewService $service): void {
|
||||
$this->handleReviewMutation(TenantTriageReview::STATE_REVIEWED, $service);
|
||||
->action(function (ManagedEnvironmentTriageReviewService $service): void {
|
||||
$this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_REVIEWED, $service);
|
||||
}),
|
||||
)
|
||||
->preserveVisibility()
|
||||
->requireCapability(Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
|
||||
->apply();
|
||||
}
|
||||
|
||||
@ -120,14 +120,14 @@ public function markFollowUpNeededAction(): Action
|
||||
->color('warning')
|
||||
->requiresConfirmation()
|
||||
->modalHeading('Mark follow-up needed')
|
||||
->modalDescription($this->reviewModalDescription(TenantTriageReview::STATE_FOLLOW_UP_NEEDED))
|
||||
->modalDescription($this->reviewModalDescription(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED))
|
||||
->visible(fn (): bool => $this->canShowReviewActions())
|
||||
->action(function (TenantTriageReviewService $service): void {
|
||||
$this->handleReviewMutation(TenantTriageReview::STATE_FOLLOW_UP_NEEDED, $service);
|
||||
->action(function (ManagedEnvironmentTriageReviewService $service): void {
|
||||
$this->handleReviewMutation(ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED, $service);
|
||||
}),
|
||||
)
|
||||
->preserveVisibility()
|
||||
->requireCapability(Capabilities::TENANT_TRIAGE_REVIEW_MANAGE)
|
||||
->requireCapability(Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE)
|
||||
->apply();
|
||||
}
|
||||
|
||||
@ -170,10 +170,10 @@ private function reviewModalDescription(string $targetManualState): \Closure
|
||||
}
|
||||
|
||||
$currentLabel = BadgeRenderer::spec(
|
||||
BadgeDomain::TenantTriageReviewState,
|
||||
(string) ($reviewState['derived_state'] ?? TenantTriageReview::DERIVED_STATE_NOT_REVIEWED),
|
||||
BadgeDomain::ManagedEnvironmentTriageReviewState,
|
||||
(string) ($reviewState['derived_state'] ?? ManagedEnvironmentTriageReview::DERIVED_STATE_NOT_REVIEWED),
|
||||
)->label;
|
||||
$targetLabel = BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $targetManualState)->label;
|
||||
$targetLabel = BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $targetManualState)->label;
|
||||
|
||||
return implode("\n\n", [
|
||||
'Concern family: '.$this->concernFamilyLabel($context->concernFamily),
|
||||
@ -184,7 +184,7 @@ private function reviewModalDescription(string $targetManualState): \Closure
|
||||
};
|
||||
}
|
||||
|
||||
private function handleReviewMutation(string $targetManualState, TenantTriageReviewService $service): void
|
||||
private function handleReviewMutation(string $targetManualState, ManagedEnvironmentTriageReviewService $service): void
|
||||
{
|
||||
$tenant = Filament::getTenant();
|
||||
|
||||
@ -219,14 +219,14 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
|
||||
$actor = auth()->user();
|
||||
|
||||
$review = match ($targetManualState) {
|
||||
TenantTriageReview::STATE_REVIEWED => $service->markReviewed(
|
||||
ManagedEnvironmentTriageReview::STATE_REVIEWED => $service->markReviewed(
|
||||
tenant: $tenant,
|
||||
concernFamily: $context->concernFamily,
|
||||
backupHealth: $concernTruth['backupHealth'],
|
||||
recoveryEvidence: $concernTruth['recoveryEvidence'],
|
||||
actor: $actor instanceof User ? $actor : null,
|
||||
),
|
||||
TenantTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
|
||||
ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED => $service->markFollowUpNeeded(
|
||||
tenant: $tenant,
|
||||
concernFamily: $context->concernFamily,
|
||||
backupHealth: $concernTruth['backupHealth'],
|
||||
@ -236,7 +236,7 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
|
||||
default => null,
|
||||
};
|
||||
|
||||
if (! $review instanceof TenantTriageReview) {
|
||||
if (! $review instanceof ManagedEnvironmentTriageReview) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -247,7 +247,7 @@ private function handleReviewMutation(string $targetManualState, TenantTriageRev
|
||||
->body(sprintf(
|
||||
'%s is now %s for %s.',
|
||||
$tenant->name,
|
||||
BadgeRenderer::spec(BadgeDomain::TenantTriageReviewState, $review->current_state)->label,
|
||||
BadgeRenderer::spec(BadgeDomain::ManagedEnvironmentTriageReviewState, $review->current_state)->label,
|
||||
$this->concernFamilyLabel($context->concernFamily),
|
||||
))
|
||||
->success()
|
||||
@ -268,7 +268,7 @@ private function currentReviewStateFor(ManagedEnvironment $tenant, string $conce
|
||||
|
||||
$concernTruth = $this->concernTruthFor($tenant);
|
||||
|
||||
$reviewState = app(TenantTriageReviewStateResolver::class)->resolveMany(
|
||||
$reviewState = app(ManagedEnvironmentTriageReviewStateResolver::class)->resolveMany(
|
||||
workspaceId: (int) $tenant->workspace_id,
|
||||
tenantIds: [$tenantId],
|
||||
backupHealthByTenant: [$tenantId => $concernTruth['backupHealth']],
|
||||
@ -2,9 +2,9 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Filament\Resources\TenantResource\Pages\ViewTenant;
|
||||
use App\Filament\Resources\ManagedEnvironmentResource\Pages\ViewManagedEnvironment;
|
||||
use App\Filament\Support\VerificationReportChangeIndicator;
|
||||
use App\Filament\Support\VerificationReportViewer;
|
||||
use App\Models\OperationRun;
|
||||
@ -24,11 +24,11 @@
|
||||
use Filament\Notifications\Notification;
|
||||
use Filament\Widgets\Widget;
|
||||
|
||||
class TenantVerificationReport extends Widget
|
||||
class ManagedEnvironmentVerificationReport extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.tenant-verification-report';
|
||||
protected string $view = 'filament.widgets.managed-environment.managed-environment-verification-report';
|
||||
|
||||
public ?ManagedEnvironment $record = null;
|
||||
|
||||
@ -203,7 +203,7 @@ protected function getViewData(): array
|
||||
'startTooltip' => $isTenantMember && $canOperate && ! $canStart ? UiTooltips::insufficientPermission() : null,
|
||||
'lifecycleNotice' => $lifecycleNotice,
|
||||
'rerunHint' => $run instanceof OperationRun && $canStart
|
||||
? ViewTenant::verificationHeaderActionHint()
|
||||
? ViewManagedEnvironment::verificationHeaderActionHint()
|
||||
: null,
|
||||
];
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Filament\Widgets\Tenant;
|
||||
namespace App\Filament\Widgets\ManagedEnvironment;
|
||||
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ManagedEnvironment;
|
||||
@ -15,7 +15,7 @@ class RecentOperationsSummary extends Widget
|
||||
{
|
||||
protected static bool $isLazy = false;
|
||||
|
||||
protected string $view = 'filament.widgets.tenant.recent-operations-summary';
|
||||
protected string $view = 'filament.widgets.managed-environment.recent-operations-summary';
|
||||
|
||||
public ?ManagedEnvironment $record = null;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Services\Intune\AuditLogger;
|
||||
use App\Support\Providers\ProviderConnectionType;
|
||||
use App\Support\Providers\ProviderConsentStatus;
|
||||
@ -23,8 +23,8 @@ public function __invoke(
|
||||
Request $request,
|
||||
AuditLogger $auditLogger,
|
||||
): View {
|
||||
$expectedState = $request->session()->pull('tenant_onboard_state');
|
||||
$workspaceId = $request->session()->pull('tenant_onboard_workspace_id');
|
||||
$expectedState = $request->session()->pull('environment_onboard_state');
|
||||
$workspaceId = $request->session()->pull('environment_onboard_workspace_id');
|
||||
$tenantKey = $request->string('tenant')->toString();
|
||||
$state = $request->string('state')->toString();
|
||||
$tenantIdentifier = $tenantKey ?: $this->parseState($state);
|
||||
@ -219,10 +219,10 @@ private function verificationStateLabel(ProviderConnection $connection): string
|
||||
|
||||
private function invalidateResumableOnboardingVerificationState(ManagedEnvironment $tenant, ProviderConnection $connection): void
|
||||
{
|
||||
TenantOnboardingSession::query()
|
||||
ManagedEnvironmentOnboardingSession::query()
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->resumable()
|
||||
->each(function (TenantOnboardingSession $draft) use ($connection): void {
|
||||
->each(function (ManagedEnvironmentOnboardingSession $draft) use ($connection): void {
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
$providerConnectionId = $state['provider_connection_id'] ?? null;
|
||||
$providerConnectionId = is_numeric($providerConnectionId) ? (int) $providerConnectionId : null;
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
final class ClearTenantContextController
|
||||
final class ClearEnvironmentContextController
|
||||
{
|
||||
public function __invoke(Request $request): RedirectResponse
|
||||
{
|
||||
@ -38,13 +38,13 @@ public function __invoke(Request $request): RedirectResponse
|
||||
$workspace = $workspaceContext->currentWorkspace($request);
|
||||
|
||||
if ($workspace !== null) {
|
||||
return redirect()->route('admin.workspace.managed-tenants.index', ['workspace' => $workspace]);
|
||||
return redirect()->route('admin.workspace.managed-environments.index', ['workspace' => $workspace]);
|
||||
}
|
||||
|
||||
return redirect()->route('admin.home');
|
||||
}
|
||||
|
||||
if ($previousPath === '' || $previousPath === '/admin/clear-tenant-context') {
|
||||
if ($previousPath === '' || $previousPath === '/admin/clear-environment-context') {
|
||||
return redirect()->to(OperationRunLinks::index());
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response as ResponseAlias;
|
||||
|
||||
class TenantOnboardingController extends Controller
|
||||
class ManagedEnvironmentOnboardingController extends Controller
|
||||
{
|
||||
public function __invoke(
|
||||
Request $request,
|
||||
@ -27,12 +27,12 @@ public function __invoke(
|
||||
abort_if($tenantIdentifier === '', ResponseAlias::HTTP_NOT_FOUND);
|
||||
|
||||
$state = Str::uuid()->toString();
|
||||
$request->session()->put('tenant_onboard_state', $state);
|
||||
$request->session()->put('environment_onboard_state', $state);
|
||||
|
||||
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId($request);
|
||||
|
||||
if ($workspaceId !== null) {
|
||||
$request->session()->put('tenant_onboard_workspace_id', (int) $workspaceId);
|
||||
$request->session()->put('environment_onboard_workspace_id', (int) $workspaceId);
|
||||
}
|
||||
|
||||
$tenant = $this->resolveTenant($tenantIdentifier, is_numeric($workspaceId) ? (int) $workspaceId : null);
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
final class OpenFindingExceptionsQueueController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request, ManagedEnvironment $tenant): RedirectResponse
|
||||
public function __invoke(Request $request, ManagedEnvironment $environment): RedirectResponse
|
||||
{
|
||||
$user = auth()->user();
|
||||
|
||||
@ -24,13 +24,13 @@ public function __invoke(Request $request, ManagedEnvironment $tenant): Redirect
|
||||
abort(403);
|
||||
}
|
||||
|
||||
$workspace = Workspace::query()->whereKey($tenant->workspace_id)->first();
|
||||
$workspace = Workspace::query()->whereKey($environment->workspace_id)->first();
|
||||
|
||||
if (! $workspace instanceof Workspace) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
if (! $user->canAccessTenant($tenant)) {
|
||||
if (! $user->canAccessTenant($environment)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
@ -49,12 +49,12 @@ public function __invoke(Request $request, ManagedEnvironment $tenant): Redirect
|
||||
|
||||
$workspaceContext->setCurrentWorkspace($workspace, $user, $request);
|
||||
|
||||
if (! $workspaceContext->rememberTenantContext($tenant, $request)) {
|
||||
if (! $workspaceContext->rememberTenantContext($environment, $request)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
return redirect()->to(FindingExceptionsQueue::getUrl([
|
||||
'tenant' => (string) $tenant->external_id,
|
||||
'tenant' => (string) $environment->external_id,
|
||||
], panel: 'admin'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,8 +55,8 @@ public function __invoke(Request $request, ReviewPack $reviewPack): StreamedResp
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_pack_id' => (int) $reviewPack->getKey(),
|
||||
'tenant_review_id' => $reviewPack->tenant_review_id !== null
|
||||
? (int) $reviewPack->tenant_review_id
|
||||
'environment_review_id' => $reviewPack->environment_review_id !== null
|
||||
? (int) $reviewPack->environment_review_id
|
||||
: null,
|
||||
'source_surface' => (string) $request->query('source_surface', 'review_pack'),
|
||||
'review_id' => $request->query('review_id'),
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
final class SelectTenantController
|
||||
final class SelectEnvironmentController
|
||||
{
|
||||
public function __invoke(Request $request): RedirectResponse
|
||||
{
|
||||
@ -204,7 +204,7 @@ private function isLivewireUpdatePath(string $path): bool
|
||||
|
||||
private function isChooserFirstPath(string $path): bool
|
||||
{
|
||||
return in_array($path, ['/admin', '/admin/choose-tenant'], true);
|
||||
return in_array($path, ['/admin', '/admin/choose-environment'], true);
|
||||
}
|
||||
|
||||
private function requestHasExplicitTenantContext(Request $request): bool
|
||||
|
||||
@ -69,9 +69,9 @@ public function handle(OperationRunService $runs): void
|
||||
$chunkSize = max(1, $chunkSize);
|
||||
|
||||
foreach (array_chunk($ids, $chunkSize) as $chunk) {
|
||||
foreach ($chunk as $targetTenantId) {
|
||||
foreach ($chunk as $targetEnvironmentId) {
|
||||
dispatch(new TenantSyncWorkerJob(
|
||||
tenantId: $targetTenantId,
|
||||
tenantId: $targetEnvironmentId,
|
||||
userId: $this->userId,
|
||||
operationRun: $this->operationRun,
|
||||
context: $this->context,
|
||||
|
||||
@ -100,7 +100,7 @@ public function handle(
|
||||
|
||||
$context = is_array($this->operationRun->context) ? $this->operationRun->context : [];
|
||||
$profileId = (int) ($context['baseline_profile_id'] ?? 0);
|
||||
$sourceTenantId = (int) ($context['source_tenant_id'] ?? 0);
|
||||
$sourceEnvironmentId = (int) ($context['source_environment_id'] ?? 0);
|
||||
|
||||
$profile = BaselineProfile::query()->find($profileId);
|
||||
|
||||
@ -108,10 +108,10 @@ public function handle(
|
||||
throw new RuntimeException("BaselineProfile #{$profileId} not found.");
|
||||
}
|
||||
|
||||
$sourceTenant = ManagedEnvironment::query()->find($sourceTenantId);
|
||||
$sourceTenant = ManagedEnvironment::query()->find($sourceEnvironmentId);
|
||||
|
||||
if (! $sourceTenant instanceof ManagedEnvironment) {
|
||||
throw new RuntimeException("Source ManagedEnvironment #{$sourceTenantId} not found.");
|
||||
throw new RuntimeException("Source ManagedEnvironment #{$sourceEnvironmentId} not found.");
|
||||
}
|
||||
|
||||
$initiator = $this->operationRun->user_id
|
||||
|
||||
@ -6,17 +6,17 @@
|
||||
|
||||
use App\Jobs\Concerns\BridgesFailedOperationRun;
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Services\OperationRunService;
|
||||
use App\Services\TenantReviews\TenantReviewService;
|
||||
use App\Services\EnvironmentReviews\EnvironmentReviewService;
|
||||
use App\Support\OperationRunOutcome;
|
||||
use App\Support\OperationRunStatus;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Queue\Queueable;
|
||||
use Throwable;
|
||||
|
||||
class ComposeTenantReviewJob implements ShouldQueue
|
||||
class ComposeEnvironmentReviewJob implements ShouldQueue
|
||||
{
|
||||
use BridgesFailedOperationRun;
|
||||
use Queueable;
|
||||
@ -26,21 +26,21 @@ class ComposeTenantReviewJob implements ShouldQueue
|
||||
public bool $failOnTimeout = true;
|
||||
|
||||
public function __construct(
|
||||
public int $tenantReviewId,
|
||||
public int $environmentReviewId,
|
||||
public int $operationRunId,
|
||||
) {}
|
||||
|
||||
public function handle(TenantReviewService $service, OperationRunService $operationRuns): void
|
||||
public function handle(EnvironmentReviewService $service, OperationRunService $operationRuns): void
|
||||
{
|
||||
$review = TenantReview::query()->with(['tenant', 'evidenceSnapshot.items'])->find($this->tenantReviewId);
|
||||
$review = EnvironmentReview::query()->with(['tenant', 'evidenceSnapshot.items'])->find($this->environmentReviewId);
|
||||
$operationRun = OperationRun::query()->find($this->operationRunId);
|
||||
|
||||
if (! $review instanceof TenantReview || ! $operationRun instanceof OperationRun || ! $review->tenant) {
|
||||
if (! $review instanceof EnvironmentReview || ! $operationRun instanceof OperationRun || ! $review->tenant) {
|
||||
return;
|
||||
}
|
||||
|
||||
$operationRuns->updateRun($operationRun, OperationRunStatus::Running->value, OperationRunOutcome::Pending->value);
|
||||
$review->update(['status' => TenantReviewStatus::Draft->value]);
|
||||
$review->update(['status' => EnvironmentReviewStatus::Draft->value]);
|
||||
|
||||
try {
|
||||
$review = $service->compose($review);
|
||||
@ -61,7 +61,7 @@ public function handle(TenantReviewService $service, OperationRunService $operat
|
||||
);
|
||||
} catch (Throwable $throwable) {
|
||||
$review->update([
|
||||
'status' => TenantReviewStatus::Failed->value,
|
||||
'status' => EnvironmentReviewStatus::Failed->value,
|
||||
'summary' => array_merge(is_array($review->summary) ? $review->summary : [], [
|
||||
'error' => $throwable->getMessage(),
|
||||
]),
|
||||
@ -73,7 +73,7 @@ public function handle(TenantReviewService $service, OperationRunService $operat
|
||||
outcome: OperationRunOutcome::Failed->value,
|
||||
failures: [
|
||||
[
|
||||
'code' => 'tenant_review_compose.failed',
|
||||
'code' => 'environment_review_compose.failed',
|
||||
'message' => $throwable->getMessage(),
|
||||
],
|
||||
],
|
||||
@ -9,7 +9,7 @@
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Services\Intune\SecretClassificationService;
|
||||
use App\Services\OperationRunService;
|
||||
use App\Services\ReviewPackService;
|
||||
@ -39,7 +39,7 @@ public function __construct(
|
||||
|
||||
public function handle(OperationRunService $operationRunService): void
|
||||
{
|
||||
$reviewPack = ReviewPack::query()->with(['tenant', 'evidenceSnapshot.items', 'tenantReview.sections'])->find($this->reviewPackId);
|
||||
$reviewPack = ReviewPack::query()->with(['tenant', 'evidenceSnapshot.items', 'environmentReview.sections'])->find($this->reviewPackId);
|
||||
$operationRun = OperationRun::query()->find($this->operationRunId);
|
||||
|
||||
if (! $reviewPack instanceof ReviewPack || ! $operationRun instanceof OperationRun) {
|
||||
@ -82,9 +82,9 @@ public function handle(OperationRunService $operationRunService): void
|
||||
|
||||
private function executeGeneration(ReviewPack $reviewPack, OperationRun $operationRun, ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, OperationRunService $operationRunService): void
|
||||
{
|
||||
$review = $reviewPack->tenantReview;
|
||||
$review = $reviewPack->environmentReview;
|
||||
|
||||
if ($review instanceof TenantReview) {
|
||||
if ($review instanceof EnvironmentReview) {
|
||||
$this->executeReviewDerivedGeneration($reviewPack, $review, $operationRun, $tenant, $snapshot, $operationRunService);
|
||||
|
||||
return;
|
||||
@ -216,7 +216,7 @@ private function executeGeneration(ReviewPack $reviewPack, OperationRun $operati
|
||||
|
||||
private function executeReviewDerivedGeneration(
|
||||
ReviewPack $reviewPack,
|
||||
TenantReview $review,
|
||||
EnvironmentReview $review,
|
||||
OperationRun $operationRun,
|
||||
ManagedEnvironment $tenant,
|
||||
EvidenceSnapshot $snapshot,
|
||||
@ -280,7 +280,7 @@ private function executeReviewDerivedGeneration(
|
||||
$fingerprint = app(ReviewPackService::class)->computeFingerprintForReview($review, $options);
|
||||
$reviewSummary = is_array($review->summary) ? $review->summary : [];
|
||||
$summary = [
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'review_status' => (string) $review->status,
|
||||
'review_completeness_state' => (string) $review->completeness_state,
|
||||
'section_count' => $review->sections->count(),
|
||||
@ -636,7 +636,7 @@ private function assembleZip(string $tempFile, array $fileMap, ?callable $afterW
|
||||
*/
|
||||
private function buildReviewDerivedFileMap(
|
||||
ReviewPack $reviewPack,
|
||||
TenantReview $review,
|
||||
EnvironmentReview $review,
|
||||
ManagedEnvironment $tenant,
|
||||
EvidenceSnapshot $snapshot,
|
||||
bool $includePii,
|
||||
@ -662,7 +662,7 @@ private function buildReviewDerivedFileMap(
|
||||
'tenant_name' => $includePii ? $tenant->name : '[REDACTED]',
|
||||
'generated_at' => $generatedAt->toIso8601String(),
|
||||
'delivery_bundle' => $deliveryMetadata,
|
||||
'tenant_review' => [
|
||||
'environment_review' => [
|
||||
'id' => (int) $review->getKey(),
|
||||
'status' => (string) $review->status,
|
||||
'completeness_state' => (string) $review->completeness_state,
|
||||
@ -686,7 +686,7 @@ private function buildReviewDerivedFileMap(
|
||||
], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR),
|
||||
'summary.json' => json_encode($this->redactReportPayload(array_merge(
|
||||
[
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'review_status' => (string) $review->status,
|
||||
'review_completeness_state' => (string) $review->completeness_state,
|
||||
],
|
||||
@ -738,7 +738,7 @@ private function buildReviewDerivedFileMap(
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function deliveryBundleSummary(TenantReview $review): array
|
||||
private function deliveryBundleSummary(EnvironmentReview $review): array
|
||||
{
|
||||
return [
|
||||
'contract' => ReviewPackService::REVIEW_DERIVED_DELIVERY_CONTRACT,
|
||||
@ -753,7 +753,7 @@ private function deliveryBundleSummary(TenantReview $review): array
|
||||
*/
|
||||
private function deliveryBundleMetadata(
|
||||
ReviewPack $reviewPack,
|
||||
TenantReview $review,
|
||||
EnvironmentReview $review,
|
||||
EvidenceSnapshot $snapshot,
|
||||
\Carbon\CarbonInterface $generatedAt,
|
||||
): array {
|
||||
@ -805,7 +805,7 @@ private function deliveryBundleMetadata(
|
||||
* @param array<string, mixed> $reviewSummary
|
||||
*/
|
||||
private function buildExecutiveEntrypoint(
|
||||
TenantReview $review,
|
||||
EnvironmentReview $review,
|
||||
ManagedEnvironment $tenant,
|
||||
EvidenceSnapshot $snapshot,
|
||||
array $reviewSummary,
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class CrossTenantPromotionExecutionJob implements ShouldQueue
|
||||
final class CrossEnvironmentPromotionExecutionJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
@ -73,16 +73,16 @@ public function handle(
|
||||
return;
|
||||
}
|
||||
|
||||
$tenant = $this->operationRun->tenant;
|
||||
$targetEnvironment = $this->operationRun->tenant;
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
throw new RuntimeException('Promotion execution target tenant is missing.');
|
||||
if (! $targetEnvironment instanceof ManagedEnvironment) {
|
||||
throw new RuntimeException('Promotion execution target environment is missing.');
|
||||
}
|
||||
|
||||
$context = is_array($this->operationRun->context) ? $this->operationRun->context : [];
|
||||
$targetScope = is_array($context['target_scope'] ?? null) ? $context['target_scope'] : [];
|
||||
|
||||
$lock = $limiter->acquireSlot((int) $tenant->getKey(), $targetScope);
|
||||
$lock = $limiter->acquireSlot((int) $targetEnvironment->getKey(), $targetScope);
|
||||
|
||||
if (! $lock) {
|
||||
$this->release(max(1, (int) config('tenantpilot.bulk_operations.poll_interval_seconds', 3)));
|
||||
@ -114,7 +114,7 @@ public function handle(
|
||||
$summary['total'] = count($items);
|
||||
|
||||
[$backupSet, $selectedItemIds, $preRestoreSummary, $preRestoreFailures] = $this->buildRestoreInputs(
|
||||
tenant: $tenant,
|
||||
targetEnvironment: $targetEnvironment,
|
||||
operationRun: $this->operationRun,
|
||||
items: $items,
|
||||
);
|
||||
@ -126,7 +126,7 @@ public function handle(
|
||||
|
||||
if ($selectedItemIds !== []) {
|
||||
$restoreRun = RestoreRun::withoutEvents(fn (): RestoreRun => $restoreService->execute(
|
||||
tenant: $tenant,
|
||||
tenant: $targetEnvironment,
|
||||
backupSet: $backupSet,
|
||||
selectedItemIds: $selectedItemIds,
|
||||
dryRun: false,
|
||||
@ -160,10 +160,10 @@ public function handle(
|
||||
failures: $failures,
|
||||
);
|
||||
|
||||
$auditLogger->logCrossTenantPromotionExecutionCompleted(
|
||||
$auditLogger->logCrossEnvironmentPromotionExecutionCompleted(
|
||||
operationRun: $updated,
|
||||
sourceTenantId: is_numeric($context['source_tenant_id'] ?? null) ? (int) $context['source_tenant_id'] : null,
|
||||
targetTenant: $tenant,
|
||||
sourceEnvironmentId: is_numeric($context['source_environment_id'] ?? null) ? (int) $context['source_environment_id'] : null,
|
||||
targetEnvironment: $targetEnvironment,
|
||||
summaryCounts: $summary,
|
||||
restoreRun: $restoreRun,
|
||||
);
|
||||
@ -183,7 +183,7 @@ public function getOperationRun(): ?OperationRun
|
||||
* @param list<array<string, mixed>> $items
|
||||
* @return array{0: ?BackupSet, 1: list<int>, 2: array<string, int>, 3: list<array{code: string, message: string}>}
|
||||
*/
|
||||
private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $operationRun, array $items): array
|
||||
private function buildRestoreInputs(ManagedEnvironment $targetEnvironment, OperationRun $operationRun, array $items): array
|
||||
{
|
||||
$summary = [
|
||||
'processed' => 0,
|
||||
@ -195,14 +195,14 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
|
||||
];
|
||||
$failures = [];
|
||||
$backupSet = BackupSet::query()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'name' => 'Cross-tenant promotion • Operation #'.$operationRun->getKey(),
|
||||
'managed_environment_id' => (int) $targetEnvironment->getKey(),
|
||||
'name' => 'Cross-environment promotion • Operation #'.$operationRun->getKey(),
|
||||
'created_by' => $operationRun->user?->email,
|
||||
'status' => 'completed',
|
||||
'item_count' => 0,
|
||||
'completed_at' => CarbonImmutable::now(),
|
||||
'metadata' => [
|
||||
'source' => 'cross_tenant_promotion',
|
||||
'source' => 'cross_environment_promotion',
|
||||
'operation_run_id' => (int) $operationRun->getKey(),
|
||||
],
|
||||
]);
|
||||
@ -219,13 +219,13 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
|
||||
}
|
||||
|
||||
$versionId = data_get($item, 'source.policy_version_id');
|
||||
$sourceTenantId = data_get($item, 'source.managed_environment_id');
|
||||
$sourceEnvironmentId = data_get($item, 'source.managed_environment_id');
|
||||
|
||||
$version = is_numeric($versionId) && is_numeric($sourceTenantId)
|
||||
$version = is_numeric($versionId) && is_numeric($sourceEnvironmentId)
|
||||
? PolicyVersion::query()
|
||||
->with('policy')
|
||||
->whereKey((int) $versionId)
|
||||
->where('managed_environment_id', (int) $sourceTenantId)
|
||||
->where('managed_environment_id', (int) $sourceEnvironmentId)
|
||||
->first()
|
||||
: null;
|
||||
|
||||
@ -248,13 +248,13 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
|
||||
: (is_string($sourceExternalId) && trim($sourceExternalId) !== '' ? trim($sourceExternalId) : (string) $sourcePolicy->external_id);
|
||||
|
||||
$targetPolicy = Policy::query()
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->where('managed_environment_id', (int) $targetEnvironment->getKey())
|
||||
->where('policy_type', (string) $sourcePolicy->policy_type)
|
||||
->where('external_id', $policyIdentifier)
|
||||
->first();
|
||||
|
||||
$backupItem = BackupItem::query()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'managed_environment_id' => (int) $targetEnvironment->getKey(),
|
||||
'backup_set_id' => (int) $backupSet->getKey(),
|
||||
'policy_id' => $targetPolicy?->getKey(),
|
||||
'policy_identifier' => $policyIdentifier,
|
||||
@ -263,10 +263,10 @@ private function buildRestoreInputs(ManagedEnvironment $tenant, OperationRun $op
|
||||
'captured_at' => $version->captured_at ?? CarbonImmutable::now(),
|
||||
'payload' => is_array($version->snapshot) ? $version->snapshot : [],
|
||||
'metadata' => [
|
||||
'source' => 'cross_tenant_promotion',
|
||||
'source' => 'cross_environment_promotion',
|
||||
'display_name' => (string) $sourcePolicy->display_name,
|
||||
'operation_run_id' => (int) $operationRun->getKey(),
|
||||
'source_tenant_id' => (int) $sourcePolicy->managed_environment_id,
|
||||
'source_environment_id' => (int) $sourcePolicy->managed_environment_id,
|
||||
'source_policy_id' => (int) $sourcePolicy->getKey(),
|
||||
'source_policy_version_id' => (int) $version->getKey(),
|
||||
'source_subject_key' => (string) ($item['subject_key'] ?? ''),
|
||||
@ -10,7 +10,7 @@
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\Intune\AuditLogger as TenantAuditLogger;
|
||||
use App\Services\Intune\TenantPermissionService;
|
||||
use App\Services\Intune\ManagedEnvironmentPermissionService;
|
||||
use App\Services\OperationRunService;
|
||||
use App\Services\Providers\Contracts\HealthResult;
|
||||
use App\Services\Providers\MicrosoftProviderHealthCheck;
|
||||
@ -24,7 +24,7 @@
|
||||
use App\Support\Providers\ProviderNextStepsRegistry;
|
||||
use App\Support\Providers\ProviderReasonCodes;
|
||||
use App\Support\Providers\TargetScope\ProviderConnectionTargetScopeNormalizer;
|
||||
use App\Support\Verification\TenantPermissionCheckClusters;
|
||||
use App\Support\Verification\ManagedEnvironmentPermissionCheckClusters;
|
||||
use App\Support\Verification\VerificationReportWriter;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -99,7 +99,7 @@ public function handle(
|
||||
|
||||
$this->updateRunTargetScope($this->operationRun, $connection, $entraTenantName);
|
||||
|
||||
$permissionService = app(TenantPermissionService::class);
|
||||
$permissionService = app(ManagedEnvironmentPermissionService::class);
|
||||
|
||||
$graphOptions = null;
|
||||
|
||||
@ -178,7 +178,7 @@ public function handle(
|
||||
];
|
||||
}
|
||||
|
||||
$permissionChecks = TenantPermissionCheckClusters::buildChecks($tenant, $permissionRows, $inventory);
|
||||
$permissionChecks = ManagedEnvironmentPermissionCheckClusters::buildChecks($tenant, $permissionRows, $inventory);
|
||||
$targetScope = app(ProviderConnectionTargetScopeNormalizer::class)
|
||||
->descriptorForConnection($connection)
|
||||
->toArray();
|
||||
|
||||
@ -6,15 +6,15 @@
|
||||
|
||||
use App\Support\Concerns\DerivesWorkspaceIdFromTenant;
|
||||
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class TenantReview extends Model
|
||||
class EnvironmentReview extends Model
|
||||
{
|
||||
use DerivesWorkspaceIdFromTenant;
|
||||
use HasFactory;
|
||||
@ -107,11 +107,11 @@ public function supersededReviews(): HasMany
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<TenantReviewSection, $this>
|
||||
* @return HasMany<EnvironmentReviewSection, $this>
|
||||
*/
|
||||
public function sections(): HasMany
|
||||
{
|
||||
return $this->hasMany(TenantReviewSection::class)->orderBy('sort_order')->orderBy('id');
|
||||
return $this->hasMany(EnvironmentReviewSection::class)->orderBy('sort_order')->orderBy('id');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,7 +146,7 @@ public function scopeForWorkspace(Builder $query, int $workspaceId): Builder
|
||||
*/
|
||||
public function scopePublished(Builder $query): Builder
|
||||
{
|
||||
return $query->where('status', TenantReviewStatus::Published->value);
|
||||
return $query->where('status', EnvironmentReviewStatus::Published->value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -156,21 +156,21 @@ public function scopePublished(Builder $query): Builder
|
||||
public function scopeMutable(Builder $query): Builder
|
||||
{
|
||||
return $query->whereIn('status', [
|
||||
TenantReviewStatus::Draft->value,
|
||||
TenantReviewStatus::Ready->value,
|
||||
TenantReviewStatus::Failed->value,
|
||||
EnvironmentReviewStatus::Draft->value,
|
||||
EnvironmentReviewStatus::Ready->value,
|
||||
EnvironmentReviewStatus::Failed->value,
|
||||
]);
|
||||
}
|
||||
|
||||
public function statusEnum(): TenantReviewStatus
|
||||
public function statusEnum(): EnvironmentReviewStatus
|
||||
{
|
||||
return TenantReviewStatus::from((string) $this->status);
|
||||
return EnvironmentReviewStatus::from((string) $this->status);
|
||||
}
|
||||
|
||||
public function completenessEnum(): TenantReviewCompletenessState
|
||||
public function completenessEnum(): EnvironmentReviewCompletenessState
|
||||
{
|
||||
return TenantReviewCompletenessState::tryFrom((string) $this->completeness_state)
|
||||
?? TenantReviewCompletenessState::Missing;
|
||||
return EnvironmentReviewCompletenessState::tryFrom((string) $this->completeness_state)
|
||||
?? EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
public function isPublished(): bool
|
||||
@ -253,12 +253,12 @@ public function controlInterpretationLimitationCounts(): array
|
||||
->all();
|
||||
}
|
||||
|
||||
public function controlInterpretationSection(): ?TenantReviewSection
|
||||
public function controlInterpretationSection(): ?EnvironmentReviewSection
|
||||
{
|
||||
if ($this->relationLoaded('sections')) {
|
||||
$section = $this->sections->firstWhere('section_key', ComplianceEvidenceMappingV1::SECTION_KEY);
|
||||
|
||||
return $section instanceof TenantReviewSection ? $section : null;
|
||||
return $section instanceof EnvironmentReviewSection ? $section : null;
|
||||
}
|
||||
|
||||
return $this->sections()
|
||||
@ -4,14 +4,14 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TenantReviewSection extends Model
|
||||
class EnvironmentReviewSection extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
@ -31,11 +31,11 @@ protected function casts(): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<TenantReview, $this>
|
||||
* @return BelongsTo<EnvironmentReview, $this>
|
||||
*/
|
||||
public function tenantReview(): BelongsTo
|
||||
public function environmentReview(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TenantReview::class);
|
||||
return $this->belongsTo(EnvironmentReview::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,10 +63,10 @@ public function scopeRequired(Builder $query): Builder
|
||||
return $query->where('required', true);
|
||||
}
|
||||
|
||||
public function completenessEnum(): TenantReviewCompletenessState
|
||||
public function completenessEnum(): EnvironmentReviewCompletenessState
|
||||
{
|
||||
return TenantReviewCompletenessState::tryFrom((string) $this->completeness_state)
|
||||
?? TenantReviewCompletenessState::Missing;
|
||||
return EnvironmentReviewCompletenessState::tryFrom((string) $this->completeness_state)
|
||||
?? EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
public function isControlInterpretation(): bool
|
||||
@ -81,11 +81,11 @@ public function reviewPacks(): HasMany
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HasMany<TenantReview, $this>
|
||||
* @return HasMany<EnvironmentReview, $this>
|
||||
*/
|
||||
public function tenantReviews(): HasMany
|
||||
public function environmentReviews(): HasMany
|
||||
{
|
||||
return $this->hasMany(TenantReview::class);
|
||||
return $this->hasMany(EnvironmentReview::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -411,9 +411,9 @@ public function evidenceSnapshots(): HasMany
|
||||
return $this->hasMany(EvidenceSnapshot::class);
|
||||
}
|
||||
|
||||
public function tenantReviews(): HasMany
|
||||
public function environmentReviews(): HasMany
|
||||
{
|
||||
return $this->hasMany(TenantReview::class);
|
||||
return $this->hasMany(EnvironmentReview::class);
|
||||
}
|
||||
|
||||
public function settings(): HasMany
|
||||
@ -423,7 +423,7 @@ public function settings(): HasMany
|
||||
|
||||
public function permissions(): HasMany
|
||||
{
|
||||
return $this->hasMany(TenantPermission::class);
|
||||
return $this->hasMany(ManagedEnvironmentPermission::class);
|
||||
}
|
||||
|
||||
public function providerConnections(): HasMany
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TenantOnboardingSession extends Model
|
||||
class ManagedEnvironmentOnboardingSession extends Model
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\TenantOnboardingSessionFactory> */
|
||||
/** @use HasFactory<\Database\Factories\ManagedEnvironmentOnboardingSessionFactory> */
|
||||
use HasFactory;
|
||||
|
||||
protected $table = 'managed_tenant_onboarding_sessions';
|
||||
protected $table = 'managed_environment_onboarding_sessions';
|
||||
|
||||
/**
|
||||
* @var array<int, string>
|
||||
@ -25,7 +25,7 @@ class TenantOnboardingSession extends Model
|
||||
public const STATE_ALLOWED_KEYS = [
|
||||
'entra_tenant_id',
|
||||
'managed_environment_id',
|
||||
'tenant_name',
|
||||
'environment_name',
|
||||
'environment',
|
||||
'primary_domain',
|
||||
'notes',
|
||||
@ -80,11 +80,19 @@ public function workspace(): BelongsTo
|
||||
/**
|
||||
* @return BelongsTo<ManagedEnvironment, $this>
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
public function managedEnvironment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ManagedEnvironment::class, 'managed_environment_id')->withTrashed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<ManagedEnvironment, $this>
|
||||
*/
|
||||
public function tenant(): BelongsTo
|
||||
{
|
||||
return $this->managedEnvironment();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<User, $this>
|
||||
*/
|
||||
@ -7,7 +7,7 @@
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TenantPermission extends Model
|
||||
class ManagedEnvironmentPermission extends Model
|
||||
{
|
||||
use DerivesWorkspaceIdFromTenant;
|
||||
use HasFactory;
|
||||
@ -10,7 +10,7 @@
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class TenantTriageReview extends Model
|
||||
class ManagedEnvironmentTriageReview extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
@ -80,11 +80,11 @@ public function evidenceSnapshot(): BelongsTo
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BelongsTo<TenantReview, $this>
|
||||
* @return BelongsTo<EnvironmentReview, $this>
|
||||
*/
|
||||
public function tenantReview(): BelongsTo
|
||||
public function environmentReview(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(TenantReview::class);
|
||||
return $this->belongsTo(EnvironmentReview::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Support\Auth\Capabilities;
|
||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class TenantReviewPolicy
|
||||
class EnvironmentReviewPolicy
|
||||
{
|
||||
use HandlesAuthorization;
|
||||
|
||||
@ -24,10 +24,10 @@ public function viewAny(User $user): bool
|
||||
return false;
|
||||
}
|
||||
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW);
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW);
|
||||
}
|
||||
|
||||
public function view(User $user, TenantReview $review): Response|bool
|
||||
public function view(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
$tenant = $this->authorizedTenantOrNull($user, $review);
|
||||
|
||||
@ -35,7 +35,7 @@ public function view(User $user, TenantReview $review): Response|bool
|
||||
return Response::denyAsNotFound();
|
||||
}
|
||||
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW)
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW)
|
||||
? true
|
||||
: Response::deny();
|
||||
}
|
||||
@ -48,35 +48,35 @@ public function create(User $user): bool
|
||||
return false;
|
||||
}
|
||||
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_MANAGE);
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_MANAGE);
|
||||
}
|
||||
|
||||
public function refresh(User $user, TenantReview $review): Response|bool
|
||||
public function refresh(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
return $this->authorizeManageAction($user, $review);
|
||||
}
|
||||
|
||||
public function publish(User $user, TenantReview $review): Response|bool
|
||||
public function publish(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
return $this->authorizeManageAction($user, $review);
|
||||
}
|
||||
|
||||
public function archive(User $user, TenantReview $review): Response|bool
|
||||
public function archive(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
return $this->authorizeManageAction($user, $review);
|
||||
}
|
||||
|
||||
public function export(User $user, TenantReview $review): Response|bool
|
||||
public function export(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
return $this->authorizeManageAction($user, $review);
|
||||
}
|
||||
|
||||
public function createNextReview(User $user, TenantReview $review): Response|bool
|
||||
public function createNextReview(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
return $this->authorizeManageAction($user, $review);
|
||||
}
|
||||
|
||||
private function authorizeManageAction(User $user, TenantReview $review): Response|bool
|
||||
private function authorizeManageAction(User $user, EnvironmentReview $review): Response|bool
|
||||
{
|
||||
$tenant = $this->authorizedTenantOrNull($user, $review);
|
||||
|
||||
@ -84,12 +84,12 @@ private function authorizeManageAction(User $user, TenantReview $review): Respon
|
||||
return Response::denyAsNotFound();
|
||||
}
|
||||
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::TENANT_REVIEW_MANAGE)
|
||||
return app(CapabilityResolver::class)->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_MANAGE)
|
||||
? true
|
||||
: Response::deny();
|
||||
}
|
||||
|
||||
private function authorizedTenantOrNull(User $user, TenantReview $review): ?ManagedEnvironment
|
||||
private function authorizedTenantOrNull(User $user, EnvironmentReview $review): ?ManagedEnvironment
|
||||
{
|
||||
$tenant = $review->tenant;
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||
@ -17,7 +17,7 @@
|
||||
use Illuminate\Auth\Access\Response;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
class TenantOnboardingSessionPolicy
|
||||
class ManagedEnvironmentOnboardingSessionPolicy
|
||||
{
|
||||
public function viewAny(User $user): bool|Response
|
||||
{
|
||||
@ -27,33 +27,33 @@ public function viewAny(User $user): bool|Response
|
||||
return Response::denyAsNotFound();
|
||||
}
|
||||
|
||||
return $this->authorizeForWorkspace($user, $workspace, Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD);
|
||||
return $this->authorizeForWorkspace($user, $workspace, Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD);
|
||||
}
|
||||
|
||||
public function view(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
|
||||
public function view(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
|
||||
{
|
||||
return $this->authorizeForDraft(
|
||||
user: $user,
|
||||
tenantOnboardingSession: $tenantOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
|
||||
environmentOnboardingSession: $environmentOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
|
||||
);
|
||||
}
|
||||
|
||||
public function update(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
|
||||
public function update(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
|
||||
{
|
||||
return $this->authorizeForDraft(
|
||||
user: $user,
|
||||
tenantOnboardingSession: $tenantOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
|
||||
environmentOnboardingSession: $environmentOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
|
||||
);
|
||||
}
|
||||
|
||||
public function cancel(User $user, TenantOnboardingSession $tenantOnboardingSession): bool|Response
|
||||
public function cancel(User $user, ManagedEnvironmentOnboardingSession $environmentOnboardingSession): bool|Response
|
||||
{
|
||||
return $this->authorizeForDraft(
|
||||
user: $user,
|
||||
tenantOnboardingSession: $tenantOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
|
||||
environmentOnboardingSession: $environmentOnboardingSession,
|
||||
capability: Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
|
||||
);
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ private function currentWorkspace(User $user): ?Workspace
|
||||
|
||||
private function authorizeForDraft(
|
||||
User $user,
|
||||
TenantOnboardingSession $tenantOnboardingSession,
|
||||
ManagedEnvironmentOnboardingSession $environmentOnboardingSession,
|
||||
string $capability,
|
||||
): bool|Response {
|
||||
$workspace = $this->currentWorkspace($user);
|
||||
@ -92,11 +92,11 @@ private function authorizeForDraft(
|
||||
return Response::denyAsNotFound();
|
||||
}
|
||||
|
||||
if ((int) $tenantOnboardingSession->workspace_id !== (int) $workspace->getKey()) {
|
||||
if ((int) $environmentOnboardingSession->workspace_id !== (int) $workspace->getKey()) {
|
||||
return Response::denyAsNotFound();
|
||||
}
|
||||
|
||||
$tenant = $tenantOnboardingSession->tenant;
|
||||
$tenant = $environmentOnboardingSession->managedEnvironment;
|
||||
|
||||
if ($tenant instanceof ManagedEnvironment) {
|
||||
if ((int) $tenant->workspace_id !== (int) $workspace->getKey()) {
|
||||
@ -140,7 +140,7 @@ private function authorizeForWorkspace(User $user, Workspace $workspace, string
|
||||
private function forbiddenCapabilityMessage(string $capability): string
|
||||
{
|
||||
return match ($capability) {
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL => 'You do not have permission to cancel this onboarding draft.',
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL => 'You do not have permission to cancel this onboarding draft.',
|
||||
default => 'You do not have permission to continue this onboarding draft.',
|
||||
};
|
||||
}
|
||||
@ -8,8 +8,8 @@
|
||||
use App\Models\PlatformUser;
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Models\WorkspaceSetting;
|
||||
@ -17,8 +17,8 @@
|
||||
use App\Policies\AlertDestinationPolicy;
|
||||
use App\Policies\AlertRulePolicy;
|
||||
use App\Policies\ProviderConnectionPolicy;
|
||||
use App\Policies\TenantOnboardingSessionPolicy;
|
||||
use App\Policies\TenantReviewPolicy;
|
||||
use App\Policies\ManagedEnvironmentOnboardingSessionPolicy;
|
||||
use App\Policies\EnvironmentReviewPolicy;
|
||||
use App\Policies\WorkspaceSettingPolicy;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||
@ -31,8 +31,8 @@ class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
protected $policies = [
|
||||
ProviderConnection::class => ProviderConnectionPolicy::class,
|
||||
TenantOnboardingSession::class => TenantOnboardingSessionPolicy::class,
|
||||
TenantReview::class => TenantReviewPolicy::class,
|
||||
ManagedEnvironmentOnboardingSession::class => ManagedEnvironmentOnboardingSessionPolicy::class,
|
||||
EnvironmentReview::class => EnvironmentReviewPolicy::class,
|
||||
WorkspaceSetting::class => WorkspaceSettingPolicy::class,
|
||||
AlertDestination::class => AlertDestinationPolicy::class,
|
||||
AlertDelivery::class => AlertDeliveryPolicy::class,
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
|
||||
use App\Filament\Pages\Auth\Login;
|
||||
use App\Filament\Pages\BaselineCompareLanding;
|
||||
use App\Filament\Pages\ChooseTenant;
|
||||
use App\Filament\Pages\ChooseEnvironment;
|
||||
use App\Filament\Pages\ChooseWorkspace;
|
||||
use App\Filament\Pages\CrossTenantComparePage;
|
||||
use App\Filament\Pages\CrossEnvironmentComparePage;
|
||||
use App\Filament\Pages\Findings\FindingsHygieneReport;
|
||||
use App\Filament\Pages\Findings\FindingsIntakeQueue;
|
||||
use App\Filament\Pages\Governance\DecisionRegister;
|
||||
@ -18,7 +18,7 @@
|
||||
use App\Filament\Pages\Reviews\ReviewRegister;
|
||||
use App\Filament\Pages\Reviews\CustomerReviewWorkspace;
|
||||
use App\Filament\Pages\Settings\WorkspaceSettings;
|
||||
use App\Filament\Pages\TenantRequiredPermissions;
|
||||
use App\Filament\Pages\EnvironmentRequiredPermissions;
|
||||
use App\Filament\Pages\WorkspaceOverview;
|
||||
use App\Filament\Resources\AlertDeliveryResource;
|
||||
use App\Filament\Resources\AlertDestinationResource;
|
||||
@ -28,7 +28,7 @@
|
||||
use App\Filament\Resources\InventoryItemResource;
|
||||
use App\Filament\Resources\PolicyResource;
|
||||
use App\Filament\Resources\ProviderConnectionResource;
|
||||
use App\Filament\Resources\TenantReviewResource;
|
||||
use App\Filament\Resources\EnvironmentReviewResource;
|
||||
use App\Filament\Resources\Workspaces\WorkspaceResource;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
@ -75,7 +75,7 @@ public function panel(Panel $panel): Panel
|
||||
->font(null, provider: LocalFontProvider::class, preload: [])
|
||||
->authenticatedRoutes(function (Panel $panel): void {
|
||||
ChooseWorkspace::registerRoutes($panel);
|
||||
ChooseTenant::registerRoutes($panel);
|
||||
ChooseEnvironment::registerRoutes($panel);
|
||||
NoAccess::registerRoutes($panel);
|
||||
})
|
||||
->colors([
|
||||
@ -166,7 +166,7 @@ public function panel(Panel $panel): Panel
|
||||
)
|
||||
->renderHook(
|
||||
PanelsRenderHook::PAGE_START,
|
||||
fn (): string => request()->routeIs('admin.workspace.managed-tenants.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-tenant')
|
||||
fn (): string => request()->routeIs('admin.workspace.managed-environments.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-environment')
|
||||
? ''
|
||||
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
|
||||
? view('livewire.bulk-operation-progress-wrapper')->render()
|
||||
@ -182,16 +182,16 @@ public function panel(Panel $panel): Panel
|
||||
WorkspaceResource::class,
|
||||
BaselineProfileResource::class,
|
||||
BaselineSnapshotResource::class,
|
||||
TenantReviewResource::class,
|
||||
EnvironmentReviewResource::class,
|
||||
])
|
||||
->discoverClusters(in: app_path('Filament/Clusters'), for: 'App\\Filament\\Clusters')
|
||||
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
|
||||
->pages([
|
||||
BaselineCompareLanding::class,
|
||||
InventoryCoverage::class,
|
||||
TenantRequiredPermissions::class,
|
||||
EnvironmentRequiredPermissions::class,
|
||||
WorkspaceSettings::class,
|
||||
CrossTenantComparePage::class,
|
||||
CrossEnvironmentComparePage::class,
|
||||
GovernanceInbox::class,
|
||||
DecisionRegister::class,
|
||||
FindingsHygieneReport::class,
|
||||
|
||||
@ -143,10 +143,10 @@ public function logSupportDiagnosticsOpened(
|
||||
/**
|
||||
* @param array<string, mixed> $preflight
|
||||
*/
|
||||
public function logCrossTenantPromotionPreflightGenerated(
|
||||
public function logCrossEnvironmentPromotionPreflightGenerated(
|
||||
Workspace $workspace,
|
||||
ManagedEnvironment $sourceTenant,
|
||||
ManagedEnvironment $targetTenant,
|
||||
ManagedEnvironment $sourceEnvironment,
|
||||
ManagedEnvironment $targetEnvironment,
|
||||
array $preflight,
|
||||
User|PlatformUser|null $actor = null,
|
||||
): \App\Models\AuditLog {
|
||||
@ -154,12 +154,12 @@ public function logCrossTenantPromotionPreflightGenerated(
|
||||
|
||||
return $this->log(
|
||||
workspace: $workspace,
|
||||
action: AuditActionId::CrossTenantPromotionPreflightGenerated,
|
||||
action: AuditActionId::CrossEnvironmentPromotionPreflightGenerated,
|
||||
context: [
|
||||
'source_tenant_id' => (int) $sourceTenant->getKey(),
|
||||
'source_tenant_name' => (string) $sourceTenant->name,
|
||||
'target_tenant_id' => (int) $targetTenant->getKey(),
|
||||
'target_tenant_name' => (string) $targetTenant->name,
|
||||
'source_environment_id' => (int) $sourceEnvironment->getKey(),
|
||||
'source_environment_name' => (string) $sourceEnvironment->name,
|
||||
'target_environment_id' => (int) $targetEnvironment->getKey(),
|
||||
'target_environment_name' => (string) $targetEnvironment->name,
|
||||
'ready_count' => (int) ($summary['ready'] ?? 0),
|
||||
'blocked_count' => (int) ($summary['blocked'] ?? 0),
|
||||
'manual_mapping_required_count' => (int) ($summary['manual_mapping_required'] ?? 0),
|
||||
@ -170,20 +170,20 @@ public function logCrossTenantPromotionPreflightGenerated(
|
||||
],
|
||||
actor: $actor,
|
||||
status: 'success',
|
||||
resourceType: 'cross_tenant_promotion_preflight',
|
||||
resourceId: sprintf('%s:%s', $sourceTenant->getKey(), $targetTenant->getKey()),
|
||||
targetLabel: $sourceTenant->name.' -> '.$targetTenant->name,
|
||||
summary: 'Cross-tenant promotion preflight generated for '.$sourceTenant->name.' -> '.$targetTenant->name,
|
||||
resourceType: 'cross_environment_promotion_preflight',
|
||||
resourceId: sprintf('%s:%s', $sourceEnvironment->getKey(), $targetEnvironment->getKey()),
|
||||
targetLabel: $sourceEnvironment->name.' -> '.$targetEnvironment->name,
|
||||
summary: 'Cross-environment promotion preflight generated for '.$sourceEnvironment->name.' -> '.$targetEnvironment->name,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $plan
|
||||
*/
|
||||
public function logCrossTenantPromotionExecutionQueued(
|
||||
public function logCrossEnvironmentPromotionExecutionQueued(
|
||||
Workspace $workspace,
|
||||
ManagedEnvironment $sourceTenant,
|
||||
ManagedEnvironment $targetTenant,
|
||||
ManagedEnvironment $sourceEnvironment,
|
||||
ManagedEnvironment $targetEnvironment,
|
||||
OperationRun $operationRun,
|
||||
array $plan,
|
||||
User|PlatformUser|null $actor = null,
|
||||
@ -192,12 +192,12 @@ public function logCrossTenantPromotionExecutionQueued(
|
||||
|
||||
return $this->log(
|
||||
workspace: $workspace,
|
||||
action: AuditActionId::CrossTenantPromotionExecutionQueued,
|
||||
action: AuditActionId::CrossEnvironmentPromotionExecutionQueued,
|
||||
context: [
|
||||
'source_tenant_id' => (int) $sourceTenant->getKey(),
|
||||
'source_tenant_name' => (string) $sourceTenant->name,
|
||||
'target_tenant_id' => (int) $targetTenant->getKey(),
|
||||
'target_tenant_name' => (string) $targetTenant->name,
|
||||
'source_environment_id' => (int) $sourceEnvironment->getKey(),
|
||||
'source_environment_name' => (string) $sourceEnvironment->name,
|
||||
'target_environment_id' => (int) $targetEnvironment->getKey(),
|
||||
'target_environment_name' => (string) $targetEnvironment->name,
|
||||
'selection' => is_array($plan['selection'] ?? null) ? $plan['selection'] : [],
|
||||
'ready_count' => (int) ($summary['ready'] ?? 0),
|
||||
'excluded_count' => (int) ($summary['excluded'] ?? 0),
|
||||
@ -208,36 +208,36 @@ public function logCrossTenantPromotionExecutionQueued(
|
||||
status: 'queued',
|
||||
resourceType: 'operation_run',
|
||||
resourceId: (string) $operationRun->getKey(),
|
||||
targetLabel: $sourceTenant->name.' -> '.$targetTenant->name,
|
||||
summary: 'Cross-tenant promotion execution queued for '.$sourceTenant->name.' -> '.$targetTenant->name,
|
||||
targetLabel: $sourceEnvironment->name.' -> '.$targetEnvironment->name,
|
||||
summary: 'Cross-environment promotion execution queued for '.$sourceEnvironment->name.' -> '.$targetEnvironment->name,
|
||||
operationRunId: (int) $operationRun->getKey(),
|
||||
tenant: $targetTenant,
|
||||
tenant: $targetEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $summaryCounts
|
||||
*/
|
||||
public function logCrossTenantPromotionExecutionCompleted(
|
||||
public function logCrossEnvironmentPromotionExecutionCompleted(
|
||||
OperationRun $operationRun,
|
||||
?int $sourceTenantId,
|
||||
ManagedEnvironment $targetTenant,
|
||||
?int $sourceEnvironmentId,
|
||||
ManagedEnvironment $targetEnvironment,
|
||||
array $summaryCounts,
|
||||
?RestoreRun $restoreRun = null,
|
||||
): \App\Models\AuditLog {
|
||||
$context = is_array($operationRun->context) ? $operationRun->context : [];
|
||||
$sourceTenantName = is_string($context['source_tenant_name'] ?? null)
|
||||
? (string) $context['source_tenant_name']
|
||||
$sourceEnvironmentName = is_string($context['source_environment_name'] ?? null)
|
||||
? (string) $context['source_environment_name']
|
||||
: null;
|
||||
|
||||
return $this->log(
|
||||
workspace: $targetTenant->workspace,
|
||||
action: AuditActionId::CrossTenantPromotionExecutionCompleted,
|
||||
workspace: $targetEnvironment->workspace,
|
||||
action: AuditActionId::CrossEnvironmentPromotionExecutionCompleted,
|
||||
context: [
|
||||
'source_tenant_id' => $sourceTenantId,
|
||||
'source_tenant_name' => $sourceTenantName,
|
||||
'target_tenant_id' => (int) $targetTenant->getKey(),
|
||||
'target_tenant_name' => (string) $targetTenant->name,
|
||||
'source_environment_id' => $sourceEnvironmentId,
|
||||
'source_environment_name' => $sourceEnvironmentName,
|
||||
'target_environment_id' => (int) $targetEnvironment->getKey(),
|
||||
'target_environment_name' => (string) $targetEnvironment->name,
|
||||
'summary_counts' => $summaryCounts,
|
||||
'restore_run_id' => $restoreRun?->getKey(),
|
||||
'operation_outcome' => (string) $operationRun->outcome,
|
||||
@ -249,10 +249,10 @@ public function logCrossTenantPromotionExecutionCompleted(
|
||||
},
|
||||
resourceType: 'operation_run',
|
||||
resourceId: (string) $operationRun->getKey(),
|
||||
targetLabel: ($sourceTenantName !== null ? $sourceTenantName.' -> ' : '').$targetTenant->name,
|
||||
summary: 'Cross-tenant promotion execution completed for '.(($sourceTenantName !== null ? $sourceTenantName.' -> ' : '')).$targetTenant->name,
|
||||
targetLabel: ($sourceEnvironmentName !== null ? $sourceEnvironmentName.' -> ' : '').$targetEnvironment->name,
|
||||
summary: 'Cross-environment promotion execution completed for '.(($sourceEnvironmentName !== null ? $sourceEnvironmentName.' -> ' : '')).$targetEnvironment->name,
|
||||
operationRunId: (int) $operationRun->getKey(),
|
||||
tenant: $targetTenant,
|
||||
tenant: $targetEnvironment,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TenantDiagnosticsService
|
||||
class ManagedEnvironmentDiagnosticsService
|
||||
{
|
||||
public function __construct(public AuditLogger $auditLogger) {}
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
use DomainException;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class TenantMembershipManager
|
||||
class ManagedEnvironmentMembershipManager
|
||||
{
|
||||
private const string SCOPE_PLACEHOLDER_ROLE = 'readonly';
|
||||
|
||||
@ -55,9 +55,9 @@ class RoleCapabilityMap
|
||||
|
||||
Capabilities::REVIEW_PACK_VIEW,
|
||||
Capabilities::REVIEW_PACK_MANAGE,
|
||||
Capabilities::TENANT_REVIEW_VIEW,
|
||||
Capabilities::TENANT_REVIEW_MANAGE,
|
||||
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::ENVIRONMENT_REVIEW_VIEW,
|
||||
Capabilities::ENVIRONMENT_REVIEW_MANAGE,
|
||||
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::EVIDENCE_VIEW,
|
||||
Capabilities::EVIDENCE_MANAGE,
|
||||
],
|
||||
@ -99,9 +99,9 @@ class RoleCapabilityMap
|
||||
|
||||
Capabilities::REVIEW_PACK_VIEW,
|
||||
Capabilities::REVIEW_PACK_MANAGE,
|
||||
Capabilities::TENANT_REVIEW_VIEW,
|
||||
Capabilities::TENANT_REVIEW_MANAGE,
|
||||
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::ENVIRONMENT_REVIEW_VIEW,
|
||||
Capabilities::ENVIRONMENT_REVIEW_MANAGE,
|
||||
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::EVIDENCE_VIEW,
|
||||
Capabilities::EVIDENCE_MANAGE,
|
||||
],
|
||||
@ -131,8 +131,8 @@ class RoleCapabilityMap
|
||||
Capabilities::PERMISSION_POSTURE_VIEW,
|
||||
|
||||
Capabilities::REVIEW_PACK_VIEW,
|
||||
Capabilities::TENANT_REVIEW_VIEW,
|
||||
Capabilities::TENANT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::ENVIRONMENT_REVIEW_VIEW,
|
||||
Capabilities::MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE,
|
||||
Capabilities::EVIDENCE_VIEW,
|
||||
],
|
||||
|
||||
@ -153,7 +153,7 @@ class RoleCapabilityMap
|
||||
Capabilities::PERMISSION_POSTURE_VIEW,
|
||||
|
||||
Capabilities::REVIEW_PACK_VIEW,
|
||||
Capabilities::TENANT_REVIEW_VIEW,
|
||||
Capabilities::ENVIRONMENT_REVIEW_VIEW,
|
||||
Capabilities::EVIDENCE_VIEW,
|
||||
],
|
||||
];
|
||||
|
||||
@ -23,17 +23,17 @@ class WorkspaceRoleCapabilityMap
|
||||
Capabilities::WORKSPACE_ARCHIVE,
|
||||
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
|
||||
Capabilities::WORKSPACE_MEMBERSHIP_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE_DEDICATED,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_ACTIVATE,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE_DEDICATED,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_ACTIVATE,
|
||||
Capabilities::WORKSPACE_SETTINGS_VIEW,
|
||||
Capabilities::WORKSPACE_SETTINGS_MANAGE,
|
||||
Capabilities::ALERTS_VIEW,
|
||||
@ -48,15 +48,15 @@ class WorkspaceRoleCapabilityMap
|
||||
Capabilities::WORKSPACE_VIEW,
|
||||
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
|
||||
Capabilities::WORKSPACE_MEMBERSHIP_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_SETTINGS_VIEW,
|
||||
Capabilities::WORKSPACE_SETTINGS_MANAGE,
|
||||
Capabilities::ALERTS_VIEW,
|
||||
@ -70,11 +70,11 @@ class WorkspaceRoleCapabilityMap
|
||||
WorkspaceRole::Operator->value => [
|
||||
Capabilities::WORKSPACE_VIEW,
|
||||
Capabilities::WORKSPACE_MEMBERSHIP_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC,
|
||||
Capabilities::WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP,
|
||||
Capabilities::WORKSPACE_SETTINGS_VIEW,
|
||||
Capabilities::ALERTS_VIEW,
|
||||
Capabilities::WORKSPACE_BASELINES_VIEW,
|
||||
|
||||
@ -91,7 +91,7 @@ public function startCapture(
|
||||
],
|
||||
],
|
||||
'baseline_profile_id' => (int) $profile->getKey(),
|
||||
'source_tenant_id' => (int) $sourceTenant->getKey(),
|
||||
'source_environment_id' => (int) $sourceTenant->getKey(),
|
||||
'effective_scope' => $effectiveScope->toEffectiveScopeContext($this->capabilityGuard, 'capture'),
|
||||
'capture_mode' => $captureMode->value,
|
||||
'baseline_capture' => [
|
||||
|
||||
@ -94,7 +94,7 @@ public function resume(OperationRun $priorRun, User $initiator): array
|
||||
|
||||
$newContext = [];
|
||||
|
||||
foreach (['target_scope', 'baseline_profile_id', 'baseline_snapshot_id', 'source_tenant_id', 'effective_scope', 'capture_mode'] as $key) {
|
||||
foreach (['target_scope', 'baseline_profile_id', 'baseline_snapshot_id', 'source_environment_id', 'effective_scope', 'capture_mode'] as $key) {
|
||||
if (array_key_exists($key, $context)) {
|
||||
$newContext[$key] = $context[$key];
|
||||
}
|
||||
|
||||
@ -2,18 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\TenantReview;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
|
||||
final class TenantReviewComposer
|
||||
final class EnvironmentReviewComposer
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantReviewFingerprint $fingerprint,
|
||||
private readonly TenantReviewSectionFactory $sectionFactory,
|
||||
private readonly TenantReviewReadinessGate $readinessGate,
|
||||
private readonly EnvironmentReviewFingerprint $fingerprint,
|
||||
private readonly EnvironmentReviewSectionFactory $sectionFactory,
|
||||
private readonly EnvironmentReviewReadinessGate $readinessGate,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@ -25,7 +25,7 @@ public function __construct(
|
||||
* sections: list<array<string, mixed>>
|
||||
* }
|
||||
*/
|
||||
public function compose(EvidenceSnapshot $snapshot, ?TenantReview $review = null): array
|
||||
public function compose(EvidenceSnapshot $snapshot, ?EnvironmentReview $review = null): array
|
||||
{
|
||||
$tenant = $snapshot->tenant;
|
||||
|
||||
@ -49,8 +49,8 @@ public function compose(EvidenceSnapshot $snapshot, ?TenantReview $review = null
|
||||
$operationsSection = collect($sections)
|
||||
->firstWhere('section_key', 'operations_health');
|
||||
|
||||
if ($review instanceof TenantReview && $review->isPublished()) {
|
||||
$status = TenantReviewStatus::Published;
|
||||
if ($review instanceof EnvironmentReview && $review->isPublished()) {
|
||||
$status = EnvironmentReviewStatus::Published;
|
||||
}
|
||||
|
||||
return [
|
||||
@ -2,13 +2,13 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
final class TenantReviewFingerprint
|
||||
final class EnvironmentReviewFingerprint
|
||||
{
|
||||
public function forSnapshot(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot): string
|
||||
{
|
||||
@ -2,31 +2,31 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use App\Support\Ui\DerivedState\DerivedStateFamily;
|
||||
use App\Support\Ui\DerivedState\RequestScopedDerivedStateStore;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class TenantReviewLifecycleService
|
||||
final class EnvironmentReviewLifecycleService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly TenantReviewReadinessGate $readinessGate,
|
||||
private readonly TenantReviewService $reviewService,
|
||||
private readonly EnvironmentReviewReadinessGate $readinessGate,
|
||||
private readonly EnvironmentReviewService $reviewService,
|
||||
private readonly WorkspaceAuditLogger $auditLogger,
|
||||
private readonly RequestScopedDerivedStateStore $derivedStateStore,
|
||||
) {}
|
||||
|
||||
public function publish(TenantReview $review, User $user, string $reason): TenantReview
|
||||
public function publish(EnvironmentReview $review, User $user, string $reason): EnvironmentReview
|
||||
{
|
||||
$review->loadMissing(['tenant', 'sections', 'currentExportReviewPack']);
|
||||
$tenant = $review->tenant;
|
||||
@ -44,7 +44,7 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
|
||||
}
|
||||
|
||||
$review->forceFill([
|
||||
'status' => TenantReviewStatus::Published->value,
|
||||
'status' => EnvironmentReviewStatus::Published->value,
|
||||
'published_at' => now(),
|
||||
'published_by_user_id' => (int) $user->getKey(),
|
||||
'summary' => array_merge(is_array($review->summary) ? $review->summary : [], [
|
||||
@ -54,17 +54,17 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
|
||||
|
||||
$this->auditLogger->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: AuditActionId::TenantReviewPublished,
|
||||
action: AuditActionId::EnvironmentReviewPublished,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_id' => (int) $review->getKey(),
|
||||
'before_status' => $beforeStatus,
|
||||
'after_status' => TenantReviewStatus::Published->value,
|
||||
'after_status' => EnvironmentReviewStatus::Published->value,
|
||||
'reason' => $reason,
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $review->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
|
||||
tenant: $tenant,
|
||||
@ -75,7 +75,7 @@ public function publish(TenantReview $review, User $user, string $reason): Tenan
|
||||
return $review->refresh()->load(['tenant', 'sections', 'currentExportReviewPack']);
|
||||
}
|
||||
|
||||
public function archive(TenantReview $review, User $user, string $reason): TenantReview
|
||||
public function archive(EnvironmentReview $review, User $user, string $reason): EnvironmentReview
|
||||
{
|
||||
$review->loadMissing('tenant');
|
||||
$tenant = $review->tenant;
|
||||
@ -92,23 +92,23 @@ public function archive(TenantReview $review, User $user, string $reason): Tenan
|
||||
}
|
||||
|
||||
$review->forceFill([
|
||||
'status' => TenantReviewStatus::Archived->value,
|
||||
'status' => EnvironmentReviewStatus::Archived->value,
|
||||
'archived_at' => now(),
|
||||
])->save();
|
||||
|
||||
$this->auditLogger->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: AuditActionId::TenantReviewArchived,
|
||||
action: AuditActionId::EnvironmentReviewArchived,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_id' => (int) $review->getKey(),
|
||||
'before_status' => $beforeStatus,
|
||||
'after_status' => TenantReviewStatus::Archived->value,
|
||||
'after_status' => EnvironmentReviewStatus::Archived->value,
|
||||
'reason' => $reason,
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $review->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
|
||||
tenant: $tenant,
|
||||
@ -119,7 +119,7 @@ public function archive(TenantReview $review, User $user, string $reason): Tenan
|
||||
return $review->refresh()->load(['tenant', 'sections', 'currentExportReviewPack']);
|
||||
}
|
||||
|
||||
public function createNextReview(TenantReview $review, User $user, ?EvidenceSnapshot $snapshot = null): TenantReview
|
||||
public function createNextReview(EnvironmentReview $review, User $user, ?EvidenceSnapshot $snapshot = null): EnvironmentReview
|
||||
{
|
||||
$review->loadMissing(['tenant', 'evidenceSnapshot']);
|
||||
$tenant = $review->tenant;
|
||||
@ -138,29 +138,29 @@ public function createNextReview(TenantReview $review, User $user, ?EvidenceSnap
|
||||
throw new InvalidArgumentException('An eligible evidence snapshot is required to create the next review.');
|
||||
}
|
||||
|
||||
$nextReview = DB::transaction(function () use ($review, $user, $snapshot, $tenant): TenantReview {
|
||||
$nextReview = DB::transaction(function () use ($review, $user, $snapshot, $tenant): EnvironmentReview {
|
||||
$nextReview = $this->reviewService->create($tenant, $snapshot, $user);
|
||||
|
||||
if ((int) $nextReview->getKey() !== (int) $review->getKey()) {
|
||||
$review->forceFill([
|
||||
'status' => TenantReviewStatus::Superseded->value,
|
||||
'status' => EnvironmentReviewStatus::Superseded->value,
|
||||
'superseded_by_review_id' => (int) $nextReview->getKey(),
|
||||
])->save();
|
||||
}
|
||||
|
||||
$this->auditLogger->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: AuditActionId::TenantReviewSuccessorCreated,
|
||||
action: AuditActionId::EnvironmentReviewSuccessorCreated,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_id' => (int) $review->getKey(),
|
||||
'next_review_id' => (int) $nextReview->getKey(),
|
||||
'before_status' => TenantReviewStatus::Published->value,
|
||||
'after_status' => TenantReviewStatus::Superseded->value,
|
||||
'before_status' => EnvironmentReviewStatus::Published->value,
|
||||
'after_status' => EnvironmentReviewStatus::Superseded->value,
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $nextReview->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $nextReview->getKey()),
|
||||
tenant: $tenant,
|
||||
@ -194,9 +194,9 @@ private function validatedReason(mixed $reason, string $field): string
|
||||
return $resolved;
|
||||
}
|
||||
|
||||
private function invalidateArtifactTruthCache(TenantReview $review): void
|
||||
private function invalidateArtifactTruthCache(EnvironmentReview $review): void
|
||||
{
|
||||
$this->derivedStateStore->invalidateModel(DerivedStateFamily::ArtifactTruth, $review, 'tenant_review');
|
||||
$this->derivedStateStore->invalidateModel(DerivedStateFamily::ArtifactTruth, $review, 'environment_review');
|
||||
|
||||
$review->loadMissing('currentExportReviewPack');
|
||||
|
||||
@ -2,14 +2,14 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\TenantReview;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class TenantReviewReadinessGate
|
||||
final class EnvironmentReviewReadinessGate
|
||||
{
|
||||
/**
|
||||
* @param iterable<array<string, mixed>> $sections
|
||||
@ -21,18 +21,18 @@ public function blockersForSections(iterable $sections): array
|
||||
|
||||
foreach ($sections as $section) {
|
||||
$required = (bool) ($section['required'] ?? false);
|
||||
$state = (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value);
|
||||
$state = (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value);
|
||||
$title = (string) ($section['title'] ?? 'Review section');
|
||||
|
||||
if (! $required) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($state === TenantReviewCompletenessState::Missing->value) {
|
||||
if ($state === EnvironmentReviewCompletenessState::Missing->value) {
|
||||
$blockers[] = sprintf('%s is missing.', $title);
|
||||
}
|
||||
|
||||
if ($state === TenantReviewCompletenessState::Stale->value) {
|
||||
if ($state === EnvironmentReviewCompletenessState::Stale->value) {
|
||||
$blockers[] = sprintf('%s is stale and must be refreshed before publication.', $title);
|
||||
}
|
||||
}
|
||||
@ -43,45 +43,45 @@ public function blockersForSections(iterable $sections): array
|
||||
/**
|
||||
* @param iterable<array<string, mixed>> $sections
|
||||
*/
|
||||
public function completenessForSections(iterable $sections): TenantReviewCompletenessState
|
||||
public function completenessForSections(iterable $sections): EnvironmentReviewCompletenessState
|
||||
{
|
||||
$states = collect($sections)
|
||||
->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value))
|
||||
->map(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
|
||||
->values();
|
||||
|
||||
if ($states->isEmpty()) {
|
||||
return TenantReviewCompletenessState::Missing;
|
||||
return EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
if ($states->contains(TenantReviewCompletenessState::Missing->value)) {
|
||||
return TenantReviewCompletenessState::Missing;
|
||||
if ($states->contains(EnvironmentReviewCompletenessState::Missing->value)) {
|
||||
return EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
if ($states->contains(TenantReviewCompletenessState::Stale->value)) {
|
||||
return TenantReviewCompletenessState::Stale;
|
||||
if ($states->contains(EnvironmentReviewCompletenessState::Stale->value)) {
|
||||
return EnvironmentReviewCompletenessState::Stale;
|
||||
}
|
||||
|
||||
if ($states->contains(TenantReviewCompletenessState::Partial->value)) {
|
||||
return TenantReviewCompletenessState::Partial;
|
||||
if ($states->contains(EnvironmentReviewCompletenessState::Partial->value)) {
|
||||
return EnvironmentReviewCompletenessState::Partial;
|
||||
}
|
||||
|
||||
return TenantReviewCompletenessState::Complete;
|
||||
return EnvironmentReviewCompletenessState::Complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param iterable<array<string, mixed>> $sections
|
||||
*/
|
||||
public function statusForSections(iterable $sections): TenantReviewStatus
|
||||
public function statusForSections(iterable $sections): EnvironmentReviewStatus
|
||||
{
|
||||
return $this->blockersForSections($sections) === []
|
||||
? TenantReviewStatus::Ready
|
||||
: TenantReviewStatus::Draft;
|
||||
? EnvironmentReviewStatus::Ready
|
||||
: EnvironmentReviewStatus::Draft;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function blockersForReview(TenantReview $review): array
|
||||
public function blockersForReview(EnvironmentReview $review): array
|
||||
{
|
||||
$sections = $review->relationLoaded('sections')
|
||||
? $review->sections
|
||||
@ -96,7 +96,7 @@ public function blockersForReview(TenantReview $review): array
|
||||
})->all());
|
||||
}
|
||||
|
||||
public function canPublish(TenantReview $review): bool
|
||||
public function canPublish(EnvironmentReview $review): bool
|
||||
{
|
||||
if (! $review->isMutable()) {
|
||||
return false;
|
||||
@ -105,11 +105,11 @@ public function canPublish(TenantReview $review): bool
|
||||
return $this->blockersForReview($review) === [];
|
||||
}
|
||||
|
||||
public function canExport(TenantReview $review): bool
|
||||
public function canExport(EnvironmentReview $review): bool
|
||||
{
|
||||
if (! in_array($review->statusEnum(), [
|
||||
TenantReviewStatus::Ready,
|
||||
TenantReviewStatus::Published,
|
||||
EnvironmentReviewStatus::Ready,
|
||||
EnvironmentReviewStatus::Published,
|
||||
], true)) {
|
||||
return false;
|
||||
}
|
||||
@ -124,14 +124,14 @@ public function canExport(TenantReview $review): bool
|
||||
public function sectionStateCounts(iterable $sections): array
|
||||
{
|
||||
$counts = collect($sections)
|
||||
->groupBy(static fn (array $section): string => (string) ($section['completeness_state'] ?? TenantReviewCompletenessState::Missing->value))
|
||||
->groupBy(static fn (array $section): string => (string) ($section['completeness_state'] ?? EnvironmentReviewCompletenessState::Missing->value))
|
||||
->map(static fn (Collection $group): int => $group->count());
|
||||
|
||||
return [
|
||||
'complete' => (int) ($counts[TenantReviewCompletenessState::Complete->value] ?? 0),
|
||||
'partial' => (int) ($counts[TenantReviewCompletenessState::Partial->value] ?? 0),
|
||||
'missing' => (int) ($counts[TenantReviewCompletenessState::Missing->value] ?? 0),
|
||||
'stale' => (int) ($counts[TenantReviewCompletenessState::Stale->value] ?? 0),
|
||||
'complete' => (int) ($counts[EnvironmentReviewCompletenessState::Complete->value] ?? 0),
|
||||
'partial' => (int) ($counts[EnvironmentReviewCompletenessState::Partial->value] ?? 0),
|
||||
'missing' => (int) ($counts[EnvironmentReviewCompletenessState::Missing->value] ?? 0),
|
||||
'stale' => (int) ($counts[EnvironmentReviewCompletenessState::Stale->value] ?? 0),
|
||||
];
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Models\WorkspaceMembership;
|
||||
@ -15,7 +15,7 @@
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
final class TenantReviewRegisterService
|
||||
final class EnvironmentReviewRegisterService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CapabilityResolver $capabilityResolver,
|
||||
@ -44,7 +44,7 @@ public function authorizedTenants(User $user, Workspace $workspace): array
|
||||
$this->capabilityResolver->primeMemberships($user, $tenants->modelKeys());
|
||||
|
||||
return $tenants
|
||||
->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::TENANT_REVIEW_VIEW))
|
||||
->filter(fn (ManagedEnvironment $tenant): bool => $this->capabilityResolver->can($user, $tenant, Capabilities::ENVIRONMENT_REVIEW_VIEW))
|
||||
->keyBy(static fn (ManagedEnvironment $tenant): int => (int) $tenant->getKey())
|
||||
->all();
|
||||
}
|
||||
@ -53,7 +53,7 @@ public function query(User $user, Workspace $workspace): Builder
|
||||
{
|
||||
$tenantIds = array_keys($this->authorizedTenants($user, $workspace));
|
||||
|
||||
return TenantReview::query()
|
||||
return EnvironmentReview::query()
|
||||
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
|
||||
->forWorkspace((int) $workspace->getKey())
|
||||
->whereIn('managed_environment_id', $tenantIds === [] ? [-1] : $tenantIds)
|
||||
@ -65,12 +65,12 @@ public function latestPublishedQuery(User $user, Workspace $workspace): Builder
|
||||
{
|
||||
$tenantIds = array_keys($this->authorizedTenants($user, $workspace));
|
||||
|
||||
$rankedReviews = TenantReview::query()
|
||||
$rankedReviews = EnvironmentReview::query()
|
||||
->select([
|
||||
'tenant_reviews.id',
|
||||
'tenant_reviews.managed_environment_id',
|
||||
'tenant_reviews.published_at',
|
||||
'tenant_reviews.generated_at',
|
||||
'environment_reviews.id',
|
||||
'environment_reviews.managed_environment_id',
|
||||
'environment_reviews.published_at',
|
||||
'environment_reviews.generated_at',
|
||||
])
|
||||
->selectRaw('ROW_NUMBER() OVER (PARTITION BY managed_environment_id ORDER BY published_at DESC, generated_at DESC, id DESC) as rn')
|
||||
->forWorkspace((int) $workspace->getKey())
|
||||
@ -78,14 +78,14 @@ public function latestPublishedQuery(User $user, Workspace $workspace): Builder
|
||||
->published();
|
||||
|
||||
$latestPublishedIds = DB::query()
|
||||
->fromSub($rankedReviews, 'ranked_tenant_reviews')
|
||||
->fromSub($rankedReviews, 'ranked_environment_reviews')
|
||||
->where('rn', 1)
|
||||
->select('id');
|
||||
|
||||
return TenantReview::query()
|
||||
return EnvironmentReview::query()
|
||||
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
|
||||
->forWorkspace((int) $workspace->getKey())
|
||||
->whereIn('tenant_reviews.id', $latestPublishedIds)
|
||||
->whereIn('environment_reviews.id', $latestPublishedIds)
|
||||
->orderByDesc('published_at')
|
||||
->orderByDesc('generated_at')
|
||||
->orderByDesc('id');
|
||||
@ -98,9 +98,9 @@ public function customerWorkspaceTenantQuery(User $user, Workspace $workspace):
|
||||
return ManagedEnvironment::query()
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->whereIn('id', $tenantIds === [] ? [-1] : $tenantIds)
|
||||
->whereHas('tenantReviews', fn ($query) => $query->published())
|
||||
->whereHas('environmentReviews', fn ($query) => $query->published())
|
||||
->with([
|
||||
'tenantReviews' => fn ($query) => $query
|
||||
'environmentReviews' => fn ($query) => $query
|
||||
->with(['tenant', 'evidenceSnapshot', 'currentExportReviewPack'])
|
||||
->published()
|
||||
->orderByDesc('published_at')
|
||||
@ -2,17 +2,17 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\EvidenceSnapshotItem;
|
||||
use App\Support\Findings\FindingOutcomeSemantics;
|
||||
use App\Support\Governance\Controls\ComplianceEvidenceMappingV1;
|
||||
use App\Support\TenantReviewCompletenessState;
|
||||
use App\Support\EnvironmentReviewCompletenessState;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
final class TenantReviewSectionFactory
|
||||
final class EnvironmentReviewSectionFactory
|
||||
{
|
||||
public function __construct(
|
||||
private readonly FindingOutcomeSemantics $findingOutcomeSemantics,
|
||||
@ -333,10 +333,10 @@ private function summary(?EvidenceSnapshotItem $item): array
|
||||
return is_array($item?->summary_payload) ? $item->summary_payload : [];
|
||||
}
|
||||
|
||||
private function state(?EvidenceSnapshotItem $item): TenantReviewCompletenessState
|
||||
private function state(?EvidenceSnapshotItem $item): EnvironmentReviewCompletenessState
|
||||
{
|
||||
return TenantReviewCompletenessState::tryFrom((string) $item?->state)
|
||||
?? TenantReviewCompletenessState::Missing;
|
||||
return EnvironmentReviewCompletenessState::tryFrom((string) $item?->state)
|
||||
?? EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
private function sourceFingerprint(?EvidenceSnapshotItem $item): ?string
|
||||
@ -361,23 +361,23 @@ private function canonicalControlsFromEntries(array $entries): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantReviewCompletenessState> $states
|
||||
* @param array<int, EnvironmentReviewCompletenessState> $states
|
||||
*/
|
||||
private function maxState(array $states): TenantReviewCompletenessState
|
||||
private function maxState(array $states): EnvironmentReviewCompletenessState
|
||||
{
|
||||
if (in_array(TenantReviewCompletenessState::Missing, $states, true)) {
|
||||
return TenantReviewCompletenessState::Missing;
|
||||
if (in_array(EnvironmentReviewCompletenessState::Missing, $states, true)) {
|
||||
return EnvironmentReviewCompletenessState::Missing;
|
||||
}
|
||||
|
||||
if (in_array(TenantReviewCompletenessState::Stale, $states, true)) {
|
||||
return TenantReviewCompletenessState::Stale;
|
||||
if (in_array(EnvironmentReviewCompletenessState::Stale, $states, true)) {
|
||||
return EnvironmentReviewCompletenessState::Stale;
|
||||
}
|
||||
|
||||
if (in_array(TenantReviewCompletenessState::Partial, $states, true)) {
|
||||
return TenantReviewCompletenessState::Partial;
|
||||
if (in_array(EnvironmentReviewCompletenessState::Partial, $states, true)) {
|
||||
return EnvironmentReviewCompletenessState::Partial;
|
||||
}
|
||||
|
||||
return TenantReviewCompletenessState::Complete;
|
||||
return EnvironmentReviewCompletenessState::Complete;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2,43 +2,43 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\TenantReviews;
|
||||
namespace App\Services\EnvironmentReviews;
|
||||
|
||||
use App\Jobs\ComposeTenantReviewJob;
|
||||
use App\Jobs\ComposeEnvironmentReviewJob;
|
||||
use App\Models\EvidenceSnapshot;
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\OperationRunService;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use App\Support\OperationRunType;
|
||||
use App\Support\TenantReviewStatus;
|
||||
use App\Support\EnvironmentReviewStatus;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final class TenantReviewService
|
||||
final class EnvironmentReviewService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly OperationRunService $operationRuns,
|
||||
private readonly WorkspaceAuditLogger $auditLogger,
|
||||
private readonly TenantReviewComposer $composer,
|
||||
private readonly TenantReviewFingerprint $fingerprint,
|
||||
private readonly EnvironmentReviewComposer $composer,
|
||||
private readonly EnvironmentReviewFingerprint $fingerprint,
|
||||
) {}
|
||||
|
||||
public function create(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, User $user): TenantReview
|
||||
public function create(ManagedEnvironment $tenant, EvidenceSnapshot $snapshot, User $user): EnvironmentReview
|
||||
{
|
||||
return $this->queueComposition(
|
||||
tenant: $tenant,
|
||||
snapshot: $snapshot,
|
||||
user: $user,
|
||||
existingReview: null,
|
||||
auditAction: AuditActionId::TenantReviewCreated,
|
||||
auditAction: AuditActionId::EnvironmentReviewCreated,
|
||||
);
|
||||
}
|
||||
|
||||
public function refresh(TenantReview $review, User $user, ?EvidenceSnapshot $snapshot = null): TenantReview
|
||||
public function refresh(EnvironmentReview $review, User $user, ?EvidenceSnapshot $snapshot = null): EnvironmentReview
|
||||
{
|
||||
$tenant = $review->tenant;
|
||||
|
||||
@ -53,11 +53,11 @@ public function refresh(TenantReview $review, User $user, ?EvidenceSnapshot $sna
|
||||
snapshot: $snapshot,
|
||||
user: $user,
|
||||
existingReview: $review,
|
||||
auditAction: AuditActionId::TenantReviewRefreshed,
|
||||
auditAction: AuditActionId::EnvironmentReviewRefreshed,
|
||||
);
|
||||
}
|
||||
|
||||
public function compose(TenantReview $review): TenantReview
|
||||
public function compose(EnvironmentReview $review): EnvironmentReview
|
||||
{
|
||||
$review->loadMissing(['tenant', 'evidenceSnapshot.items']);
|
||||
|
||||
@ -121,7 +121,7 @@ public function activeCompositionRun(ManagedEnvironment $tenant, ?EvidenceSnapsh
|
||||
|
||||
return $this->operationRuns->findCanonicalRunWithIdentity(
|
||||
tenant: $tenant,
|
||||
type: OperationRunType::TenantReviewCompose->value,
|
||||
type: OperationRunType::EnvironmentReviewCompose->value,
|
||||
identityInputs: [
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'snapshot_id' => (int) $snapshot->getKey(),
|
||||
@ -134,9 +134,9 @@ private function queueComposition(
|
||||
ManagedEnvironment $tenant,
|
||||
?EvidenceSnapshot $snapshot,
|
||||
User $user,
|
||||
?TenantReview $existingReview,
|
||||
?EnvironmentReview $existingReview,
|
||||
AuditActionId $auditAction,
|
||||
): TenantReview {
|
||||
): EnvironmentReview {
|
||||
if (! $snapshot instanceof EvidenceSnapshot) {
|
||||
throw new InvalidArgumentException('An eligible evidence snapshot is required.');
|
||||
}
|
||||
@ -148,17 +148,17 @@ private function queueComposition(
|
||||
$fingerprint = $this->fingerprint->forSnapshot($tenant, $snapshot);
|
||||
$review = $existingReview;
|
||||
|
||||
if (! $review instanceof TenantReview) {
|
||||
if (! $review instanceof EnvironmentReview) {
|
||||
$existing = $this->findExistingMutableReview($tenant, $fingerprint);
|
||||
|
||||
if ($existing instanceof TenantReview) {
|
||||
if ($existing instanceof EnvironmentReview) {
|
||||
return $existing->load(['tenant', 'evidenceSnapshot', 'sections', 'operationRun', 'initiator', 'publisher']);
|
||||
}
|
||||
}
|
||||
|
||||
$operationRun = $this->operationRuns->ensureRunWithIdentity(
|
||||
tenant: $tenant,
|
||||
type: OperationRunType::TenantReviewCompose->value,
|
||||
type: OperationRunType::EnvironmentReviewCompose->value,
|
||||
identityInputs: [
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'snapshot_id' => (int) $snapshot->getKey(),
|
||||
@ -177,14 +177,14 @@ private function queueComposition(
|
||||
initiator: $user,
|
||||
);
|
||||
|
||||
$review ??= TenantReview::query()->create([
|
||||
$review ??= EnvironmentReview::query()->create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
||||
'operation_run_id' => (int) $operationRun->getKey(),
|
||||
'initiated_by_user_id' => (int) $user->getKey(),
|
||||
'fingerprint' => $fingerprint,
|
||||
'status' => TenantReviewStatus::Draft->value,
|
||||
'status' => EnvironmentReviewStatus::Draft->value,
|
||||
'completeness_state' => (string) $snapshot->completeness_state,
|
||||
'summary' => [
|
||||
'evidence_basis' => [
|
||||
@ -199,12 +199,12 @@ private function queueComposition(
|
||||
],
|
||||
]);
|
||||
|
||||
if ($existingReview instanceof TenantReview) {
|
||||
if ($existingReview instanceof EnvironmentReview) {
|
||||
$existingReview->forceFill([
|
||||
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
||||
'operation_run_id' => (int) $operationRun->getKey(),
|
||||
'fingerprint' => $fingerprint,
|
||||
'status' => TenantReviewStatus::Draft->value,
|
||||
'status' => EnvironmentReviewStatus::Draft->value,
|
||||
])->save();
|
||||
|
||||
$review = $existingReview->refresh();
|
||||
@ -212,8 +212,8 @@ private function queueComposition(
|
||||
|
||||
if ($operationRun->wasRecentlyCreated) {
|
||||
$this->operationRuns->dispatchOrFail($operationRun, function () use ($review, $operationRun): void {
|
||||
ComposeTenantReviewJob::dispatch(
|
||||
tenantReviewId: (int) $review->getKey(),
|
||||
ComposeEnvironmentReviewJob::dispatch(
|
||||
environmentReviewId: (int) $review->getKey(),
|
||||
operationRunId: (int) $operationRun->getKey(),
|
||||
);
|
||||
});
|
||||
@ -231,7 +231,7 @@ private function queueComposition(
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $review->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
|
||||
operationRunId: (int) $operationRun->getKey(),
|
||||
@ -241,9 +241,9 @@ private function queueComposition(
|
||||
return $review->load(['tenant', 'evidenceSnapshot', 'sections', 'operationRun', 'initiator', 'publisher']);
|
||||
}
|
||||
|
||||
private function findExistingMutableReview(ManagedEnvironment $tenant, string $fingerprint): ?TenantReview
|
||||
private function findExistingMutableReview(ManagedEnvironment $tenant, string $fingerprint): ?EnvironmentReview
|
||||
{
|
||||
return TenantReview::query()
|
||||
return EnvironmentReview::query()
|
||||
->forTenant((int) $tenant->getKey())
|
||||
->mutable()
|
||||
->where('fingerprint', $fingerprint)
|
||||
@ -3,13 +3,13 @@
|
||||
namespace App\Services\Intune;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantPermission;
|
||||
use App\Models\ManagedEnvironmentPermission;
|
||||
use App\Services\Graph\GraphClientInterface;
|
||||
use App\Services\Providers\MicrosoftGraphOptionsResolver;
|
||||
use DateTimeInterface;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class TenantPermissionService
|
||||
class ManagedEnvironmentPermissionService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly GraphClientInterface $graphClient,
|
||||
@ -32,11 +32,11 @@ public function getRequiredPermissions(): array
|
||||
*/
|
||||
public function getGrantedPermissions(ManagedEnvironment $tenant): array
|
||||
{
|
||||
return TenantPermission::query()
|
||||
return ManagedEnvironmentPermission::query()
|
||||
->where('managed_environment_id', $tenant->id)
|
||||
->get()
|
||||
->keyBy('permission_key')
|
||||
->map(fn (TenantPermission $permission) => [
|
||||
->map(fn (ManagedEnvironmentPermission $permission) => [
|
||||
'status' => $permission->status,
|
||||
'details' => $permission->details,
|
||||
'last_checked_at' => $permission->last_checked_at,
|
||||
@ -46,7 +46,7 @@ public function getGrantedPermissions(ManagedEnvironment $tenant): array
|
||||
|
||||
/**
|
||||
* @param array<string, array{status:string,details?:array<string,mixed>|null}|string>|null $grantedStatuses
|
||||
* @param bool $persist Persist comparison results to tenant_permissions
|
||||
* @param bool $persist Persist comparison results to managed_environment_permissions
|
||||
* @param bool $liveCheck If true, fetch actual permissions from Graph API
|
||||
* @param bool $useConfiguredStub Include configured stub permissions when no live check is used
|
||||
* @param array{tenant?:string|null,client_id?:string|null,client_secret?:string|null,client_request_id?:string|null}|null $graphOptions
|
||||
@ -165,7 +165,7 @@ public function compare(
|
||||
if (! $shouldPersistErrorSnapshot) {
|
||||
$canPersist = false;
|
||||
} else {
|
||||
$hasStoredStatuses = TenantPermission::query()
|
||||
$hasStoredStatuses = ManagedEnvironmentPermission::query()
|
||||
->where('managed_environment_id', $tenant->id)
|
||||
->exists();
|
||||
|
||||
@ -189,7 +189,7 @@ public function compare(
|
||||
: ($granted[$key]['details'] ?? null);
|
||||
|
||||
if ($canPersist) {
|
||||
TenantPermission::updateOrCreate(
|
||||
ManagedEnvironmentPermission::updateOrCreate(
|
||||
[
|
||||
'managed_environment_id' => $tenant->id,
|
||||
'permission_key' => $key,
|
||||
@ -433,7 +433,7 @@ private function fetchLivePermissions(ManagedEnvironment $tenant, ?array $graphO
|
||||
|
||||
private function lastRefreshedAtIso(ManagedEnvironment $tenant): ?string
|
||||
{
|
||||
$lastCheckedAt = TenantPermission::query()
|
||||
$lastCheckedAt = ManagedEnvironmentPermission::query()
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->max('last_checked_at');
|
||||
|
||||
@ -6,15 +6,15 @@
|
||||
use App\Support\Providers\Capabilities\ProviderCapabilityDefinition;
|
||||
use App\Support\Providers\Capabilities\ProviderCapabilityRegistry;
|
||||
use App\Support\Providers\Capabilities\ProviderCapabilityStatus;
|
||||
use App\Support\Verification\TenantPermissionCheckClusters;
|
||||
use App\Support\Verification\ManagedEnvironmentPermissionCheckClusters;
|
||||
use App\Support\Verification\VerificationReportOverall;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class TenantRequiredPermissionsViewModelBuilder
|
||||
class ManagedEnvironmentRequiredPermissionsViewModelBuilder
|
||||
{
|
||||
/**
|
||||
* @phpstan-type TenantPermissionRow array{key:string,type:'application'|'delegated',description:?string,features:array<int,string>,status:'granted'|'missing'|'error',details:array<string,mixed>|null}
|
||||
* @phpstan-type ManagedEnvironmentPermissionRow array{key:string,type:'application'|'delegated',description:?string,features:array<int,string>,status:'granted'|'missing'|'error',details:array<string,mixed>|null}
|
||||
* @phpstan-type FeatureImpact array{feature:string,missing:int,required_application:int,required_delegated:int,blocked:bool}
|
||||
* @phpstan-type CapabilityGroup array{provider_capability_key:string,label:string,status:string,provider_requirement_keys:array<int,string>,missing_requirement_keys:array<int,string>,evidence_counts:array{requirements:int,missing:int,errors:int},message:string}
|
||||
* @phpstan-type FilterState array{status:'missing'|'present'|'all',type:'application'|'delegated'|'all',features:array<int,string>,search:string}
|
||||
@ -28,12 +28,12 @@ class TenantRequiredPermissionsViewModelBuilder
|
||||
* primary_capability_group: CapabilityGroup|null,
|
||||
* freshness: array{last_refreshed_at:?string,is_stale:bool}
|
||||
* },
|
||||
* permissions: array<int, TenantPermissionRow>,
|
||||
* permissions: array<int, ManagedEnvironmentPermissionRow>,
|
||||
* filters: FilterState,
|
||||
* copy: array{application:string,delegated:string}
|
||||
* }
|
||||
*/
|
||||
public function __construct(private readonly TenantPermissionService $permissionService) {}
|
||||
public function __construct(private readonly ManagedEnvironmentPermissionService $permissionService) {}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $filters
|
||||
@ -48,7 +48,7 @@ public function build(ManagedEnvironment $tenant, array $filters = []): array
|
||||
useConfiguredStub: false,
|
||||
);
|
||||
|
||||
/** @var array<int, TenantPermissionRow> $allPermissions */
|
||||
/** @var array<int, ManagedEnvironmentPermissionRow> $allPermissions */
|
||||
$allPermissions = collect($comparison['permissions'] ?? [])
|
||||
->filter(fn (mixed $row): bool => is_array($row))
|
||||
->map(fn (array $row): array => self::normalizePermissionRow($row))
|
||||
@ -87,7 +87,7 @@ public function build(ManagedEnvironment $tenant, array $filters = []): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @param array{last_refreshed_at:?string,is_stale:bool} $freshness
|
||||
* @return array<int, CapabilityGroup>
|
||||
*/
|
||||
@ -127,7 +127,7 @@ public static function primaryCapabilityGroup(array $groups): ?array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @return CapabilityGroup
|
||||
*/
|
||||
private static function deriveCapabilityGroup(
|
||||
@ -138,7 +138,7 @@ private static function deriveCapabilityGroup(
|
||||
$rowsByRequirement = [];
|
||||
|
||||
foreach ($definition->providerRequirementKeys as $requirementKey) {
|
||||
$rowsByRequirement[$requirementKey] = TenantPermissionCheckClusters::rowsForRequirementKey($permissions, $requirementKey);
|
||||
$rowsByRequirement[$requirementKey] = ManagedEnvironmentPermissionCheckClusters::rowsForRequirementKey($permissions, $requirementKey);
|
||||
}
|
||||
|
||||
$rows = array_values(array_merge(...array_values($rowsByRequirement ?: [[]])));
|
||||
@ -193,7 +193,7 @@ private static function deriveCapabilityGroup(
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
*/
|
||||
public static function deriveOverallStatus(array $permissions, bool $hasStaleFreshness = false): string
|
||||
{
|
||||
@ -243,7 +243,7 @@ public static function deriveFreshness(?CarbonInterface $lastRefreshedAt, ?Carbo
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @return array{missing_application:int,missing_delegated:int,present:int,error:int}
|
||||
*/
|
||||
public static function deriveCounts(array $permissions): array
|
||||
@ -281,7 +281,7 @@ public static function deriveCounts(array $permissions): array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @return array<int, FeatureImpact>
|
||||
*/
|
||||
public static function deriveFeatureImpacts(array $permissions): array
|
||||
@ -345,7 +345,7 @@ public static function deriveFeatureImpacts(array $permissions): array
|
||||
* - Respects Feature filter only
|
||||
* - Ignores Search
|
||||
*
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @param 'application'|'delegated' $type
|
||||
* @param array<int, string> $featureFilter
|
||||
*/
|
||||
@ -383,8 +383,8 @@ public static function deriveCopyPayload(array $permissions, string $type, array
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, TenantPermissionRow> $permissions
|
||||
* @return array<int, TenantPermissionRow>
|
||||
* @param array<int, ManagedEnvironmentPermissionRow> $permissions
|
||||
* @return array<int, ManagedEnvironmentPermissionRow>
|
||||
*/
|
||||
public static function applyFilterState(array $permissions, array $state): array
|
||||
{
|
||||
@ -489,7 +489,7 @@ public static function normalizeFilterState(array $filters): array
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $row
|
||||
* @return TenantPermissionRow
|
||||
* @return ManagedEnvironmentPermissionRow
|
||||
*/
|
||||
private static function normalizePermissionRow(array $row): array
|
||||
{
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
use App\Exceptions\Onboarding\OnboardingDraftConflictException;
|
||||
use App\Exceptions\Onboarding\OnboardingDraftImmutableException;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -19,25 +19,25 @@ public function __construct(
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param callable(TenantOnboardingSession):void $mutator
|
||||
* @param callable(ManagedEnvironmentOnboardingSession):void $mutator
|
||||
*/
|
||||
public function createOrResume(
|
||||
Workspace $workspace,
|
||||
User $actor,
|
||||
string $entraTenantId,
|
||||
callable $mutator,
|
||||
?TenantOnboardingSession $preferredDraft = null,
|
||||
?ManagedEnvironmentOnboardingSession $preferredDraft = null,
|
||||
?int $expectedVersion = null,
|
||||
bool $incrementVersion = true,
|
||||
?bool &$wasCreated = null,
|
||||
): TenantOnboardingSession {
|
||||
return DB::transaction(function () use ($workspace, $actor, $entraTenantId, $mutator, $preferredDraft, $expectedVersion, $incrementVersion, &$wasCreated): TenantOnboardingSession {
|
||||
): ManagedEnvironmentOnboardingSession {
|
||||
return DB::transaction(function () use ($workspace, $actor, $entraTenantId, $mutator, $preferredDraft, $expectedVersion, $incrementVersion, &$wasCreated): ManagedEnvironmentOnboardingSession {
|
||||
$draft = $this->resolveDraftForIdentity($workspace, $entraTenantId, $preferredDraft);
|
||||
$isNew = ! $draft instanceof TenantOnboardingSession;
|
||||
$isNew = ! $draft instanceof ManagedEnvironmentOnboardingSession;
|
||||
$wasCreated = $isNew;
|
||||
|
||||
if ($isNew) {
|
||||
$draft = new TenantOnboardingSession;
|
||||
$draft = new ManagedEnvironmentOnboardingSession;
|
||||
$draft->workspace_id = (int) $workspace->getKey();
|
||||
$draft->entra_tenant_id = $entraTenantId;
|
||||
$draft->started_by_user_id = (int) $actor->getKey();
|
||||
@ -61,18 +61,18 @@ public function createOrResume(
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(TenantOnboardingSession):void $mutator
|
||||
* @param callable(ManagedEnvironmentOnboardingSession):void $mutator
|
||||
*/
|
||||
public function mutate(
|
||||
TenantOnboardingSession $draft,
|
||||
ManagedEnvironmentOnboardingSession $draft,
|
||||
User $actor,
|
||||
callable $mutator,
|
||||
?int $expectedVersion = null,
|
||||
bool $incrementVersion = true,
|
||||
bool $allowTerminal = false,
|
||||
): TenantOnboardingSession {
|
||||
return DB::transaction(function () use ($draft, $actor, $mutator, $expectedVersion, $incrementVersion, $allowTerminal): TenantOnboardingSession {
|
||||
$lockedDraft = TenantOnboardingSession::query()
|
||||
): ManagedEnvironmentOnboardingSession {
|
||||
return DB::transaction(function () use ($draft, $actor, $mutator, $expectedVersion, $incrementVersion, $allowTerminal): ManagedEnvironmentOnboardingSession {
|
||||
$lockedDraft = ManagedEnvironmentOnboardingSession::query()
|
||||
->whereKey($draft->getKey())
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
@ -101,19 +101,19 @@ public function mutate(
|
||||
});
|
||||
}
|
||||
|
||||
public function lockForTrustedMutation(TenantOnboardingSession|int|string $draft, Workspace $workspace): TenantOnboardingSession
|
||||
public function lockForTrustedMutation(ManagedEnvironmentOnboardingSession|int|string $draft, Workspace $workspace): ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
$draftId = $draft instanceof TenantOnboardingSession
|
||||
$draftId = $draft instanceof ManagedEnvironmentOnboardingSession
|
||||
? (int) $draft->getKey()
|
||||
: (int) $draft;
|
||||
|
||||
$lockedDraft = TenantOnboardingSession::query()
|
||||
$lockedDraft = ManagedEnvironmentOnboardingSession::query()
|
||||
->whereKey($draftId)
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if (! $lockedDraft instanceof TenantOnboardingSession) {
|
||||
if (! $lockedDraft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
@ -123,16 +123,16 @@ public function lockForTrustedMutation(TenantOnboardingSession|int|string $draft
|
||||
private function resolveDraftForIdentity(
|
||||
Workspace $workspace,
|
||||
string $entraTenantId,
|
||||
?TenantOnboardingSession $preferredDraft = null,
|
||||
): ?TenantOnboardingSession {
|
||||
if ($preferredDraft instanceof TenantOnboardingSession) {
|
||||
$lockedPreferredDraft = TenantOnboardingSession::query()
|
||||
?ManagedEnvironmentOnboardingSession $preferredDraft = null,
|
||||
): ?ManagedEnvironmentOnboardingSession {
|
||||
if ($preferredDraft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
$lockedPreferredDraft = ManagedEnvironmentOnboardingSession::query()
|
||||
->whereKey($preferredDraft->getKey())
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
|
||||
if ($lockedPreferredDraft instanceof TenantOnboardingSession && $lockedPreferredDraft->entra_tenant_id === $entraTenantId) {
|
||||
if ($lockedPreferredDraft instanceof ManagedEnvironmentOnboardingSession && $lockedPreferredDraft->entra_tenant_id === $entraTenantId) {
|
||||
if ($lockedPreferredDraft->lifecycleState()->isTerminal()) {
|
||||
throw new OnboardingDraftImmutableException(
|
||||
draftId: (int) $lockedPreferredDraft->getKey(),
|
||||
@ -144,7 +144,7 @@ private function resolveDraftForIdentity(
|
||||
}
|
||||
}
|
||||
|
||||
return TenantOnboardingSession::query()
|
||||
return ManagedEnvironmentOnboardingSession::query()
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->where('entra_tenant_id', $entraTenantId)
|
||||
->resumable()
|
||||
@ -153,7 +153,7 @@ private function resolveDraftForIdentity(
|
||||
->first();
|
||||
}
|
||||
|
||||
private function assertExpectedVersion(TenantOnboardingSession $draft, int $expectedVersion): void
|
||||
private function assertExpectedVersion(ManagedEnvironmentOnboardingSession $draft, int $expectedVersion): void
|
||||
{
|
||||
$actualVersion = max(1, (int) ($draft->version ?? 1));
|
||||
|
||||
@ -168,7 +168,7 @@ private function assertExpectedVersion(TenantOnboardingSession $draft, int $expe
|
||||
);
|
||||
}
|
||||
|
||||
private function persistDraft(TenantOnboardingSession $draft, bool $incrementVersion): void
|
||||
private function persistDraft(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion): void
|
||||
{
|
||||
$currentVersion = max(0, (int) ($draft->version ?? 0));
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace App\Services\Onboarding;
|
||||
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\User;
|
||||
use App\Models\Workspace;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
@ -25,18 +25,18 @@ public function __construct(
|
||||
* @throws AuthorizationException
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function resolve(TenantOnboardingSession|int|string $draft, User $user, Workspace $workspace): TenantOnboardingSession
|
||||
public function resolve(ManagedEnvironmentOnboardingSession|int|string $draft, User $user, Workspace $workspace): ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
$draftId = $draft instanceof TenantOnboardingSession
|
||||
$draftId = $draft instanceof ManagedEnvironmentOnboardingSession
|
||||
? (int) $draft->getKey()
|
||||
: (int) $draft;
|
||||
|
||||
$resolvedDraft = TenantOnboardingSession::query()
|
||||
->with(['tenant', 'startedByUser', 'updatedByUser'])
|
||||
$resolvedDraft = ManagedEnvironmentOnboardingSession::query()
|
||||
->with(['managedEnvironment', 'startedByUser', 'updatedByUser'])
|
||||
->whereKey($draftId)
|
||||
->first();
|
||||
|
||||
if (! $resolvedDraft instanceof TenantOnboardingSession) {
|
||||
if (! $resolvedDraft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
throw new NotFoundHttpException;
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
|
||||
|
||||
$resolvedDraft = $this->lifecycleService
|
||||
->syncPersistedLifecycle($resolvedDraft)
|
||||
->loadMissing(['tenant', 'startedByUser', 'updatedByUser']);
|
||||
->loadMissing(['managedEnvironment', 'startedByUser', 'updatedByUser']);
|
||||
|
||||
$normalizedTenant = $this->lifecycleService->syncLinkedTenantAfterCancellation($resolvedDraft);
|
||||
|
||||
@ -65,7 +65,7 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
|
||||
],
|
||||
);
|
||||
|
||||
$resolvedDraft->setRelation('tenant', $normalizedTenant);
|
||||
$resolvedDraft->setRelation('managedEnvironment', $normalizedTenant);
|
||||
}
|
||||
|
||||
return $resolvedDraft;
|
||||
@ -75,18 +75,18 @@ public function resolve(TenantOnboardingSession|int|string $draft, User $user, W
|
||||
* @throws AuthorizationException
|
||||
* @throws NotFoundHttpException
|
||||
*/
|
||||
public function resolveForTrustedAction(TenantOnboardingSession|int|string $draft, User $user, Workspace $workspace): TenantOnboardingSession
|
||||
public function resolveForTrustedAction(ManagedEnvironmentOnboardingSession|int|string $draft, User $user, Workspace $workspace): ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
return $this->resolve($draft, $user, $workspace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, TenantOnboardingSession>
|
||||
* @return Collection<int, ManagedEnvironmentOnboardingSession>
|
||||
*/
|
||||
public function resumableDraftsFor(User $user, Workspace $workspace): Collection
|
||||
{
|
||||
$drafts = TenantOnboardingSession::query()
|
||||
->with(['tenant', 'startedByUser', 'updatedByUser'])
|
||||
$drafts = ManagedEnvironmentOnboardingSession::query()
|
||||
->with(['managedEnvironment', 'startedByUser', 'updatedByUser'])
|
||||
->where('workspace_id', (int) $workspace->getKey())
|
||||
->resumable()
|
||||
->orderByDesc('updated_at')
|
||||
@ -103,7 +103,7 @@ public function resumableDraftsFor(User $user, Workspace $workspace): Collection
|
||||
|
||||
$resolvedDraft = $this->lifecycleService
|
||||
->syncPersistedLifecycle($draft)
|
||||
->loadMissing(['tenant', 'startedByUser', 'updatedByUser']);
|
||||
->loadMissing(['managedEnvironment', 'startedByUser', 'updatedByUser']);
|
||||
|
||||
if (! $this->lifecycleService->canResumeDraft($resolvedDraft)) {
|
||||
continue;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace App\Services\Onboarding;
|
||||
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Support\Onboarding\OnboardingCheckpoint;
|
||||
use App\Support\Onboarding\OnboardingDraftStage;
|
||||
use App\Support\Onboarding\OnboardingLifecycleState;
|
||||
@ -15,9 +15,9 @@ public function __construct(
|
||||
private readonly OnboardingLifecycleService $lifecycleService,
|
||||
) {}
|
||||
|
||||
public function resolve(?TenantOnboardingSession $draft): OnboardingDraftStage
|
||||
public function resolve(?ManagedEnvironmentOnboardingSession $draft): OnboardingDraftStage
|
||||
{
|
||||
if (! $draft instanceof TenantOnboardingSession) {
|
||||
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
return OnboardingDraftStage::Identify;
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Services\Tenants\TenantOperabilityService;
|
||||
use App\Support\Onboarding\OnboardingCheckpoint;
|
||||
use App\Support\Onboarding\OnboardingLifecycleState;
|
||||
@ -26,11 +26,11 @@ public function __construct(
|
||||
private readonly ProductTelemetryRecorder $productTelemetryRecorder,
|
||||
) {}
|
||||
|
||||
public function syncPersistedLifecycle(TenantOnboardingSession $draft, bool $incrementVersion = false): TenantOnboardingSession
|
||||
public function syncPersistedLifecycle(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
$freshDraft = TenantOnboardingSession::query()->whereKey($draft->getKey())->first();
|
||||
$freshDraft = ManagedEnvironmentOnboardingSession::query()->whereKey($draft->getKey())->first();
|
||||
|
||||
if (! $freshDraft instanceof TenantOnboardingSession) {
|
||||
if (! $freshDraft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
return $draft;
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ public function syncPersistedLifecycle(TenantOnboardingSession $draft, bool $inc
|
||||
return $freshDraft->refresh();
|
||||
}
|
||||
|
||||
public function applySnapshot(TenantOnboardingSession $draft, bool $incrementVersion = false): bool
|
||||
public function applySnapshot(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion = false): bool
|
||||
{
|
||||
$snapshot = $this->snapshot($draft);
|
||||
$lifecycleState = $draft->lifecycle_state instanceof OnboardingLifecycleState
|
||||
@ -98,7 +98,7 @@ public function applySnapshot(TenantOnboardingSession $draft, bool $incrementVer
|
||||
return $changed;
|
||||
}
|
||||
|
||||
public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSession $draft): void
|
||||
public function recordCompletedCheckpointTelemetryIfNeeded(ManagedEnvironmentOnboardingSession $draft): void
|
||||
{
|
||||
if (! $draft->wasChanged('last_completed_checkpoint')) {
|
||||
return;
|
||||
@ -127,7 +127,7 @@ public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSessi
|
||||
workspaceId: $workspaceId,
|
||||
tenantId: $tenantId,
|
||||
userId: $userId,
|
||||
subjectType: 'tenant_onboarding_session',
|
||||
subjectType: 'managed_environment_onboarding_session',
|
||||
subjectId: (int) $draft->getKey(),
|
||||
metadata: [
|
||||
'checkpoint_key' => $checkpoint->value,
|
||||
@ -147,7 +147,7 @@ public function recordCompletedCheckpointTelemetryIfNeeded(TenantOnboardingSessi
|
||||
* blocking_reason_code: string|null
|
||||
* }
|
||||
*/
|
||||
public function snapshot(TenantOnboardingSession $draft): array
|
||||
public function snapshot(ManagedEnvironmentOnboardingSession $draft): array
|
||||
{
|
||||
$selectedProviderConnectionId = $this->selectedProviderConnectionId($draft);
|
||||
$verificationRun = $this->verificationRun($draft);
|
||||
@ -334,7 +334,7 @@ public function snapshot(TenantOnboardingSession $draft): array
|
||||
];
|
||||
}
|
||||
|
||||
public function verificationRun(TenantOnboardingSession $draft): ?OperationRun
|
||||
public function verificationRun(ManagedEnvironmentOnboardingSession $draft): ?OperationRun
|
||||
{
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
$runId = $this->normalizeInteger($state['verification_operation_run_id'] ?? $state['verification_run_id'] ?? null);
|
||||
@ -355,7 +355,7 @@ public function verificationRun(TenantOnboardingSession $draft): ?OperationRun
|
||||
}
|
||||
|
||||
public function verificationStatus(
|
||||
TenantOnboardingSession $draft,
|
||||
ManagedEnvironmentOnboardingSession $draft,
|
||||
?int $selectedProviderConnectionId = null,
|
||||
?OperationRun $run = null,
|
||||
): string {
|
||||
@ -386,7 +386,7 @@ public function verificationStatus(
|
||||
}
|
||||
|
||||
public function verificationCanProceed(
|
||||
TenantOnboardingSession $draft,
|
||||
ManagedEnvironmentOnboardingSession $draft,
|
||||
?int $selectedProviderConnectionId = null,
|
||||
?OperationRun $run = null,
|
||||
): bool {
|
||||
@ -408,7 +408,7 @@ public function verificationCanProceed(
|
||||
return in_array($this->verificationStatus($draft, $selectedProviderConnectionId, $run), ['ready', 'needs_attention'], true);
|
||||
}
|
||||
|
||||
public function verificationIsBlocked(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId = null): bool
|
||||
public function verificationIsBlocked(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): bool
|
||||
{
|
||||
return $this->verificationStatus($draft, $selectedProviderConnectionId) === 'blocked';
|
||||
}
|
||||
@ -425,32 +425,32 @@ public function verificationIsBlocked(TenantOnboardingSession $draft, ?int $sele
|
||||
* is_completed: bool
|
||||
* }>
|
||||
*/
|
||||
public function bootstrapRunSummaries(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId = null): array
|
||||
public function bootstrapRunSummaries(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId = null): array
|
||||
{
|
||||
$selectedProviderConnectionId ??= $this->selectedProviderConnectionId($draft);
|
||||
|
||||
return $this->bootstrapState($draft, $selectedProviderConnectionId)['summaries'];
|
||||
}
|
||||
|
||||
public function isReadyForActivation(TenantOnboardingSession $draft): bool
|
||||
public function isReadyForActivation(ManagedEnvironmentOnboardingSession $draft): bool
|
||||
{
|
||||
return $this->snapshot($draft)['lifecycle_state'] === OnboardingLifecycleState::ReadyForActivation;
|
||||
}
|
||||
|
||||
public function hasActiveCheckpoint(TenantOnboardingSession $draft): bool
|
||||
public function hasActiveCheckpoint(ManagedEnvironmentOnboardingSession $draft): bool
|
||||
{
|
||||
$snapshot = $this->snapshot($draft);
|
||||
|
||||
return in_array($snapshot['lifecycle_state'], [OnboardingLifecycleState::Verifying, OnboardingLifecycleState::Bootstrapping], true);
|
||||
}
|
||||
|
||||
public function canResumeDraft(TenantOnboardingSession $draft): bool
|
||||
public function canResumeDraft(ManagedEnvironmentOnboardingSession $draft): bool
|
||||
{
|
||||
if (! $draft->isWorkflowResumable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tenant = $draft->tenant;
|
||||
$tenant = $draft->managedEnvironment;
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
return true;
|
||||
@ -459,9 +459,9 @@ public function canResumeDraft(TenantOnboardingSession $draft): bool
|
||||
return $this->tenantOperabilityService->canResumeOnboarding($tenant);
|
||||
}
|
||||
|
||||
public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft): ?ManagedEnvironment
|
||||
public function syncLinkedTenantAfterCancellation(ManagedEnvironmentOnboardingSession $draft): ?ManagedEnvironment
|
||||
{
|
||||
$tenant = $draft->tenant;
|
||||
$tenant = $draft->managedEnvironment;
|
||||
|
||||
if (! $tenant instanceof ManagedEnvironment) {
|
||||
return null;
|
||||
@ -471,7 +471,7 @@ public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft
|
||||
return null;
|
||||
}
|
||||
|
||||
$hasOtherResumableDrafts = TenantOnboardingSession::query()
|
||||
$hasOtherResumableDrafts = ManagedEnvironmentOnboardingSession::query()
|
||||
->where('workspace_id', (int) $draft->workspace_id)
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->whereKeyNot((int) $draft->getKey())
|
||||
@ -487,7 +487,7 @@ public function syncLinkedTenantAfterCancellation(TenantOnboardingSession $draft
|
||||
return $tenant->fresh();
|
||||
}
|
||||
|
||||
private function hasTenantIdentity(TenantOnboardingSession $draft): bool
|
||||
private function hasTenantIdentity(ManagedEnvironmentOnboardingSession $draft): bool
|
||||
{
|
||||
if ($draft->managed_environment_id !== null) {
|
||||
return true;
|
||||
@ -499,7 +499,7 @@ private function hasTenantIdentity(TenantOnboardingSession $draft): bool
|
||||
return is_string($entraTenantId) && trim($entraTenantId) !== '';
|
||||
}
|
||||
|
||||
private function selectedProviderConnectionId(TenantOnboardingSession $draft): ?int
|
||||
private function selectedProviderConnectionId(ManagedEnvironmentOnboardingSession $draft): ?int
|
||||
{
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
|
||||
@ -518,7 +518,7 @@ private function selectedProviderConnectionId(TenantOnboardingSession $draft): ?
|
||||
return $exists ? $providerConnectionId : null;
|
||||
}
|
||||
|
||||
private function connectionRecentlyUpdated(TenantOnboardingSession $draft): bool
|
||||
private function connectionRecentlyUpdated(ManagedEnvironmentOnboardingSession $draft): bool
|
||||
{
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
|
||||
@ -639,7 +639,7 @@ private function runReasonCodes(OperationRun $run): array
|
||||
* }>
|
||||
* }
|
||||
*/
|
||||
private function bootstrapState(TenantOnboardingSession $draft, ?int $selectedProviderConnectionId): array
|
||||
private function bootstrapState(ManagedEnvironmentOnboardingSession $draft, ?int $selectedProviderConnectionId): array
|
||||
{
|
||||
$selectedTypes = $this->bootstrapOperationTypes($draft);
|
||||
$runMap = $this->bootstrapRunMap($draft, $selectedTypes);
|
||||
@ -717,7 +717,7 @@ private function bootstrapState(TenantOnboardingSession $draft, ?int $selectedPr
|
||||
/**
|
||||
* @return array<int, string>
|
||||
*/
|
||||
private function bootstrapOperationTypes(TenantOnboardingSession $draft): array
|
||||
private function bootstrapOperationTypes(ManagedEnvironmentOnboardingSession $draft): array
|
||||
{
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
$types = $state['bootstrap_operation_types'] ?? [];
|
||||
@ -736,7 +736,7 @@ private function bootstrapOperationTypes(TenantOnboardingSession $draft): array
|
||||
* @param array<int, string> $selectedTypes
|
||||
* @return array<string, int>
|
||||
*/
|
||||
private function bootstrapRunMap(TenantOnboardingSession $draft, array $selectedTypes): array
|
||||
private function bootstrapRunMap(ManagedEnvironmentOnboardingSession $draft, array $selectedTypes): array
|
||||
{
|
||||
$state = is_array($draft->state) ? $draft->state : [];
|
||||
$runs = $state['bootstrap_operation_runs'] ?? null;
|
||||
|
||||
@ -342,7 +342,7 @@ private function laneForContext(QueuedExecutionContext $context): TenantInteract
|
||||
$runContext = is_array($context->run->context) ? $context->run->context : [];
|
||||
$wizardFlow = data_get($runContext, 'wizard.flow');
|
||||
|
||||
if (is_string($wizardFlow) && trim($wizardFlow) === 'managed_tenant_onboarding') {
|
||||
if (is_string($wizardFlow) && trim($wizardFlow) === 'managed_environment_onboarding') {
|
||||
return TenantInteractionLane::OnboardingWorkflow;
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
/**
|
||||
* Generates, auto-resolves, and re-opens permission posture findings
|
||||
* based on the output of TenantPermissionService::compare().
|
||||
* based on the output of ManagedEnvironmentPermissionService::compare().
|
||||
*/
|
||||
final class PermissionPostureFindingGenerator implements FindingGeneratorContract
|
||||
{
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
namespace App\Services\PortfolioCompare;
|
||||
|
||||
use App\Jobs\Operations\CrossTenantPromotionExecutionJob;
|
||||
use App\Jobs\Operations\CrossEnvironmentPromotionExecutionJob;
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ProviderConnection;
|
||||
use App\Models\User;
|
||||
@ -17,14 +17,14 @@
|
||||
use App\Support\OperationalControls\OperationalControlBlockedException;
|
||||
use App\Support\OperationalControls\OperationalControlEvaluator;
|
||||
use App\Support\OperationRunStatus;
|
||||
use App\Support\PortfolioCompare\CrossTenantCompareSelection;
|
||||
use App\Support\PortfolioCompare\CrossTenantPromotionExecutionPlanner;
|
||||
use App\Support\PortfolioCompare\CrossEnvironmentCompareSelection;
|
||||
use App\Support\PortfolioCompare\CrossEnvironmentPromotionExecutionPlanner;
|
||||
use Carbon\CarbonImmutable;
|
||||
|
||||
final class CrossTenantPromotionExecutionService
|
||||
final class CrossEnvironmentPromotionExecutionService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly CrossTenantPromotionExecutionPlanner $planner,
|
||||
private readonly CrossEnvironmentPromotionExecutionPlanner $planner,
|
||||
private readonly OperationRunService $operationRuns,
|
||||
private readonly WorkspaceAuditLogger $auditLogger,
|
||||
private readonly OperationalControlEvaluator $operationalControls,
|
||||
@ -35,12 +35,12 @@ public function __construct(
|
||||
* @param array<string, mixed> $preflight
|
||||
*/
|
||||
public function start(
|
||||
CrossTenantCompareSelection $selection,
|
||||
CrossEnvironmentCompareSelection $selection,
|
||||
array $preview,
|
||||
array $preflight,
|
||||
User $actor,
|
||||
): ProviderOperationStartResult {
|
||||
$workspace = $selection->targetTenant->workspace;
|
||||
$workspace = $selection->targetEnvironment->workspace;
|
||||
|
||||
if (! $workspace instanceof Workspace) {
|
||||
throw new \RuntimeException('Promotion execution requires a workspace context.');
|
||||
@ -60,8 +60,8 @@ public function start(
|
||||
'reason_text' => $decision->reasonText,
|
||||
'expires_at' => $decision->expiresAt?->toIso8601String(),
|
||||
'actor_id' => (int) $actor->getKey(),
|
||||
'source_tenant_id' => (int) $selection->sourceTenant->getKey(),
|
||||
'target_tenant_id' => (int) $selection->targetTenant->getKey(),
|
||||
'source_environment_id' => (int) $selection->sourceEnvironment->getKey(),
|
||||
'target_environment_id' => (int) $selection->targetEnvironment->getKey(),
|
||||
'requested_scope' => 'promotion.execute',
|
||||
], static fn (mixed $value): bool => $value !== null && $value !== ''),
|
||||
],
|
||||
@ -71,14 +71,14 @@ public function start(
|
||||
resourceId: $decision->sourceActivationId !== null ? (string) $decision->sourceActivationId : null,
|
||||
targetLabel: 'Promotion execution',
|
||||
summary: 'Promotion execution blocked by operational control',
|
||||
tenant: $selection->targetTenant,
|
||||
tenant: $selection->targetEnvironment,
|
||||
);
|
||||
|
||||
throw OperationalControlBlockedException::forDecision($decision, 'Promotion execution');
|
||||
}
|
||||
|
||||
$plan = $this->planner->build($preview, $preflight);
|
||||
$providerConnection = $this->defaultProviderConnection((int) $selection->targetTenant->getKey());
|
||||
$providerConnection = $this->defaultProviderConnection((int) $selection->targetEnvironment->getKey());
|
||||
$now = CarbonImmutable::now();
|
||||
|
||||
$identity = array_replace($plan['identity'], [
|
||||
@ -87,20 +87,20 @@ public function start(
|
||||
|
||||
$context = [
|
||||
'operation_type' => 'promotion.execute',
|
||||
'source_tenant_id' => (int) $selection->sourceTenant->getKey(),
|
||||
'source_tenant_name' => (string) $selection->sourceTenant->name,
|
||||
'target_tenant_id' => (int) $selection->targetTenant->getKey(),
|
||||
'target_tenant_name' => (string) $selection->targetTenant->name,
|
||||
'source_environment_id' => (int) $selection->sourceEnvironment->getKey(),
|
||||
'source_environment_name' => (string) $selection->sourceEnvironment->name,
|
||||
'target_environment_id' => (int) $selection->targetEnvironment->getKey(),
|
||||
'target_environment_name' => (string) $selection->targetEnvironment->name,
|
||||
'provider_connection_id' => $providerConnection instanceof ProviderConnection ? (int) $providerConnection->getKey() : null,
|
||||
'required_capability' => Capabilities::TENANT_MANAGE,
|
||||
'workspace_required_capability' => Capabilities::WORKSPACE_BASELINES_MANAGE,
|
||||
'target_scope' => [
|
||||
'workspace_id' => (int) $workspace->getKey(),
|
||||
'managed_environment_id' => (int) $selection->targetTenant->getKey(),
|
||||
'managed_environment_id' => (int) $selection->targetEnvironment->getKey(),
|
||||
'provider_connection_id' => $providerConnection instanceof ProviderConnection ? (int) $providerConnection->getKey() : null,
|
||||
'entra_tenant_id' => $providerConnection instanceof ProviderConnection
|
||||
? (string) $providerConnection->entra_tenant_id
|
||||
: (string) ($selection->targetTenant->managed_environment_id ?? $selection->targetTenant->external_id ?? $selection->targetTenant->getKey()),
|
||||
: (string) ($selection->targetEnvironment->managed_environment_id ?? $selection->targetEnvironment->external_id ?? $selection->targetEnvironment->getKey()),
|
||||
],
|
||||
'promotion_execution' => [
|
||||
'queued_at' => $now->toIso8601String(),
|
||||
@ -111,7 +111,7 @@ public function start(
|
||||
];
|
||||
|
||||
$run = $this->operationRuns->ensureRunWithIdentity(
|
||||
tenant: $selection->targetTenant,
|
||||
tenant: $selection->targetEnvironment,
|
||||
type: 'promotion.execute',
|
||||
identityInputs: $identity,
|
||||
context: $context,
|
||||
@ -134,13 +134,13 @@ public function start(
|
||||
|
||||
$this->operationRuns->dispatchOrFail(
|
||||
$run,
|
||||
fn (OperationRun $operationRun): mixed => CrossTenantPromotionExecutionJob::dispatch($operationRun),
|
||||
fn (OperationRun $operationRun): mixed => CrossEnvironmentPromotionExecutionJob::dispatch($operationRun),
|
||||
);
|
||||
|
||||
$this->auditLogger->logCrossTenantPromotionExecutionQueued(
|
||||
$this->auditLogger->logCrossEnvironmentPromotionExecutionQueued(
|
||||
workspace: $workspace,
|
||||
sourceTenant: $selection->sourceTenant,
|
||||
targetTenant: $selection->targetTenant,
|
||||
sourceEnvironment: $selection->sourceEnvironment,
|
||||
targetEnvironment: $selection->targetEnvironment,
|
||||
operationRun: $run->fresh() ?? $run,
|
||||
plan: $plan,
|
||||
actor: $actor,
|
||||
@ -149,10 +149,10 @@ public function start(
|
||||
return ProviderOperationStartResult::started($run->fresh() ?? $run, true);
|
||||
}
|
||||
|
||||
private function defaultProviderConnection(int $tenantId): ?ProviderConnection
|
||||
private function defaultProviderConnection(int $environmentId): ?ProviderConnection
|
||||
{
|
||||
return ProviderConnection::query()
|
||||
->where('managed_environment_id', $tenantId)
|
||||
->where('managed_environment_id', $environmentId)
|
||||
->where('provider', 'microsoft')
|
||||
->where('is_default', true)
|
||||
->orderBy('id')
|
||||
@ -5,19 +5,19 @@
|
||||
namespace App\Services\PortfolioTriage;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantTriageReview;
|
||||
use App\Models\ManagedEnvironmentTriageReview;
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
use App\Support\BackupHealth\TenantBackupHealthAssessment;
|
||||
use App\Support\PortfolioTriage\TenantTriageReviewFingerprint;
|
||||
use App\Support\PortfolioTriage\ManagedEnvironmentTriageReviewFingerprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use InvalidArgumentException;
|
||||
|
||||
final readonly class TenantTriageReviewService
|
||||
final readonly class ManagedEnvironmentTriageReviewService
|
||||
{
|
||||
public function __construct(
|
||||
private TenantTriageReviewFingerprint $fingerprints,
|
||||
private ManagedEnvironmentTriageReviewFingerprint $fingerprints,
|
||||
private WorkspaceAuditLogger $auditLogger,
|
||||
) {}
|
||||
|
||||
@ -30,11 +30,11 @@ public function markReviewed(
|
||||
?TenantBackupHealthAssessment $backupHealth = null,
|
||||
?array $recoveryEvidence = null,
|
||||
?User $actor = null,
|
||||
): TenantTriageReview {
|
||||
): ManagedEnvironmentTriageReview {
|
||||
return $this->store(
|
||||
tenant: $tenant,
|
||||
concernFamily: $concernFamily,
|
||||
manualState: TenantTriageReview::STATE_REVIEWED,
|
||||
manualState: ManagedEnvironmentTriageReview::STATE_REVIEWED,
|
||||
backupHealth: $backupHealth,
|
||||
recoveryEvidence: $recoveryEvidence,
|
||||
actor: $actor,
|
||||
@ -50,11 +50,11 @@ public function markFollowUpNeeded(
|
||||
?TenantBackupHealthAssessment $backupHealth = null,
|
||||
?array $recoveryEvidence = null,
|
||||
?User $actor = null,
|
||||
): TenantTriageReview {
|
||||
): ManagedEnvironmentTriageReview {
|
||||
return $this->store(
|
||||
tenant: $tenant,
|
||||
concernFamily: $concernFamily,
|
||||
manualState: TenantTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
manualState: ManagedEnvironmentTriageReview::STATE_FOLLOW_UP_NEEDED,
|
||||
backupHealth: $backupHealth,
|
||||
recoveryEvidence: $recoveryEvidence,
|
||||
actor: $actor,
|
||||
@ -71,8 +71,8 @@ private function store(
|
||||
?TenantBackupHealthAssessment $backupHealth,
|
||||
?array $recoveryEvidence,
|
||||
?User $actor,
|
||||
): TenantTriageReview {
|
||||
if (! in_array($manualState, TenantTriageReview::MANUAL_STATES, true)) {
|
||||
): ManagedEnvironmentTriageReview {
|
||||
if (! in_array($manualState, ManagedEnvironmentTriageReview::MANUAL_STATES, true)) {
|
||||
throw new InvalidArgumentException('Unsupported triage review state.');
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ private function store(
|
||||
$workspaceId = (int) $tenant->workspace_id;
|
||||
$now = now();
|
||||
|
||||
/** @var TenantTriageReview $review */
|
||||
/** @var ManagedEnvironmentTriageReview $review */
|
||||
$review = DB::transaction(function () use (
|
||||
$tenant,
|
||||
$workspaceId,
|
||||
@ -97,8 +97,8 @@ private function store(
|
||||
$currentConcern,
|
||||
$actor,
|
||||
$now,
|
||||
): TenantTriageReview {
|
||||
TenantTriageReview::query()
|
||||
): ManagedEnvironmentTriageReview {
|
||||
ManagedEnvironmentTriageReview::query()
|
||||
->forWorkspace($workspaceId)
|
||||
->forTenant((int) $tenant->getKey())
|
||||
->where('concern_family', $currentConcern['concern_family'])
|
||||
@ -108,7 +108,7 @@ private function store(
|
||||
'updated_at' => $now,
|
||||
]);
|
||||
|
||||
return TenantTriageReview::query()->create([
|
||||
return ManagedEnvironmentTriageReview::query()->create([
|
||||
'workspace_id' => $workspaceId,
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'concern_family' => $currentConcern['concern_family'],
|
||||
@ -126,9 +126,9 @@ private function store(
|
||||
|
||||
$this->auditLogger->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: $manualState === TenantTriageReview::STATE_REVIEWED
|
||||
? AuditActionId::TenantTriageReviewMarkedReviewed
|
||||
: AuditActionId::TenantTriageReviewMarkedFollowUpNeeded,
|
||||
action: $manualState === ManagedEnvironmentTriageReview::STATE_REVIEWED
|
||||
? AuditActionId::ManagedEnvironmentTriageReviewMarkedReviewed
|
||||
: AuditActionId::ManagedEnvironmentTriageReviewMarkedFollowUpNeeded,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'concern_family' => $currentConcern['concern_family'],
|
||||
@ -138,7 +138,7 @@ private function store(
|
||||
],
|
||||
],
|
||||
actor: $actor,
|
||||
resourceType: 'tenant_triage_review',
|
||||
resourceType: 'managed_environment_triage_review',
|
||||
resourceId: (string) $review->getKey(),
|
||||
targetLabel: $tenant->name,
|
||||
tenant: $tenant,
|
||||
@ -156,7 +156,7 @@ private function summaryFor(string $concernFamily, string $manualState): string
|
||||
default => 'Portfolio concern',
|
||||
};
|
||||
|
||||
$state = $manualState === TenantTriageReview::STATE_REVIEWED
|
||||
$state = $manualState === ManagedEnvironmentTriageReview::STATE_REVIEWED
|
||||
? 'reviewed'
|
||||
: 'follow-up needed';
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
use App\Models\OperationRun;
|
||||
use App\Models\ReviewPack;
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantReview;
|
||||
use App\Models\EnvironmentReview;
|
||||
use App\Models\User;
|
||||
use App\Services\Audit\WorkspaceAuditLogger;
|
||||
use App\Services\Entitlements\WorkspaceCommercialLifecycleResolver;
|
||||
@ -136,7 +136,7 @@ public function generate(ManagedEnvironment $tenant, User $user, array $options
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function generateFromReview(TenantReview $review, User $user, array $options = []): ReviewPack
|
||||
public function generateFromReview(EnvironmentReview $review, User $user, array $options = []): ReviewPack
|
||||
{
|
||||
$review->loadMissing(['tenant', 'evidenceSnapshot', 'sections']);
|
||||
|
||||
@ -155,7 +155,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
|
||||
|
||||
if ($existing instanceof ReviewPack) {
|
||||
$this->logReviewExport($review, $user, $existing, 'reused');
|
||||
$this->recordReviewPackRequestTelemetry($existing, $user, 'tenant_review');
|
||||
$this->recordReviewPackRequestTelemetry($existing, $user, 'environment_review');
|
||||
|
||||
return $existing;
|
||||
}
|
||||
@ -164,7 +164,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
|
||||
tenant: $tenant,
|
||||
type: OperationRunType::ReviewPackGenerate->value,
|
||||
inputs: [
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'include_pii' => $options['include_pii'],
|
||||
'include_operations' => $options['include_operations'],
|
||||
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
||||
@ -177,7 +177,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
|
||||
|
||||
if ($queuedPack instanceof ReviewPack) {
|
||||
$this->logReviewExport($review, $user, $queuedPack, 'reused_active_run');
|
||||
$this->recordReviewPackRequestTelemetry($queuedPack, $user, 'tenant_review');
|
||||
$this->recordReviewPackRequestTelemetry($queuedPack, $user, 'environment_review');
|
||||
|
||||
return $queuedPack;
|
||||
}
|
||||
@ -186,14 +186,14 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
|
||||
$reviewPack = ReviewPack::create([
|
||||
'managed_environment_id' => (int) $tenant->getKey(),
|
||||
'workspace_id' => (int) $tenant->workspace_id,
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'operation_run_id' => (int) $operationRun->getKey(),
|
||||
'evidence_snapshot_id' => (int) $snapshot->getKey(),
|
||||
'initiated_by_user_id' => (int) $user->getKey(),
|
||||
'status' => ReviewPackStatus::Queued->value,
|
||||
'options' => $options,
|
||||
'summary' => [
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'review_status' => (string) $review->status,
|
||||
'review_completeness_state' => (string) $review->completeness_state,
|
||||
'section_count' => $review->sections->count(),
|
||||
@ -226,7 +226,7 @@ public function generateFromReview(TenantReview $review, User $user, array $opti
|
||||
});
|
||||
|
||||
$this->logReviewExport($review, $user, $reviewPack, 'queued');
|
||||
$this->recordReviewPackRequestTelemetry($reviewPack, $user, 'tenant_review');
|
||||
$this->recordReviewPackRequestTelemetry($reviewPack, $user, 'environment_review');
|
||||
|
||||
return $reviewPack;
|
||||
}
|
||||
@ -308,10 +308,10 @@ public function findExistingPack(ManagedEnvironment $tenant, string $fingerprint
|
||||
->first();
|
||||
}
|
||||
|
||||
public function findExistingPackForReview(TenantReview $review, string $fingerprint): ?ReviewPack
|
||||
public function findExistingPackForReview(EnvironmentReview $review, string $fingerprint): ?ReviewPack
|
||||
{
|
||||
return ReviewPack::query()
|
||||
->where('tenant_review_id', (int) $review->getKey())
|
||||
->where('environment_review_id', (int) $review->getKey())
|
||||
->ready()
|
||||
->where('fingerprint', $fingerprint)
|
||||
->where('expires_at', '>', now())
|
||||
@ -330,12 +330,12 @@ public function checkActiveRun(ManagedEnvironment $tenant): bool
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function checkActiveRunForReview(TenantReview $review): bool
|
||||
public function checkActiveRunForReview(EnvironmentReview $review): bool
|
||||
{
|
||||
return OperationRun::query()
|
||||
->where('managed_environment_id', (int) $review->managed_environment_id)
|
||||
->where('type', OperationRunType::ReviewPackGenerate->value)
|
||||
->whereJsonContains('context->tenant_review_id', (int) $review->getKey())
|
||||
->whereJsonContains('context->environment_review_id', (int) $review->getKey())
|
||||
->active()
|
||||
->exists();
|
||||
}
|
||||
@ -376,10 +376,10 @@ private function computeFingerprintForSnapshot(EvidenceSnapshot $snapshot, array
|
||||
return hash('sha256', json_encode($data, JSON_THROW_ON_ERROR));
|
||||
}
|
||||
|
||||
public function computeFingerprintForReview(TenantReview $review, array $options): string
|
||||
public function computeFingerprintForReview(EnvironmentReview $review, array $options): string
|
||||
{
|
||||
$data = [
|
||||
'tenant_review_id' => (int) $review->getKey(),
|
||||
'environment_review_id' => (int) $review->getKey(),
|
||||
'review_fingerprint' => (string) $review->fingerprint,
|
||||
'review_status' => (string) $review->status,
|
||||
'delivery_contract' => self::REVIEW_DERIVED_DELIVERY_CONTRACT,
|
||||
@ -414,7 +414,7 @@ private function findPackForRun(ManagedEnvironment $tenant, OperationRun $operat
|
||||
->first();
|
||||
}
|
||||
|
||||
private function logReviewExport(TenantReview $review, User $user, ReviewPack $reviewPack, string $mode): void
|
||||
private function logReviewExport(EnvironmentReview $review, User $user, ReviewPack $reviewPack, string $mode): void
|
||||
{
|
||||
$tenant = $review->tenant;
|
||||
|
||||
@ -424,7 +424,7 @@ private function logReviewExport(TenantReview $review, User $user, ReviewPack $r
|
||||
|
||||
$this->auditLogger->log(
|
||||
workspace: $tenant->workspace,
|
||||
action: AuditActionId::TenantReviewExported,
|
||||
action: AuditActionId::EnvironmentReviewExported,
|
||||
context: [
|
||||
'metadata' => [
|
||||
'review_id' => (int) $review->getKey(),
|
||||
@ -434,7 +434,7 @@ private function logReviewExport(TenantReview $review, User $user, ReviewPack $r
|
||||
],
|
||||
],
|
||||
actor: $user,
|
||||
resourceType: 'tenant_review',
|
||||
resourceType: 'environment_review',
|
||||
resourceId: (string) $review->getKey(),
|
||||
targetLabel: sprintf('ManagedEnvironment review #%d', (int) $review->getKey()),
|
||||
operationRunId: $reviewPack->operation_run_id,
|
||||
|
||||
@ -19,8 +19,8 @@ final class OperationRunTriageService
|
||||
'directory.groups.sync',
|
||||
'rbac.health_check',
|
||||
'entra.admin_roles.scan',
|
||||
'tenant.review_pack.generate',
|
||||
'tenant.review.compose',
|
||||
'environment.review_pack.generate',
|
||||
'environment.review.compose',
|
||||
];
|
||||
|
||||
private const CANCELABLE_TYPES = [
|
||||
@ -29,8 +29,8 @@ final class OperationRunTriageService
|
||||
'directory.groups.sync',
|
||||
'rbac.health_check',
|
||||
'entra.admin_roles.scan',
|
||||
'tenant.review_pack.generate',
|
||||
'tenant.review.compose',
|
||||
'environment.review_pack.generate',
|
||||
'environment.review.compose',
|
||||
];
|
||||
|
||||
public function __construct(
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
namespace App\Services\Tenants;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\User;
|
||||
use App\Services\Onboarding\OnboardingLifecycleService;
|
||||
use App\Support\Audit\AuditActionId;
|
||||
@ -38,7 +38,7 @@ public function buildContext(ManagedEnvironment $tenant, TenantActionSurface $su
|
||||
$workspaceId = request() !== null
|
||||
? $this->workspaceContext->currentWorkspaceId(request())
|
||||
: null;
|
||||
$resumeOutcome = $draft instanceof TenantOnboardingSession
|
||||
$resumeOutcome = $draft instanceof ManagedEnvironmentOnboardingSession
|
||||
? $this->tenantOperabilityService->outcomeFor(
|
||||
tenant: $tenant,
|
||||
question: TenantOperabilityQuestion::ResumeOnboardingEligibility,
|
||||
@ -58,7 +58,7 @@ public function buildContext(ManagedEnvironment $tenant, TenantActionSurface $su
|
||||
lane: $lane,
|
||||
relatedOnboardingDraft: $draft,
|
||||
relatedOnboardingIsResumable: $resumeOutcome?->allowed ?? false,
|
||||
hasRelatedOnboardingDraft: $draft instanceof TenantOnboardingSession,
|
||||
hasRelatedOnboardingDraft: $draft instanceof ManagedEnvironmentOnboardingSession,
|
||||
isArchived: $lifecycle->canRestore(),
|
||||
);
|
||||
}
|
||||
@ -130,7 +130,7 @@ public function onboardingEntryDescriptor(int $resumableDraftCount): TenantActio
|
||||
};
|
||||
}
|
||||
|
||||
public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user = null): ?TenantOnboardingSession
|
||||
public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user = null): ?ManagedEnvironmentOnboardingSession
|
||||
{
|
||||
$user ??= auth()->user();
|
||||
|
||||
@ -138,12 +138,12 @@ public function relatedOnboardingDraft(ManagedEnvironment $tenant, ?User $user =
|
||||
return null;
|
||||
}
|
||||
|
||||
return TenantOnboardingSession::query()
|
||||
return ManagedEnvironmentOnboardingSession::query()
|
||||
->where('workspace_id', (int) $tenant->workspace_id)
|
||||
->where('managed_environment_id', (int) $tenant->getKey())
|
||||
->orderByDesc('updated_at')
|
||||
->get()
|
||||
->first(fn (TenantOnboardingSession $draft): bool => Gate::forUser($user)->allows('view', $draft));
|
||||
->first(fn (ManagedEnvironmentOnboardingSession $draft): bool => Gate::forUser($user)->allows('view', $draft));
|
||||
}
|
||||
|
||||
private function viewAction(): TenantActionDescriptor
|
||||
@ -267,7 +267,7 @@ private function relatedOnboardingActionForContext(
|
||||
): ?TenantActionDescriptor {
|
||||
$draft = $context->relatedOnboardingDraft;
|
||||
|
||||
if (! $draft instanceof TenantOnboardingSession) {
|
||||
if (! $draft instanceof ManagedEnvironmentOnboardingSession) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
namespace App\Services\Tenants;
|
||||
|
||||
use App\Models\ManagedEnvironment;
|
||||
use App\Models\TenantOnboardingSession;
|
||||
use App\Models\ManagedEnvironmentOnboardingSession;
|
||||
use App\Models\User;
|
||||
use App\Services\Auth\CapabilityResolver;
|
||||
use App\Support\ReasonTranslation\ReasonResolutionEnvelope;
|
||||
@ -186,7 +186,7 @@ public function outcomeFor(
|
||||
?User $actor = null,
|
||||
?int $workspaceId = null,
|
||||
TenantInteractionLane $lane = TenantInteractionLane::AdministrativeManagement,
|
||||
?TenantOnboardingSession $onboardingDraft = null,
|
||||
?ManagedEnvironmentOnboardingSession $onboardingDraft = null,
|
||||
?string $requiredCapability = null,
|
||||
?ManagedEnvironment $selectedTenant = null,
|
||||
?string $linkedRecordType = null,
|
||||
@ -478,7 +478,7 @@ private function resumeOnboardingOutcome(TenantOperabilityContext $context, Tena
|
||||
);
|
||||
}
|
||||
|
||||
if ($context->onboardingDraft instanceof TenantOnboardingSession && ! $context->onboardingDraft->isWorkflowResumable()) {
|
||||
if ($context->onboardingDraft instanceof ManagedEnvironmentOnboardingSession && ! $context->onboardingDraft->isWorkflowResumable()) {
|
||||
return TenantOperabilityOutcome::deny(
|
||||
question: TenantOperabilityQuestion::ResumeOnboardingEligibility,
|
||||
lifecycle: $lifecycle,
|
||||
|
||||
@ -34,19 +34,19 @@ enum AuditActionId: string
|
||||
case PolicyProviderMissingDetected = 'policy.provider_missing_detected';
|
||||
case PolicyProviderMissingCleared = 'policy.provider_missing_cleared';
|
||||
|
||||
// Managed tenant onboarding wizard.
|
||||
case ManagedTenantOnboardingStart = 'managed_tenant_onboarding.start';
|
||||
case ManagedTenantOnboardingResume = 'managed_tenant_onboarding.resume';
|
||||
case ManagedTenantOnboardingDraftSelected = 'managed_tenant_onboarding.draft_selected';
|
||||
case ManagedTenantOnboardingDraftUpdated = 'managed_tenant_onboarding.draft_updated';
|
||||
case ManagedTenantOnboardingProviderConnectionChanged = 'managed_tenant_onboarding.provider_connection_changed';
|
||||
case ManagedTenantOnboardingVerificationStart = 'managed_tenant_onboarding.verification_start';
|
||||
case ManagedTenantOnboardingVerificationPersisted = 'managed_tenant_onboarding.verification_persisted';
|
||||
case ManagedTenantOnboardingBootstrapStarted = 'managed_tenant_onboarding.bootstrap_started';
|
||||
case ManagedTenantOnboardingCancelled = 'managed_tenant_onboarding.cancelled';
|
||||
case ManagedTenantOnboardingDeleted = 'managed_tenant_onboarding.deleted';
|
||||
case ManagedTenantOnboardingActivationOverrideUsed = 'managed_tenant_onboarding.activation_override_used';
|
||||
case ManagedTenantOnboardingActivation = 'managed_tenant_onboarding.activation';
|
||||
// Managed environment onboarding wizard.
|
||||
case ManagedEnvironmentOnboardingStart = 'managed_environment_onboarding.start';
|
||||
case ManagedEnvironmentOnboardingResume = 'managed_environment_onboarding.resume';
|
||||
case ManagedEnvironmentOnboardingDraftSelected = 'managed_environment_onboarding.draft_selected';
|
||||
case ManagedEnvironmentOnboardingDraftUpdated = 'managed_environment_onboarding.draft_updated';
|
||||
case ManagedEnvironmentOnboardingProviderConnectionChanged = 'managed_environment_onboarding.provider_connection_changed';
|
||||
case ManagedEnvironmentOnboardingVerificationStart = 'managed_environment_onboarding.verification_start';
|
||||
case ManagedEnvironmentOnboardingVerificationPersisted = 'managed_environment_onboarding.verification_persisted';
|
||||
case ManagedEnvironmentOnboardingBootstrapStarted = 'managed_environment_onboarding.bootstrap_started';
|
||||
case ManagedEnvironmentOnboardingCancelled = 'managed_environment_onboarding.cancelled';
|
||||
case ManagedEnvironmentOnboardingDeleted = 'managed_environment_onboarding.deleted';
|
||||
case ManagedEnvironmentOnboardingActivationOverrideUsed = 'managed_environment_onboarding.activation_override_used';
|
||||
case ManagedEnvironmentOnboardingActivation = 'managed_environment_onboarding.activation';
|
||||
case VerificationCompleted = 'verification.completed';
|
||||
case VerificationCheckAcknowledged = 'verification.check_acknowledged';
|
||||
|
||||
@ -79,9 +79,9 @@ enum AuditActionId: string
|
||||
case BaselineCompareStarted = 'baseline_compare.started';
|
||||
case BaselineCompareCompleted = 'baseline_compare.completed';
|
||||
case BaselineCompareFailed = 'baseline_compare.failed';
|
||||
case CrossTenantPromotionPreflightGenerated = 'cross_tenant_promotion_preflight.generated';
|
||||
case CrossTenantPromotionExecutionQueued = 'cross_tenant_promotion_execution.queued';
|
||||
case CrossTenantPromotionExecutionCompleted = 'cross_tenant_promotion_execution.completed';
|
||||
case CrossEnvironmentPromotionPreflightGenerated = 'cross_environment_promotion_preflight.generated';
|
||||
case CrossEnvironmentPromotionExecutionQueued = 'cross_environment_promotion_execution.queued';
|
||||
case CrossEnvironmentPromotionExecutionCompleted = 'cross_environment_promotion_execution.completed';
|
||||
case BaselineAssignmentCreated = 'baseline_assignment.created';
|
||||
case BaselineAssignmentUpdated = 'baseline_assignment.updated';
|
||||
case BaselineAssignmentDeleted = 'baseline_assignment.deleted';
|
||||
@ -104,17 +104,17 @@ enum AuditActionId: string
|
||||
case EvidenceSnapshotRefreshed = 'evidence_snapshot.refreshed';
|
||||
case EvidenceSnapshotExpired = 'evidence_snapshot.expired';
|
||||
case EvidenceSnapshotOpened = 'evidence_snapshot.opened';
|
||||
case TenantReviewCreated = 'tenant_review.created';
|
||||
case TenantReviewRefreshed = 'tenant_review.refreshed';
|
||||
case TenantReviewPublished = 'tenant_review.published';
|
||||
case TenantReviewArchived = 'tenant_review.archived';
|
||||
case TenantReviewOpened = 'tenant_review.opened';
|
||||
case TenantReviewExported = 'tenant_review.exported';
|
||||
case TenantReviewSuccessorCreated = 'tenant_review.successor_created';
|
||||
case EnvironmentReviewCreated = 'environment_review.created';
|
||||
case EnvironmentReviewRefreshed = 'environment_review.refreshed';
|
||||
case EnvironmentReviewPublished = 'environment_review.published';
|
||||
case EnvironmentReviewArchived = 'environment_review.archived';
|
||||
case EnvironmentReviewOpened = 'environment_review.opened';
|
||||
case EnvironmentReviewExported = 'environment_review.exported';
|
||||
case EnvironmentReviewSuccessorCreated = 'environment_review.successor_created';
|
||||
case CustomerReviewWorkspaceOpened = 'customer_review_workspace.opened';
|
||||
case ReviewPackDownloaded = 'review_pack.downloaded';
|
||||
case TenantTriageReviewMarkedReviewed = 'tenant_triage_review.marked_reviewed';
|
||||
case TenantTriageReviewMarkedFollowUpNeeded = 'tenant_triage_review.marked_follow_up_needed';
|
||||
case ManagedEnvironmentTriageReviewMarkedReviewed = 'managed_environment_triage_review.marked_reviewed';
|
||||
case ManagedEnvironmentTriageReviewMarkedFollowUpNeeded = 'managed_environment_triage_review.marked_follow_up_needed';
|
||||
|
||||
case SupportDiagnosticsOpened = 'support_diagnostics.opened';
|
||||
case SupportRequestCreated = 'support_request.created';
|
||||
@ -209,18 +209,18 @@ private static function labels(): array
|
||||
self::ManagedEnvironmentAccessScopeRemove->value => 'ManagedEnvironment access scope removal',
|
||||
self::PolicyProviderMissingDetected->value => 'Policy provider missing detected',
|
||||
self::PolicyProviderMissingCleared->value => 'Policy provider missing cleared',
|
||||
self::ManagedTenantOnboardingStart->value => 'Managed tenant onboarding start',
|
||||
self::ManagedTenantOnboardingResume->value => 'Managed tenant onboarding resume',
|
||||
self::ManagedTenantOnboardingDraftSelected->value => 'Managed tenant onboarding draft selected',
|
||||
self::ManagedTenantOnboardingDraftUpdated->value => 'Managed tenant onboarding draft updated',
|
||||
self::ManagedTenantOnboardingProviderConnectionChanged->value => 'Managed tenant onboarding provider connection changed',
|
||||
self::ManagedTenantOnboardingVerificationStart->value => 'Managed tenant onboarding verification start',
|
||||
self::ManagedTenantOnboardingVerificationPersisted->value => 'Managed tenant onboarding verification persisted',
|
||||
self::ManagedTenantOnboardingBootstrapStarted->value => 'Managed tenant onboarding bootstrap started',
|
||||
self::ManagedTenantOnboardingCancelled->value => 'Managed tenant onboarding cancelled',
|
||||
self::ManagedTenantOnboardingDeleted->value => 'Managed tenant onboarding deleted',
|
||||
self::ManagedTenantOnboardingActivationOverrideUsed->value => 'Managed tenant onboarding activation override used',
|
||||
self::ManagedTenantOnboardingActivation->value => 'Managed tenant onboarding activation',
|
||||
self::ManagedEnvironmentOnboardingStart->value => 'Managed environment onboarding start',
|
||||
self::ManagedEnvironmentOnboardingResume->value => 'Managed environment onboarding resume',
|
||||
self::ManagedEnvironmentOnboardingDraftSelected->value => 'Managed environment onboarding draft selected',
|
||||
self::ManagedEnvironmentOnboardingDraftUpdated->value => 'Managed environment onboarding draft updated',
|
||||
self::ManagedEnvironmentOnboardingProviderConnectionChanged->value => 'Managed environment onboarding provider connection changed',
|
||||
self::ManagedEnvironmentOnboardingVerificationStart->value => 'Managed environment onboarding verification start',
|
||||
self::ManagedEnvironmentOnboardingVerificationPersisted->value => 'Managed environment onboarding verification persisted',
|
||||
self::ManagedEnvironmentOnboardingBootstrapStarted->value => 'Managed environment onboarding bootstrap started',
|
||||
self::ManagedEnvironmentOnboardingCancelled->value => 'Managed environment onboarding cancelled',
|
||||
self::ManagedEnvironmentOnboardingDeleted->value => 'Managed environment onboarding deleted',
|
||||
self::ManagedEnvironmentOnboardingActivationOverrideUsed->value => 'Managed environment onboarding activation override used',
|
||||
self::ManagedEnvironmentOnboardingActivation->value => 'Managed environment onboarding activation',
|
||||
self::VerificationCompleted->value => 'Verification completed',
|
||||
self::VerificationCheckAcknowledged->value => 'Verification check acknowledged',
|
||||
self::AlertDestinationCreated->value => 'Alert destination created',
|
||||
@ -249,9 +249,9 @@ private static function labels(): array
|
||||
self::BaselineCompareStarted->value => 'Baseline compare started',
|
||||
self::BaselineCompareCompleted->value => 'Baseline compare completed',
|
||||
self::BaselineCompareFailed->value => 'Baseline compare failed',
|
||||
self::CrossTenantPromotionPreflightGenerated->value => 'Cross-tenant promotion preflight generated',
|
||||
self::CrossTenantPromotionExecutionQueued->value => 'Cross-tenant promotion execution queued',
|
||||
self::CrossTenantPromotionExecutionCompleted->value => 'Cross-tenant promotion execution completed',
|
||||
self::CrossEnvironmentPromotionPreflightGenerated->value => 'Cross-environment promotion preflight generated',
|
||||
self::CrossEnvironmentPromotionExecutionQueued->value => 'Cross-environment promotion execution queued',
|
||||
self::CrossEnvironmentPromotionExecutionCompleted->value => 'Cross-environment promotion execution completed',
|
||||
self::BaselineAssignmentCreated->value => 'Baseline assignment created',
|
||||
self::BaselineAssignmentUpdated->value => 'Baseline assignment updated',
|
||||
self::BaselineAssignmentDeleted->value => 'Baseline assignment deleted',
|
||||
@ -274,17 +274,17 @@ private static function labels(): array
|
||||
self::EvidenceSnapshotRefreshed->value => 'Evidence snapshot refreshed',
|
||||
self::EvidenceSnapshotExpired->value => 'Evidence snapshot expired',
|
||||
self::EvidenceSnapshotOpened->value => 'Evidence snapshot opened',
|
||||
self::TenantReviewCreated->value => 'ManagedEnvironment review created',
|
||||
self::TenantReviewRefreshed->value => 'ManagedEnvironment review refreshed',
|
||||
self::TenantReviewPublished->value => 'ManagedEnvironment review published',
|
||||
self::TenantReviewArchived->value => 'ManagedEnvironment review archived',
|
||||
self::TenantReviewOpened->value => 'ManagedEnvironment review opened',
|
||||
self::TenantReviewExported->value => 'ManagedEnvironment review exported',
|
||||
self::TenantReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
|
||||
self::EnvironmentReviewCreated->value => 'ManagedEnvironment review created',
|
||||
self::EnvironmentReviewRefreshed->value => 'ManagedEnvironment review refreshed',
|
||||
self::EnvironmentReviewPublished->value => 'ManagedEnvironment review published',
|
||||
self::EnvironmentReviewArchived->value => 'ManagedEnvironment review archived',
|
||||
self::EnvironmentReviewOpened->value => 'ManagedEnvironment review opened',
|
||||
self::EnvironmentReviewExported->value => 'ManagedEnvironment review exported',
|
||||
self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
|
||||
self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened',
|
||||
self::ReviewPackDownloaded->value => 'Review pack downloaded',
|
||||
self::TenantTriageReviewMarkedReviewed->value => 'Triage review marked reviewed',
|
||||
self::TenantTriageReviewMarkedFollowUpNeeded->value => 'Triage review marked follow-up needed',
|
||||
self::ManagedEnvironmentTriageReviewMarkedReviewed->value => 'Triage review marked reviewed',
|
||||
self::ManagedEnvironmentTriageReviewMarkedFollowUpNeeded->value => 'Triage review marked follow-up needed',
|
||||
self::SupportDiagnosticsOpened->value => 'Support diagnostics opened',
|
||||
self::SupportRequestCreated->value => 'Support request created',
|
||||
self::SupportRequestExternalTicketCreated->value => 'Support request external ticket created',
|
||||
@ -362,9 +362,9 @@ private static function summaries(): array
|
||||
self::BaselineProfileUpdated->value => 'Baseline profile updated',
|
||||
self::BaselineProfileArchived->value => 'Baseline profile archived',
|
||||
self::BaselineProfileScopeBackfilled->value => 'Baseline profile scope backfilled',
|
||||
self::CrossTenantPromotionPreflightGenerated->value => 'Cross-tenant promotion preflight generated',
|
||||
self::CrossTenantPromotionExecutionQueued->value => 'Cross-tenant promotion execution queued',
|
||||
self::CrossTenantPromotionExecutionCompleted->value => 'Cross-tenant promotion execution completed',
|
||||
self::CrossEnvironmentPromotionPreflightGenerated->value => 'Cross-environment promotion preflight generated',
|
||||
self::CrossEnvironmentPromotionExecutionQueued->value => 'Cross-environment promotion execution queued',
|
||||
self::CrossEnvironmentPromotionExecutionCompleted->value => 'Cross-environment promotion execution completed',
|
||||
self::AlertDestinationCreated->value => 'Alert destination created',
|
||||
self::AlertDestinationUpdated->value => 'Alert destination updated',
|
||||
self::AlertDestinationDeleted->value => 'Alert destination deleted',
|
||||
@ -388,13 +388,13 @@ private static function summaries(): array
|
||||
self::EvidenceSnapshotRefreshed->value => 'Evidence snapshot refreshed',
|
||||
self::EvidenceSnapshotExpired->value => 'Evidence snapshot expired',
|
||||
self::EvidenceSnapshotOpened->value => 'Evidence snapshot opened',
|
||||
self::TenantReviewCreated->value => 'ManagedEnvironment review created',
|
||||
self::TenantReviewRefreshed->value => 'ManagedEnvironment review refreshed',
|
||||
self::TenantReviewPublished->value => 'ManagedEnvironment review published',
|
||||
self::TenantReviewArchived->value => 'ManagedEnvironment review archived',
|
||||
self::TenantReviewOpened->value => 'ManagedEnvironment review opened',
|
||||
self::TenantReviewExported->value => 'ManagedEnvironment review exported',
|
||||
self::TenantReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
|
||||
self::EnvironmentReviewCreated->value => 'ManagedEnvironment review created',
|
||||
self::EnvironmentReviewRefreshed->value => 'ManagedEnvironment review refreshed',
|
||||
self::EnvironmentReviewPublished->value => 'ManagedEnvironment review published',
|
||||
self::EnvironmentReviewArchived->value => 'ManagedEnvironment review archived',
|
||||
self::EnvironmentReviewOpened->value => 'ManagedEnvironment review opened',
|
||||
self::EnvironmentReviewExported->value => 'ManagedEnvironment review exported',
|
||||
self::EnvironmentReviewSuccessorCreated->value => 'ManagedEnvironment review next cycle created',
|
||||
self::CustomerReviewWorkspaceOpened->value => 'Customer review workspace opened',
|
||||
self::ReviewPackDownloaded->value => 'Review pack downloaded',
|
||||
self::SupportDiagnosticsOpened->value => 'Support diagnostics opened',
|
||||
|
||||
@ -27,28 +27,28 @@ class Capabilities
|
||||
|
||||
public const WORKSPACE_MEMBERSHIP_MANAGE = 'workspace_membership.manage';
|
||||
|
||||
// Managed tenant onboarding
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD = 'workspace_managed_tenant.onboard';
|
||||
// Managed environment onboarding
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD = 'workspace_managed_environment.onboard';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_IDENTIFY = 'workspace_managed_tenant.onboard.identify';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_IDENTIFY = 'workspace_managed_environment.onboard.identify';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CANCEL = 'workspace_managed_tenant.onboard.cancel';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CANCEL = 'workspace_managed_environment.onboard.cancel';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_VIEW = 'workspace_managed_tenant.onboard.connection.view';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_VIEW = 'workspace_managed_environment.onboard.connection.view';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE = 'workspace_managed_tenant.onboard.connection.manage';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE = 'workspace_managed_environment.onboard.connection.manage';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_CONNECTION_MANAGE_DEDICATED = 'workspace_managed_tenant.onboard.connection.manage_dedicated';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_CONNECTION_MANAGE_DEDICATED = 'workspace_managed_environment.onboard.connection.manage_dedicated';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_VERIFICATION_START = 'workspace_managed_tenant.onboard.verification.start';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_VERIFICATION_START = 'workspace_managed_environment.onboard.verification.start';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.inventory_sync';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_INVENTORY_SYNC = 'workspace_managed_environment.onboard.bootstrap.inventory_sync';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_POLICY_SYNC = 'workspace_managed_tenant.onboard.bootstrap.policy_sync';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_POLICY_SYNC = 'workspace_managed_environment.onboard.bootstrap.policy_sync';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP = 'workspace_managed_tenant.onboard.bootstrap.backup_bootstrap';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_BOOTSTRAP_BACKUP_BOOTSTRAP = 'workspace_managed_environment.onboard.bootstrap.backup_bootstrap';
|
||||
|
||||
public const WORKSPACE_MANAGED_TENANT_ONBOARD_ACTIVATE = 'workspace_managed_tenant.onboard.activate';
|
||||
public const WORKSPACE_MANAGED_ENVIRONMENT_ONBOARD_ACTIVATE = 'workspace_managed_environment.onboard.activate';
|
||||
|
||||
// Workspace settings
|
||||
public const WORKSPACE_SETTINGS_VIEW = 'workspace_settings.view';
|
||||
@ -146,12 +146,12 @@ class Capabilities
|
||||
public const REVIEW_PACK_MANAGE = 'review_pack.manage';
|
||||
|
||||
// ManagedEnvironment reviews
|
||||
public const TENANT_REVIEW_VIEW = 'tenant_review.view';
|
||||
public const ENVIRONMENT_REVIEW_VIEW = 'environment_review.view';
|
||||
|
||||
public const TENANT_REVIEW_MANAGE = 'tenant_review.manage';
|
||||
public const ENVIRONMENT_REVIEW_MANAGE = 'environment_review.manage';
|
||||
|
||||
// Portfolio triage review progress
|
||||
public const TENANT_TRIAGE_REVIEW_MANAGE = 'tenant_triage_review.manage';
|
||||
public const MANAGED_ENVIRONMENT_TRIAGE_REVIEW_MANAGE = 'managed_environment_triage_review.manage';
|
||||
|
||||
// Evidence snapshots
|
||||
public const EVIDENCE_VIEW = 'evidence.view';
|
||||
|
||||
@ -42,7 +42,7 @@ final class BadgeCatalog
|
||||
BadgeDomain::TenantStatus->value => Domains\TenantStatusBadge::class,
|
||||
BadgeDomain::TenantWorkspacePosture->value => Domains\TenantWorkspacePostureBadge::class,
|
||||
BadgeDomain::TenantRbacStatus->value => Domains\TenantRbacStatusBadge::class,
|
||||
BadgeDomain::TenantPermissionStatus->value => Domains\TenantPermissionStatusBadge::class,
|
||||
BadgeDomain::ManagedEnvironmentPermissionStatus->value => Domains\ManagedEnvironmentPermissionStatusBadge::class,
|
||||
BadgeDomain::PolicySnapshotMode->value => Domains\PolicySnapshotModeBadge::class,
|
||||
BadgeDomain::PolicyRestoreMode->value => Domains\PolicyRestoreModeBadge::class,
|
||||
BadgeDomain::PolicyRisk->value => Domains\PolicyRiskBadge::class,
|
||||
@ -53,7 +53,7 @@ final class BadgeCatalog
|
||||
BadgeDomain::ProviderConsentStatus->value => Domains\ProviderConsentStatusBadge::class,
|
||||
BadgeDomain::ProviderVerificationStatus->value => Domains\ProviderVerificationStatusBadge::class,
|
||||
BadgeDomain::ProviderCapabilityStatus->value => Domains\ProviderCapabilityStatusBadge::class,
|
||||
BadgeDomain::ManagedTenantOnboardingVerificationStatus->value => Domains\ManagedTenantOnboardingVerificationStatusBadge::class,
|
||||
BadgeDomain::ManagedEnvironmentOnboardingVerificationStatus->value => Domains\ManagedEnvironmentOnboardingVerificationStatusBadge::class,
|
||||
BadgeDomain::VerificationCheckStatus->value => Domains\VerificationCheckStatusBadge::class,
|
||||
BadgeDomain::VerificationCheckSeverity->value => Domains\VerificationCheckSeverityBadge::class,
|
||||
BadgeDomain::VerificationReportOverall->value => Domains\VerificationReportOverallBadge::class,
|
||||
@ -66,9 +66,9 @@ final class BadgeCatalog
|
||||
BadgeDomain::CommercialLifecycleState->value => Domains\CommercialLifecycleStateBadge::class,
|
||||
BadgeDomain::EvidenceSnapshotStatus->value => Domains\EvidenceSnapshotStatusBadge::class,
|
||||
BadgeDomain::EvidenceCompleteness->value => Domains\EvidenceCompletenessBadge::class,
|
||||
BadgeDomain::TenantReviewStatus->value => Domains\TenantReviewStatusBadge::class,
|
||||
BadgeDomain::TenantReviewCompleteness->value => Domains\TenantReviewCompletenessStateBadge::class,
|
||||
BadgeDomain::TenantTriageReviewState->value => Domains\TenantTriageReviewStateBadge::class,
|
||||
BadgeDomain::EnvironmentReviewStatus->value => Domains\EnvironmentReviewStatusBadge::class,
|
||||
BadgeDomain::EnvironmentReviewCompleteness->value => Domains\EnvironmentReviewCompletenessStateBadge::class,
|
||||
BadgeDomain::ManagedEnvironmentTriageReviewState->value => Domains\ManagedEnvironmentTriageReviewStateBadge::class,
|
||||
BadgeDomain::SystemHealth->value => Domains\SystemHealthBadge::class,
|
||||
BadgeDomain::ReferenceResolutionState->value => Domains\ReferenceResolutionStateBadge::class,
|
||||
BadgeDomain::DiffRowStatus->value => Domains\DiffRowStatusBadge::class,
|
||||
@ -204,7 +204,7 @@ public static function normalizeProviderVerificationStatus(mixed $value): ?strin
|
||||
};
|
||||
}
|
||||
|
||||
public static function normalizeManagedTenantOnboardingVerificationStatus(mixed $value): ?string
|
||||
public static function normalizeManagedEnvironmentOnboardingVerificationStatus(mixed $value): ?string
|
||||
{
|
||||
$state = self::normalizeState($value);
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ enum BadgeDomain: string
|
||||
case TenantStatus = 'tenant_status';
|
||||
case TenantWorkspacePosture = 'tenant_workspace_posture';
|
||||
case TenantRbacStatus = 'tenant_rbac_status';
|
||||
case TenantPermissionStatus = 'tenant_permission_status';
|
||||
case ManagedEnvironmentPermissionStatus = 'managed_environment_permission_status';
|
||||
case PolicySnapshotMode = 'policy_snapshot_mode';
|
||||
case PolicyRestoreMode = 'policy_restore_mode';
|
||||
case PolicyRisk = 'policy_risk';
|
||||
@ -44,7 +44,7 @@ enum BadgeDomain: string
|
||||
case ProviderConsentStatus = 'provider_connection.consent_status';
|
||||
case ProviderVerificationStatus = 'provider_connection.verification_status';
|
||||
case ProviderCapabilityStatus = 'provider_capability_status';
|
||||
case ManagedTenantOnboardingVerificationStatus = 'managed_tenant_onboarding.verification_status';
|
||||
case ManagedEnvironmentOnboardingVerificationStatus = 'managed_environment_onboarding.verification_status';
|
||||
case VerificationCheckStatus = 'verification_check_status';
|
||||
case VerificationCheckSeverity = 'verification_check_severity';
|
||||
case VerificationReportOverall = 'verification_report_overall';
|
||||
@ -57,9 +57,9 @@ enum BadgeDomain: string
|
||||
case CommercialLifecycleState = 'commercial_lifecycle_state';
|
||||
case EvidenceSnapshotStatus = 'evidence_snapshot_status';
|
||||
case EvidenceCompleteness = 'evidence_completeness';
|
||||
case TenantReviewStatus = 'tenant_review_status';
|
||||
case TenantReviewCompleteness = 'tenant_review_completeness';
|
||||
case TenantTriageReviewState = 'tenant_triage_review_state';
|
||||
case EnvironmentReviewStatus = 'environment_review_status';
|
||||
case EnvironmentReviewCompleteness = 'environment_review_completeness';
|
||||
case ManagedEnvironmentTriageReviewState = 'managed_environment_triage_review_state';
|
||||
case SystemHealth = 'system_health';
|
||||
case ReferenceResolutionState = 'reference_resolution_state';
|
||||
case DiffRowStatus = 'diff_row_status';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user