106 lines
3.5 KiB
PHP
106 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Tenant;
|
|
use App\Services\Operations\OperationLifecycleReconciler;
|
|
use App\Support\Operations\OperationLifecyclePolicy;
|
|
use Illuminate\Console\Command;
|
|
|
|
class TenantpilotReconcileOperationRuns extends Command
|
|
{
|
|
protected $signature = 'tenantpilot:operation-runs:reconcile
|
|
{--type=* : Limit reconciliation to one or more covered operation types}
|
|
{--tenant=* : Limit reconciliation to tenant_id or tenant external_id}
|
|
{--workspace=* : Limit reconciliation to workspace ids}
|
|
{--limit=100 : Maximum number of active runs to inspect}
|
|
{--dry-run : Report the changes without writing them}';
|
|
|
|
protected $description = 'Reconcile stale covered operation runs back to deterministic terminal truth.';
|
|
|
|
public function handle(
|
|
OperationLifecycleReconciler $reconciler,
|
|
OperationLifecyclePolicy $policy,
|
|
): int {
|
|
$types = array_values(array_filter(
|
|
(array) $this->option('type'),
|
|
static fn (mixed $type): bool => is_string($type) && trim($type) !== '',
|
|
));
|
|
$workspaceIds = array_values(array_filter(
|
|
array_map(
|
|
static fn (mixed $workspaceId): int => is_numeric($workspaceId) ? (int) $workspaceId : 0,
|
|
(array) $this->option('workspace'),
|
|
),
|
|
static fn (int $workspaceId): bool => $workspaceId > 0,
|
|
));
|
|
$tenantIds = $this->resolveTenantIds(array_values(array_filter((array) $this->option('tenant'))));
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
|
|
if ($types === []) {
|
|
$types = $policy->coveredTypeNames();
|
|
}
|
|
|
|
$result = $reconciler->reconcile([
|
|
'types' => $types,
|
|
'tenant_ids' => $tenantIds,
|
|
'workspace_ids' => $workspaceIds,
|
|
'limit' => max(1, (int) $this->option('limit')),
|
|
'dry_run' => $dryRun,
|
|
]);
|
|
|
|
$rows = collect($result['changes'] ?? [])
|
|
->map(static function (array $change): array {
|
|
return [
|
|
'Run' => (string) ($change['operation_run_id'] ?? '—'),
|
|
'Type' => (string) ($change['type'] ?? '—'),
|
|
'Reason' => (string) ($change['reason_code'] ?? '—'),
|
|
'Applied' => (($change['applied'] ?? false) === true) ? 'yes' : 'no',
|
|
];
|
|
})
|
|
->values()
|
|
->all();
|
|
|
|
if ($rows !== []) {
|
|
$this->table(['Run', 'Type', 'Reason', 'Applied'], $rows);
|
|
}
|
|
|
|
$this->info(sprintf(
|
|
'Inspected %d run(s); reconciled %d; skipped %d.',
|
|
(int) ($result['candidates'] ?? 0),
|
|
(int) ($result['reconciled'] ?? 0),
|
|
(int) ($result['skipped'] ?? 0),
|
|
));
|
|
|
|
if ($dryRun) {
|
|
$this->comment('Dry-run: no changes written.');
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @param array<int, string> $tenantIdentifiers
|
|
* @return array<int, int>
|
|
*/
|
|
private function resolveTenantIds(array $tenantIdentifiers): array
|
|
{
|
|
if ($tenantIdentifiers === []) {
|
|
return [];
|
|
}
|
|
|
|
$tenantIds = [];
|
|
|
|
foreach ($tenantIdentifiers as $identifier) {
|
|
$tenant = Tenant::query()->forTenant($identifier)->first();
|
|
|
|
if ($tenant instanceof Tenant) {
|
|
$tenantIds[] = (int) $tenant->getKey();
|
|
}
|
|
}
|
|
|
|
return array_values(array_unique($tenantIds));
|
|
}
|
|
}
|