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 $tenantIdentifiers * @return array */ 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)); } }