resolveDraftForIdentity($workspace, $entraTenantId, $preferredDraft); $isNew = ! $draft instanceof ManagedEnvironmentOnboardingSession; $wasCreated = $isNew; if ($isNew) { $draft = new ManagedEnvironmentOnboardingSession; $draft->workspace_id = (int) $workspace->getKey(); $draft->entra_tenant_id = $entraTenantId; $draft->started_by_user_id = (int) $actor->getKey(); $draft->version = 0; } elseif ($expectedVersion !== null) { $this->assertExpectedVersion($draft, $expectedVersion); } $draft->entra_tenant_id = $entraTenantId; $draft->updated_by_user_id = (int) $actor->getKey(); $mutator($draft); $this->persistDraft( draft: $draft, incrementVersion: $incrementVersion || $isNew, ); return $draft->refresh(); }); } /** * @param callable(ManagedEnvironmentOnboardingSession):void $mutator */ public function mutate( ManagedEnvironmentOnboardingSession $draft, User $actor, callable $mutator, ?int $expectedVersion = null, bool $incrementVersion = true, bool $allowTerminal = false, ): ManagedEnvironmentOnboardingSession { return DB::transaction(function () use ($draft, $actor, $mutator, $expectedVersion, $incrementVersion, $allowTerminal): ManagedEnvironmentOnboardingSession { $lockedDraft = ManagedEnvironmentOnboardingSession::query() ->whereKey($draft->getKey()) ->lockForUpdate() ->firstOrFail(); if (! $allowTerminal && $lockedDraft->lifecycleState()->isTerminal()) { throw new OnboardingDraftImmutableException( draftId: (int) $lockedDraft->getKey(), lifecycleState: $lockedDraft->lifecycleState(), ); } if ($expectedVersion !== null) { $this->assertExpectedVersion($lockedDraft, $expectedVersion); } $lockedDraft->updated_by_user_id = (int) $actor->getKey(); $mutator($lockedDraft); $this->persistDraft( draft: $lockedDraft, incrementVersion: $incrementVersion, ); return $lockedDraft->refresh(); }); } public function lockForTrustedMutation(ManagedEnvironmentOnboardingSession|int|string $draft, Workspace $workspace): ManagedEnvironmentOnboardingSession { $draftId = $draft instanceof ManagedEnvironmentOnboardingSession ? (int) $draft->getKey() : (int) $draft; $lockedDraft = ManagedEnvironmentOnboardingSession::query() ->whereKey($draftId) ->where('workspace_id', (int) $workspace->getKey()) ->lockForUpdate() ->first(); if (! $lockedDraft instanceof ManagedEnvironmentOnboardingSession) { throw new NotFoundHttpException; } return $lockedDraft; } private function resolveDraftForIdentity( Workspace $workspace, string $entraTenantId, ?ManagedEnvironmentOnboardingSession $preferredDraft = null, ): ?ManagedEnvironmentOnboardingSession { if ($preferredDraft instanceof ManagedEnvironmentOnboardingSession) { $lockedPreferredDraft = ManagedEnvironmentOnboardingSession::query() ->whereKey($preferredDraft->getKey()) ->where('workspace_id', (int) $workspace->getKey()) ->lockForUpdate() ->first(); if ($lockedPreferredDraft instanceof ManagedEnvironmentOnboardingSession && $lockedPreferredDraft->entra_tenant_id === $entraTenantId) { if ($lockedPreferredDraft->lifecycleState()->isTerminal()) { throw new OnboardingDraftImmutableException( draftId: (int) $lockedPreferredDraft->getKey(), lifecycleState: $lockedPreferredDraft->lifecycleState(), ); } return $lockedPreferredDraft; } } return ManagedEnvironmentOnboardingSession::query() ->where('workspace_id', (int) $workspace->getKey()) ->where('entra_tenant_id', $entraTenantId) ->resumable() ->orderByDesc('updated_at') ->lockForUpdate() ->first(); } private function assertExpectedVersion(ManagedEnvironmentOnboardingSession $draft, int $expectedVersion): void { $actualVersion = max(1, (int) ($draft->version ?? 1)); if ($expectedVersion === $actualVersion) { return; } throw new OnboardingDraftConflictException( draftId: (int) $draft->getKey(), expectedVersion: $expectedVersion, actualVersion: $actualVersion, ); } private function persistDraft(ManagedEnvironmentOnboardingSession $draft, bool $incrementVersion): void { $currentVersion = max(0, (int) ($draft->version ?? 0)); $draft->version = $incrementVersion ? $currentVersion + 1 : max(1, $currentVersion); $this->lifecycleService->applySnapshot($draft, false); $draft->save(); $this->lifecycleService->recordCompletedCheckpointTelemetryIfNeeded($draft); } }