|null */ private ?Collection $laterSuccessfulRuns = null; /** @var Collection|null */ private ?Collection $providerConnections = null; /** * @param Collection $runs */ public function __construct( private readonly Collection $runs, private readonly ?OperationRunActionabilityRegistry $registry = null, ) {} /** * @param list $canonicalTypes * @param list $matchContextKeys */ public function laterSuccessfulRun( OperationRun $run, array $canonicalTypes, array $matchContextKeys = [], ): ?OperationRun { $canonicalTypes = array_values(array_unique(array_filter($canonicalTypes, static fn (string $type): bool => trim($type) !== ''))); if ($canonicalTypes === []) { return null; } return $this->laterSuccessfulRuns() ->first(function (OperationRun $candidate) use ($run, $canonicalTypes, $matchContextKeys): bool { if ((int) $candidate->getKey() === (int) $run->getKey()) { return false; } if (! in_array($candidate->canonicalOperationType(), $canonicalTypes, true)) { return false; } if (! $this->sameScope($run, $candidate)) { return false; } if (! $this->isLater($run, $candidate)) { return false; } foreach ($matchContextKeys as $key) { $left = data_get($run->context, $key); $right = data_get($candidate->context, $key); if ($left === null && $right === null) { continue; } if ((string) $left !== (string) $right) { return false; } } return true; }); } public function healthyProviderConnection(OperationRun $run): ?ProviderConnection { $connectionId = data_get($run->context, 'provider_connection_id'); if (! is_numeric($connectionId)) { return null; } $connection = $this->providerConnections()->get((int) $connectionId); if (! $connection instanceof ProviderConnection) { return null; } if ((int) $connection->workspace_id !== (int) $run->workspace_id) { return null; } if ((int) $connection->managed_environment_id !== (int) $run->managed_environment_id) { return null; } if (! $connection->is_enabled) { return null; } return $this->enumValue($connection->consent_status) === 'granted' && $this->enumValue($connection->verification_status) === 'healthy' ? $connection : null; } private function enumValue(mixed $value): string { return $value instanceof BackedEnum ? (string) $value->value : (string) $value; } private function sameScope(OperationRun $run, OperationRun $candidate): bool { return (int) $candidate->workspace_id === (int) $run->workspace_id && (int) ($candidate->managed_environment_id ?? 0) === (int) ($run->managed_environment_id ?? 0); } private function isLater(OperationRun $run, OperationRun $candidate): bool { $runTimestamp = $run->completed_at ?? $run->created_at; $candidateTimestamp = $candidate->completed_at ?? $candidate->created_at; if ($runTimestamp !== null && $candidateTimestamp !== null) { if ($candidateTimestamp->greaterThan($runTimestamp)) { return true; } if ($candidateTimestamp->lessThan($runTimestamp)) { return false; } } return (int) $candidate->getKey() > (int) $run->getKey(); } /** * @return Collection */ private function laterSuccessfulRuns(): Collection { if ($this->laterSuccessfulRuns instanceof Collection) { return $this->laterSuccessfulRuns; } $workspaceIds = $this->runs ->pluck('workspace_id') ->filter(static fn (mixed $value): bool => is_numeric($value)) ->map(static fn (mixed $value): int => (int) $value) ->unique() ->values() ->all(); if ($workspaceIds === []) { return $this->laterSuccessfulRuns = collect(); } $rawTypes = collect($this->registry?->canonicalTypesWithLaterSuccessPolicy() ?? []) ->flatMap(static fn (string $type): array => OperationCatalog::rawValuesForCanonical($type)) ->unique() ->values() ->all(); if ($rawTypes === []) { return $this->laterSuccessfulRuns = collect(); } $minCreatedAt = $this->runs ->map(static fn (OperationRun $run): mixed => $run->created_at ?? $run->completed_at) ->filter() ->sort() ->first(); $query = OperationRun::query() ->whereIn('workspace_id', $workspaceIds) ->whereIn('type', $rawTypes) ->where('status', OperationRunStatus::Completed->value) ->where('outcome', OperationRunOutcome::Succeeded->value) ->orderBy('completed_at') ->orderBy('created_at') ->orderBy('id'); if ($minCreatedAt !== null) { $query->where(function ($query) use ($minCreatedAt): void { $query ->whereNull('completed_at') ->orWhere('completed_at', '>=', $minCreatedAt) ->orWhere('created_at', '>=', $minCreatedAt); }); } return $this->laterSuccessfulRuns = $query->get(); } /** * @return Collection */ private function providerConnections(): Collection { if ($this->providerConnections instanceof Collection) { return $this->providerConnections; } $ids = $this->runs ->map(static fn (OperationRun $run): mixed => data_get($run->context, 'provider_connection_id')) ->filter(static fn (mixed $value): bool => is_numeric($value)) ->map(static fn (mixed $value): int => (int) $value) ->unique() ->values() ->all(); if ($ids === []) { return $this->providerConnections = collect(); } return $this->providerConnections = ProviderConnection::query() ->whereIn('id', $ids) ->get() ->keyBy(static fn (ProviderConnection $connection): int => (int) $connection->getKey()); } }