Some checks failed
Main Confidence / confidence (push) Failing after 53s
Automated commit and PR created by Copilot per user request. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #287
327 lines
13 KiB
PHP
327 lines
13 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Entitlements;
|
|
|
|
use App\Models\Tenant;
|
|
use App\Models\Workspace;
|
|
use App\Models\WorkspaceSetting;
|
|
use App\Services\Settings\SettingsResolver;
|
|
use Carbon\CarbonInterface;
|
|
|
|
final class WorkspaceEntitlementResolver
|
|
{
|
|
public const SETTING_DOMAIN = 'entitlements';
|
|
|
|
public const SETTING_PLAN_PROFILE = 'plan_profile';
|
|
|
|
public const SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_VALUE = 'managed_tenant_limit_override_value';
|
|
|
|
public const SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_REASON = 'managed_tenant_limit_override_reason';
|
|
|
|
public const SETTING_REVIEW_PACK_GENERATION_OVERRIDE_VALUE = 'review_pack_generation_override_value';
|
|
|
|
public const SETTING_REVIEW_PACK_GENERATION_OVERRIDE_REASON = 'review_pack_generation_override_reason';
|
|
|
|
public const KEY_MANAGED_TENANT_ACTIVATION_LIMIT = 'managed_tenant_activation_limit';
|
|
|
|
public const KEY_REVIEW_PACK_GENERATION_ENABLED = 'review_pack_generation_enabled';
|
|
|
|
public function __construct(
|
|
private SettingsResolver $settingsResolver,
|
|
private WorkspacePlanProfileCatalog $planProfileCatalog,
|
|
) {}
|
|
|
|
/**
|
|
* @return array{
|
|
* plan_profile: array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool},
|
|
* decisions: array<string, array{
|
|
* workspace_id: int,
|
|
* plan_profile_id: string,
|
|
* plan_profile_label: string,
|
|
* plan_profile_description: string,
|
|
* key: string,
|
|
* effective_value: int|bool,
|
|
* source: 'plan_profile_default'|'workspace_override',
|
|
* rationale: string|null,
|
|
* current_usage: int|null,
|
|
* remaining_capacity: int|null,
|
|
* is_blocked: bool,
|
|
* block_reason: string|null,
|
|
* last_changed_at: CarbonInterface|null,
|
|
* last_changed_by: string|null
|
|
* }>
|
|
* }
|
|
*/
|
|
public function summary(Workspace $workspace): array
|
|
{
|
|
$planProfile = $this->resolvePlanProfile($workspace);
|
|
|
|
return [
|
|
'plan_profile' => $planProfile,
|
|
'decisions' => [
|
|
self::KEY_MANAGED_TENANT_ACTIVATION_LIMIT => $this->resolve($workspace, self::KEY_MANAGED_TENANT_ACTIVATION_LIMIT, $planProfile),
|
|
self::KEY_REVIEW_PACK_GENERATION_ENABLED => $this->resolve($workspace, self::KEY_REVIEW_PACK_GENERATION_ENABLED, $planProfile),
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool}
|
|
*/
|
|
public function resolvePlanProfile(Workspace $workspace): array
|
|
{
|
|
$planProfileId = $this->settingsResolver->resolveValue(
|
|
workspace: $workspace,
|
|
domain: self::SETTING_DOMAIN,
|
|
key: self::SETTING_PLAN_PROFILE,
|
|
);
|
|
|
|
return $this->planProfileCatalog->resolve(is_string($planProfileId) ? $planProfileId : null);
|
|
}
|
|
|
|
/**
|
|
* @param array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool}|null $planProfile
|
|
* @return array{
|
|
* workspace_id: int,
|
|
* plan_profile_id: string,
|
|
* plan_profile_label: string,
|
|
* plan_profile_description: string,
|
|
* key: string,
|
|
* effective_value: int|bool,
|
|
* source: 'plan_profile_default'|'workspace_override',
|
|
* rationale: string|null,
|
|
* current_usage: int|null,
|
|
* remaining_capacity: int|null,
|
|
* is_blocked: bool,
|
|
* block_reason: string|null,
|
|
* last_changed_at: CarbonInterface|null,
|
|
* last_changed_by: string|null
|
|
* }
|
|
*/
|
|
public function resolve(Workspace $workspace, string $key, ?array $planProfile = null): array
|
|
{
|
|
$planProfile ??= $this->resolvePlanProfile($workspace);
|
|
$lastChanged = $this->lastChangedMetadata($workspace);
|
|
|
|
return match ($key) {
|
|
self::KEY_MANAGED_TENANT_ACTIVATION_LIMIT => $this->resolveManagedTenantActivationLimitDecision($workspace, $planProfile, $lastChanged),
|
|
self::KEY_REVIEW_PACK_GENERATION_ENABLED => $this->resolveReviewPackGenerationDecision($workspace, $planProfile, $lastChanged),
|
|
default => throw new \InvalidArgumentException(sprintf('Unknown workspace entitlement key: %s', $key)),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* @param array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool} $planProfile
|
|
* @param array{last_changed_at: CarbonInterface|null, last_changed_by: string|null} $lastChanged
|
|
* @return array{
|
|
* workspace_id: int,
|
|
* plan_profile_id: string,
|
|
* plan_profile_label: string,
|
|
* plan_profile_description: string,
|
|
* key: string,
|
|
* effective_value: int,
|
|
* source: 'plan_profile_default'|'workspace_override',
|
|
* rationale: string|null,
|
|
* current_usage: int,
|
|
* remaining_capacity: int,
|
|
* is_blocked: bool,
|
|
* block_reason: string|null,
|
|
* last_changed_at: CarbonInterface|null,
|
|
* last_changed_by: string|null
|
|
* }
|
|
*/
|
|
private function resolveManagedTenantActivationLimitDecision(Workspace $workspace, array $planProfile, array $lastChanged): array
|
|
{
|
|
$overrideValue = $this->settingsResolver->resolveDetailed(
|
|
workspace: $workspace,
|
|
domain: self::SETTING_DOMAIN,
|
|
key: self::SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_VALUE,
|
|
);
|
|
|
|
$overrideReason = $this->settingsResolver->resolveValue(
|
|
workspace: $workspace,
|
|
domain: self::SETTING_DOMAIN,
|
|
key: self::SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_REASON,
|
|
);
|
|
|
|
$effectiveValue = is_int($overrideValue['value'])
|
|
? $overrideValue['value']
|
|
: (int) $planProfile['managed_tenant_limit_default'];
|
|
|
|
$source = $overrideValue['source'] === 'workspace_override'
|
|
? 'workspace_override'
|
|
: 'plan_profile_default';
|
|
|
|
$currentUsage = Tenant::activeQuery()
|
|
->where('workspace_id', (int) $workspace->getKey())
|
|
->count();
|
|
|
|
$remainingCapacity = $effectiveValue - $currentUsage;
|
|
$isBlocked = $currentUsage >= $effectiveValue;
|
|
$rationale = $source === 'workspace_override'
|
|
? (is_string($overrideReason) && $overrideReason !== '' ? $overrideReason : null)
|
|
: (string) $planProfile['description'];
|
|
|
|
return [
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'plan_profile_id' => (string) $planProfile['id'],
|
|
'plan_profile_label' => (string) $planProfile['label'],
|
|
'plan_profile_description' => (string) $planProfile['description'],
|
|
'key' => self::KEY_MANAGED_TENANT_ACTIVATION_LIMIT,
|
|
'effective_value' => $effectiveValue,
|
|
'source' => $source,
|
|
'rationale' => $rationale,
|
|
'current_usage' => $currentUsage,
|
|
'remaining_capacity' => $remainingCapacity,
|
|
'is_blocked' => $isBlocked,
|
|
'block_reason' => $isBlocked
|
|
? $this->managedTenantLimitBlockReason($currentUsage, $effectiveValue, $source, $planProfile, $rationale)
|
|
: null,
|
|
'last_changed_at' => $lastChanged['last_changed_at'],
|
|
'last_changed_by' => $lastChanged['last_changed_by'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool} $planProfile
|
|
* @param array{last_changed_at: CarbonInterface|null, last_changed_by: string|null} $lastChanged
|
|
* @return array{
|
|
* workspace_id: int,
|
|
* plan_profile_id: string,
|
|
* plan_profile_label: string,
|
|
* plan_profile_description: string,
|
|
* key: string,
|
|
* effective_value: bool,
|
|
* source: 'plan_profile_default'|'workspace_override',
|
|
* rationale: string|null,
|
|
* current_usage: null,
|
|
* remaining_capacity: null,
|
|
* is_blocked: bool,
|
|
* block_reason: string|null,
|
|
* last_changed_at: CarbonInterface|null,
|
|
* last_changed_by: string|null
|
|
* }
|
|
*/
|
|
private function resolveReviewPackGenerationDecision(Workspace $workspace, array $planProfile, array $lastChanged): array
|
|
{
|
|
$overrideValue = $this->settingsResolver->resolveDetailed(
|
|
workspace: $workspace,
|
|
domain: self::SETTING_DOMAIN,
|
|
key: self::SETTING_REVIEW_PACK_GENERATION_OVERRIDE_VALUE,
|
|
);
|
|
|
|
$overrideReason = $this->settingsResolver->resolveValue(
|
|
workspace: $workspace,
|
|
domain: self::SETTING_DOMAIN,
|
|
key: self::SETTING_REVIEW_PACK_GENERATION_OVERRIDE_REASON,
|
|
);
|
|
|
|
$effectiveValue = is_bool($overrideValue['value'])
|
|
? $overrideValue['value']
|
|
: (bool) $planProfile['review_pack_generation_default'];
|
|
|
|
$source = $overrideValue['source'] === 'workspace_override'
|
|
? 'workspace_override'
|
|
: 'plan_profile_default';
|
|
|
|
$rationale = $source === 'workspace_override'
|
|
? (is_string($overrideReason) && $overrideReason !== '' ? $overrideReason : null)
|
|
: (string) $planProfile['description'];
|
|
|
|
return [
|
|
'workspace_id' => (int) $workspace->getKey(),
|
|
'plan_profile_id' => (string) $planProfile['id'],
|
|
'plan_profile_label' => (string) $planProfile['label'],
|
|
'plan_profile_description' => (string) $planProfile['description'],
|
|
'key' => self::KEY_REVIEW_PACK_GENERATION_ENABLED,
|
|
'effective_value' => $effectiveValue,
|
|
'source' => $source,
|
|
'rationale' => $rationale,
|
|
'current_usage' => null,
|
|
'remaining_capacity' => null,
|
|
'is_blocked' => ! $effectiveValue,
|
|
'block_reason' => $effectiveValue
|
|
? null
|
|
: $this->reviewPackGenerationBlockReason($source, $planProfile, $rationale),
|
|
'last_changed_at' => $lastChanged['last_changed_at'],
|
|
'last_changed_by' => $lastChanged['last_changed_by'],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @return array{last_changed_at: CarbonInterface|null, last_changed_by: string|null}
|
|
*/
|
|
private function lastChangedMetadata(Workspace $workspace): array
|
|
{
|
|
$record = WorkspaceSetting::query()
|
|
->where('workspace_id', (int) $workspace->getKey())
|
|
->where('domain', self::SETTING_DOMAIN)
|
|
->whereIn('key', [
|
|
self::SETTING_PLAN_PROFILE,
|
|
self::SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_VALUE,
|
|
self::SETTING_MANAGED_TENANT_LIMIT_OVERRIDE_REASON,
|
|
self::SETTING_REVIEW_PACK_GENERATION_OVERRIDE_VALUE,
|
|
self::SETTING_REVIEW_PACK_GENERATION_OVERRIDE_REASON,
|
|
])
|
|
->whereNotNull('updated_by_user_id')
|
|
->with('updatedByUser:id,name')
|
|
->latest('updated_at')
|
|
->latest('id')
|
|
->first();
|
|
|
|
if (! $record instanceof WorkspaceSetting) {
|
|
return [
|
|
'last_changed_at' => null,
|
|
'last_changed_by' => null,
|
|
];
|
|
}
|
|
|
|
return [
|
|
'last_changed_at' => $record->updated_at,
|
|
'last_changed_by' => $record->updatedByUser?->name,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @param array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool} $planProfile
|
|
*/
|
|
private function managedTenantLimitBlockReason(int $currentUsage, int $effectiveValue, string $source, array $planProfile, ?string $rationale): string
|
|
{
|
|
$prefix = $source === 'workspace_override'
|
|
? 'This workspace override currently allows'
|
|
: sprintf('The %s plan profile currently allows', $planProfile['label']);
|
|
|
|
$message = sprintf(
|
|
'%s %d active managed tenant%s, and this workspace already has %d active managed tenant%s.',
|
|
$prefix,
|
|
$effectiveValue,
|
|
$effectiveValue === 1 ? '' : 's',
|
|
$currentUsage,
|
|
$currentUsage === 1 ? '' : 's',
|
|
);
|
|
|
|
if ($source === 'workspace_override' && $rationale !== null) {
|
|
$message .= ' Reason: '.$rationale;
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
|
|
/**
|
|
* @param array{id: string, label: string, description: string, managed_tenant_limit_default: int, review_pack_generation_default: bool, is_default: bool} $planProfile
|
|
*/
|
|
private function reviewPackGenerationBlockReason(string $source, array $planProfile, ?string $rationale): string
|
|
{
|
|
$message = $source === 'workspace_override'
|
|
? 'Review pack generation is disabled by workspace override.'
|
|
: sprintf('Review pack generation is disabled by the %s plan profile.', $planProfile['label']);
|
|
|
|
if ($source === 'workspace_override' && $rationale !== null) {
|
|
$message .= ' Reason: '.$rationale;
|
|
}
|
|
|
|
return $message;
|
|
}
|
|
} |