$extraContext */ public function start( Tenant $tenant, ?ProviderConnection $connection, string $operationType, callable $dispatcher, ?User $initiator = null, array $extraContext = [], ): ProviderOperationStartResult { $definition = $this->registry->get($operationType); $resolution = $connection instanceof ProviderConnection ? $this->resolver->validateConnection($tenant, (string) $definition['provider'], $connection) : $this->resolver->resolveDefault($tenant, (string) $definition['provider']); if (! $resolution->resolved || ! $resolution->connection instanceof ProviderConnection) { return $this->startBlocked( tenant: $tenant, operationType: $operationType, provider: (string) $definition['provider'], module: (string) $definition['module'], reasonCode: $resolution->effectiveReasonCode(), extensionReasonCode: $resolution->extensionReasonCode, reasonMessage: $resolution->message, connection: $resolution->connection ?? $connection, initiator: $initiator, extraContext: $extraContext, ); } return DB::transaction(function () use ($tenant, $operationType, $dispatcher, $initiator, $extraContext, $definition, $resolution): ProviderOperationStartResult { $connection = $resolution->connection; if (! $connection instanceof ProviderConnection) { throw new InvalidArgumentException('Resolved provider connection is missing.'); } $lockedConnection = ProviderConnection::query() ->whereKey($connection->getKey()) ->lockForUpdate() ->firstOrFail(); $activeRun = OperationRun::query() ->where('tenant_id', $tenant->getKey()) ->active() ->where('context->provider_connection_id', (int) $lockedConnection->getKey()) ->orderByDesc('id') ->first(); if ($activeRun instanceof OperationRun) { if ($activeRun->type === $operationType) { return ProviderOperationStartResult::deduped($activeRun); } return ProviderOperationStartResult::scopeBusy($activeRun); } $context = array_merge($extraContext, [ 'provider' => $lockedConnection->provider, 'module' => $definition['module'], 'provider_connection_id' => (int) $lockedConnection->getKey(), 'target_scope' => [ 'entra_tenant_id' => $lockedConnection->entra_tenant_id, ], ]); $run = $this->runs->ensureRunWithIdentity( tenant: $tenant, type: $operationType, identityInputs: [ 'provider_connection_id' => (int) $lockedConnection->getKey(), ], context: $context, initiator: $initiator, ); $dispatched = false; if ($run->wasRecentlyCreated) { $this->invokeDispatcher($dispatcher, $run); $dispatched = true; } return ProviderOperationStartResult::started($run, $dispatched); }); } /** * @param array $extraContext */ private function startBlocked( Tenant $tenant, string $operationType, string $provider, string $module, string $reasonCode, ?string $extensionReasonCode = null, ?string $reasonMessage = null, ?ProviderConnection $connection = null, ?User $initiator = null, array $extraContext = [], ): ProviderOperationStartResult { $context = array_merge($extraContext, [ 'provider' => $provider, 'module' => $module, 'target_scope' => [ 'entra_tenant_id' => $tenant->graphTenantId(), ], ]); $identityInputs = [ 'provider' => $provider, 'reason_code' => $reasonCode, ]; if (is_string($extensionReasonCode) && $extensionReasonCode !== '') { $context['reason_code_extension'] = $extensionReasonCode; $identityInputs['reason_code_extension'] = $extensionReasonCode; } if ($connection instanceof ProviderConnection) { $context['provider_connection_id'] = (int) $connection->getKey(); $identityInputs['provider_connection_id'] = (int) $connection->getKey(); } $run = $this->runs->ensureRunWithIdentity( tenant: $tenant, type: $operationType, identityInputs: $identityInputs, context: $context, initiator: $initiator, ); $run = $this->runs->finalizeBlockedRun( $run, reasonCode: ProviderReasonCodes::isKnown($reasonCode) ? $reasonCode : ProviderReasonCodes::UnknownError, nextSteps: $this->nextStepsRegistry->forReason($tenant, $reasonCode, $connection), message: $reasonMessage, ); return ProviderOperationStartResult::blocked($run); } private function invokeDispatcher(callable $dispatcher, OperationRun $run): void { $ref = null; if (is_array($dispatcher) && count($dispatcher) === 2) { $ref = new ReflectionMethod($dispatcher[0], (string) $dispatcher[1]); } elseif (is_string($dispatcher) && str_contains($dispatcher, '::')) { [$class, $method] = explode('::', $dispatcher, 2); $ref = new ReflectionMethod($class, $method); } elseif ($dispatcher instanceof \Closure) { $ref = new ReflectionFunction($dispatcher); } elseif (is_object($dispatcher) && method_exists($dispatcher, '__invoke')) { $ref = new ReflectionMethod($dispatcher, '__invoke'); } if ($ref && $ref->getNumberOfParameters() >= 1) { $dispatcher($run); return; } $dispatcher(); } }