diff --git a/src/Controller/InstallerController.php b/src/Controller/InstallerController.php index 8b0f39798fe06fa56d33e15cf4a32f7077a6fc3e..dc2ff6d884e0e64c5275884f30f3145a264ce34a 100644 --- a/src/Controller/InstallerController.php +++ b/src/Controller/InstallerController.php @@ -6,13 +6,12 @@ use Drupal\Component\Datetime\TimeInterface; use Drupal\Component\Utility\DeprecationHelper; use Drupal\Core\Access\AccessResult; use Drupal\Core\Controller\ControllerBase; -use Drupal\Core\TempStore\SharedTempStore; -use Drupal\Core\TempStore\SharedTempStoreFactory; use Drupal\Core\Url; use Drupal\package_manager\Exception\StageException; use Drupal\project_browser\ActivatorInterface; use Drupal\project_browser\ComposerInstaller\Installer; use Drupal\project_browser\EnabledSourceHandler; +use Drupal\project_browser\InstallState; use Drupal\project_browser\ProjectBrowser\Project; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -53,39 +52,14 @@ class InstallerController extends ControllerBase { */ protected const STAGE_STATUS_OK = 0; - /** - * The Project Browser tempstore object. - * - * @var \Drupal\Core\TempStore\SharedTempStore - */ - protected SharedTempStore $projectBrowserTempStore; - - /** - * Constructor for install controller. - * - * @param \Drupal\project_browser\ComposerInstaller\Installer $installer - * The installer service. - * @param \Drupal\Core\TempStore\SharedTempStoreFactory $shared_temp_store_factory - * The temporary storage factory. - * @param \Drupal\project_browser\EnabledSourceHandler $enabledSourceHandler - * The enabled project browser source. - * @param \Drupal\Component\Datetime\TimeInterface $time - * The system time. - * @param \Psr\Log\LoggerInterface $logger - * The logger instance. - * @param \Drupal\project_browser\ActivatorInterface $activator - * The project activator service. - */ public function __construct( private readonly Installer $installer, - SharedTempStoreFactory $shared_temp_store_factory, private readonly EnabledSourceHandler $enabledSourceHandler, private readonly TimeInterface $time, private readonly LoggerInterface $logger, private readonly ActivatorInterface $activator, - ) { - $this->projectBrowserTempStore = $shared_temp_store_factory->get('project_browser'); - } + private readonly InstallState $installState, + ) {} /** * {@inheritdoc} @@ -93,11 +67,11 @@ class InstallerController extends ControllerBase { public static function create(ContainerInterface $container) { return new static( $container->get(Installer::class), - $container->get(SharedTempStoreFactory::class), $container->get(EnabledSourceHandler::class), $container->get(TimeInterface::class), $container->get('logger.channel.project_browser'), $container->get(ActivatorInterface::class), + $container->get(InstallState::class), ); } @@ -109,19 +83,11 @@ class InstallerController extends ControllerBase { return AccessResult::allowedIf((bool) $ui_install); } - /** - * Nulls the installing and core installing states. - */ - private function resetProgress(): void { - $this->projectBrowserTempStore->delete('requiring'); - $this->projectBrowserTempStore->delete('installing'); - } - /** * Resets progress and destroys the stage. */ private function cancelRequire(): void { - $this->resetProgress(); + $this->installState->deleteAll(); // Checking the for the presence of a lock in the package manager stage is // necessary as this method can be called during create(), which includes // both the PreCreate and PostCreate events. If an exception is caught @@ -148,27 +114,17 @@ class InstallerController extends ControllerBase { * * @return \Symfony\Component\HttpFoundation\JsonResponse * Information about the project's require/install status. - * - * If the project is being required, the response will include which require - * phase is currently occurring. - * - * When a project is required via the UI, the UI fetches this endpoint - * regularly so it can monitor the progress of the process and report which - * stage is taking place. */ public function inProgress(Project $project): JsonResponse { - $requiring = $this->projectBrowserTempStore->get('requiring'); - $core_installing = $this->projectBrowserTempStore->get('installing'); + $project_state = $this->installState->getStatus($project); $return = ['status' => self::STATUS_IDLE]; - if (isset($requiring['project_id']) && $requiring['project_id'] === $project->id) { - $return['status'] = self::STATUS_REQUIRING_PROJECT; - $return['phase'] = $requiring['phase']; + if ($project_state !== NULL) { + $return['status'] = ($project_state === 'requiring' || $project_state === 'applying') + ? self::STATUS_REQUIRING_PROJECT + : self::STATUS_INSTALLING_PROJECT; + $return['phase'] = $project_state; } - if ($core_installing === $project->id) { - $return['status'] = self::STATUS_INSTALLING_PROJECT; - } - return new JsonResponse($return); } @@ -242,24 +198,6 @@ class InstallerController extends ControllerBase { ], 418); } - /** - * Updates the 'requiring' state in the temp store. - * - * @param string $id - * The ID of the project being required, as known to the enabled sources - * handler. - * @param string $phase - * The require phase in progress. - */ - private function setRequiringState(?string $id, string $phase): void { - $data = $this->projectBrowserTempStore->get('requiring') ?? []; - if ($id) { - $data['project_id'] = $id; - } - $data['phase'] = $phase; - $this->projectBrowserTempStore->set('requiring', $data); - } - /** * Unlocks and destroys the stage. * @@ -291,7 +229,7 @@ class InstallerController extends ControllerBase { catch (\Exception $e) { return $this->errorResponse($e); } - $this->projectBrowserTempStore->delete('requiring'); + $this->installState->deleteAll(); $this->messenger()->addStatus($this->t('Install staging area unlocked.')); return $this->redirect('project_browser.browse'); } @@ -332,18 +270,18 @@ class InstallerController extends ControllerBase { public function begin(): JsonResponse { $stage_available = $this->installer->isAvailable(); if (!$stage_available) { - $requiring_metadata = $this->projectBrowserTempStore->getMetadata('requiring'); + $updated_time = $this->installState->getFirstUpdatedTime(); if (!$this->installer->lockCameFromProjectBrowserInstaller()) { return $this->lockedResponse($this->t('The installation stage is locked by a process outside of Project Browser'), ''); } - if (empty($requiring_metadata)) { + if (empty($updated_time)) { $unlock_url = self::getUrlWithReplacedCsrfTokenPlaceholder( Url::fromRoute('project_browser.install.unlock') ); $message = t('An install staging area claimed by Project Browser exists but has expired. You may unlock the stage and try the install again.'); return $this->lockedResponse($message, $unlock_url); } - $time_since_updated = $this->time->getRequestTime() - $requiring_metadata->getUpdated(); + $time_since_updated = $this->time->getRequestTime() - $updated_time; $hours = (int) gmdate("H", $time_since_updated); $minutes = (int) gmdate("i", $time_since_updated); $minutes = $time_since_updated > 60 ? $minutes : 'less than 1'; @@ -400,7 +338,7 @@ class InstallerController extends ControllerBase { * Status message. */ public function require(Request $request, string $stage_id): JsonResponse { - $package_names = $package_ids = []; + $package_names = []; foreach ($request->toArray() as $project) { $project = $this->enabledSourceHandler->getStoredProject($project); if ($project->source === 'project_browser_test_mock') { @@ -412,23 +350,11 @@ class InstallerController extends ControllerBase { return new JsonResponse(['message' => "$project->machineName is not safe to add because its security coverage has been revoked"], 500); } } + $this->installState->setState($project, 'requiring'); $package_names[] = $project->packageName; - $package_ids[] = $project->id; - } - - $requiring = $this->projectBrowserTempStore->get('requiring'); - $current_package_names = implode(', ', $package_names); - if (!empty($requiring['project_id']) && $requiring['project_id'] !== $current_package_names) { - $error_message = sprintf( - 'Error: a request to install %s was ignored as an install for a different project is in progress.', - $current_package_names - ); - return new JsonResponse(['message' => $error_message], 500); } - $this->setRequiringState(implode(', ', $package_ids), 'requiring module'); try { $this->installer->claim($stage_id)->require($package_names); - $this->setRequiringState(NULL, 'requiring module'); return $this->successResponse('require', $stage_id); } catch (\Exception $e) { @@ -447,7 +373,9 @@ class InstallerController extends ControllerBase { * Status message. */ public function apply(string $stage_id): JsonResponse { - $this->setRequiringState(NULL, 'applying'); + foreach (array_keys($this->installState->toArray()) as $project_id) { + $this->installState->setState($this->enabledSourceHandler->getStoredProject($project_id), 'applying'); + } try { $this->installer->claim($stage_id)->apply(); } @@ -468,7 +396,6 @@ class InstallerController extends ControllerBase { * Status message. */ public function postApply(string $stage_id): JsonResponse { - $this->setRequiringState(NULL, 'post apply'); try { $this->installer->claim($stage_id)->postApply(); } @@ -488,14 +415,12 @@ class InstallerController extends ControllerBase { * Status message. */ public function destroy(string $stage_id): JsonResponse { - $this->setRequiringState(NULL, 'completing'); try { $this->installer->claim($stage_id)->destroy(); } catch (\Exception $e) { return $this->errorResponse($e, 'destroy'); } - $this->projectBrowserTempStore->delete('requiring'); return new JsonResponse([ 'phase' => 'destroy', 'status' => self::STAGE_STATUS_OK, @@ -515,15 +440,16 @@ class InstallerController extends ControllerBase { public function activate(Request $request): JsonResponse { foreach ($request->toArray() as $project) { $project = $this->enabledSourceHandler->getStoredProject($project); - $this->projectBrowserTempStore->set('installing', $project->id); + $this->installState->setState($project, 'activating'); try { $this->activator->activate($project); + $this->installState->setState($project, 'installed'); } catch (\Throwable $e) { return $this->errorResponse($e, 'project install'); } finally { - $this->resetProgress(); + $this->installState->deleteAll(); } } return new JsonResponse(['status' => 0]); diff --git a/src/InstallState.php b/src/InstallState.php new file mode 100644 index 0000000000000000000000000000000000000000..acc81e53fe96ac03a6b5b50e96cdc53d202328cb --- /dev/null +++ b/src/InstallState.php @@ -0,0 +1,115 @@ +<?php + +namespace Drupal\project_browser; + +use Drupal\Component\Datetime\TimeInterface; +use Drupal\Core\KeyValueStore\KeyValueFactoryInterface; +use Drupal\Core\KeyValueStore\KeyValueStoreInterface; +use Drupal\project_browser\ProjectBrowser\Project; + +/** + * Defines a service to manage the installation state of projects. + */ +final class InstallState { + + /** + * The key-value storage. + */ + private readonly KeyValueStoreInterface $keyValue; + + public function __construct( + KeyValueFactoryInterface $keyValueFactory, + private readonly TimeInterface $time, + ) { + $this->keyValue = $keyValueFactory->get('project_browser.install_status'); + } + + /** + * Returns information on all in-progress project installs and timestamp. + * + * @param bool $include_timestamp + * Whether to include the `__timestamp` entry in the returned array. + * Defaults to FALSE. + * + * @return array + * The array contains: + * - Project states: Keyed by project ID, where each entry is an associative + * array containing: + * - source: The source plugin ID for the project. + * - status: The installation status of the project, or NULL if not set. + * - A separate `__timestamp` entry: The UNIX timestamp indicating when the + * request started (included only if $include_timestamp is TRUE). + * + * Example return value: + * [ + * 'project_id1' => [ + * 'source' => 'source_plugin_id1', + * 'status' => 'requiring', + * ], + * 'project_id2' => [ + * 'source' => 'source_plugin_id2', + * 'status' => 'installing', + * ], + * '__timestamp' => 1732086755, + * ] + */ + public function toArray(bool $include_timestamp = FALSE): array { + $data = $this->keyValue->getAll(); + if (!$include_timestamp) { + unset($data['__timestamp']); + } + return $data; + } + + /** + * Sets project state and initializes a timestamp if not set. + * + * @param \Drupal\project_browser\ProjectBrowser\Project $project + * The project object containing the ID and source of the project. + * @param string|null $status + * The installation status to set for the project, or NULL if no status. + * The status can be any arbitrary string, depending on the context + * or use case. + */ + public function setState(Project $project, ?string $status): void { + $this->keyValue->setIfNotExists('__timestamp', $this->time->getRequestTime()); + if (is_string($status)) { + $this->keyValue->set($project->id, ['source' => $project->source, 'status' => $status]); + } + else { + $this->keyValue->delete($project->id); + } + } + + /** + * Retrieves the install state of a project. + * + * @param \Drupal\project_browser\ProjectBrowser\Project $project + * The project object for which to retrieve the install state. + * + * @return string|null + * The current install status of the project, or NULL if not found. + */ + public function getStatus(Project $project): ?string { + $project_data = $this->keyValue->get($project->id); + return $project_data['status'] ?? NULL; + } + + /** + * Deletes all project state data from key store. + */ + public function deleteAll(): void { + $this->keyValue->deleteAll(); + } + + /** + * Retrieves the first updated time of the project states. + * + * @return int|null + * The timestamp when the project states were first updated, or NULL. + */ + public function getFirstUpdatedTime(): ?int { + return $this->keyValue->get('__timestamp'); + } + +} diff --git a/src/ProjectBrowserServiceProvider.php b/src/ProjectBrowserServiceProvider.php index e9b8ddd675bb0a432a26e6259e176e4cadb62688..01f34d2d7f628679ca759bcacd7769dd5be2d3b4 100644 --- a/src/ProjectBrowserServiceProvider.php +++ b/src/ProjectBrowserServiceProvider.php @@ -19,6 +19,7 @@ use Drupal\project_browser\ComposerInstaller\Installer; use Drupal\project_browser\ComposerInstaller\Validator\CoreNotUpdatedValidator; use Drupal\project_browser\ComposerInstaller\Validator\PackageNotInstalledValidator; use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; /** * Base class acts as a helper for Project Browser services. @@ -35,6 +36,10 @@ class ProjectBrowserServiceProvider extends ServiceProviderBase { $container->register(Installer::class, Installer::class) ->setAutowired(TRUE); + $container->register(InstallState::class, InstallState::class) + ->setArgument('$keyValueFactory', new Reference('keyvalue')) + ->setAutowired(TRUE); + $container->register(InstallReadiness::class, InstallReadiness::class) ->setAutowired(TRUE); diff --git a/tests/src/Functional/InstallerControllerTest.php b/tests/src/Functional/InstallerControllerTest.php index c17e27bce1d8a830d03313012e203c01992370e7..d62ae2f8cb49838c2fcf60ffe688c101b209f329 100644 --- a/tests/src/Functional/InstallerControllerTest.php +++ b/tests/src/Functional/InstallerControllerTest.php @@ -17,6 +17,7 @@ use Drupal\package_manager\ValidationResult; use Drupal\package_manager_test_validation\EventSubscriber\TestSubscriber; use Drupal\project_browser\ComposerInstaller\Installer; use Drupal\project_browser\EnabledSourceHandler; +use Drupal\project_browser\InstallState; use Drupal\project_browser_test\Datetime\TestTime; use GuzzleHttp\RequestOptions; use Psr\Http\Message\ResponseInterface; @@ -35,13 +36,6 @@ class InstallerControllerTest extends BrowserTestBase { use PackageManagerFixtureUtilityTrait; use ApiRequestTrait; - /** - * The shared tempstore object. - * - * @var \Drupal\Core\TempStore\SharedTempStore - */ - protected $sharedTempStore; - /** * A stage id. * @@ -168,7 +162,6 @@ class InstallerControllerTest extends BrowserTestBase { ]); $query->execute(); $this->initPackageManager(); - $this->sharedTempStore = $this->container->get('tempstore.shared'); $this->installer = $this->container->get(Installer::class); $this->drupalLogin($this->drupalCreateUser(['administer modules'])); $this->config('project_browser.admin_settings') @@ -198,7 +191,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::begin */ public function testInstallSecurityRevokedModule() { - $this->assertProjectBrowserTempStatus(NULL, NULL); + $this->assertSame([], $this->container->get(InstallState::class)->toArray()); $contents = $this->drupalGet('admin/modules/project_browser/install-begin'); $this->stageId = Json::decode($contents)['stage_id']; $response = $this->getPostResponse('project_browser.stage.require', 'project_browser_test_mock/security_revoked_module', [ @@ -214,7 +207,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::require */ public function testInstallAlreadyPresentPackage() { - $this->assertProjectBrowserTempStatus(NULL, NULL); + $this->assertSame([], $this->container->get(InstallState::class)->toArray()); // Though core is not available as a choice in project browser, it works // well for the purposes of this test as it's definitely already added // via composer. @@ -233,7 +226,7 @@ class InstallerControllerTest extends BrowserTestBase { * @covers ::begin */ private function doStart() { - $this->assertProjectBrowserTempStatus(NULL, NULL); + $this->assertSame([], $this->container->get(InstallState::class)->toArray()); $contents = $this->drupalGet('admin/modules/project_browser/install-begin'); $this->stageId = Json::decode($contents)['stage_id']; $this->assertSession()->statusCodeEquals(200); @@ -251,7 +244,7 @@ class InstallerControllerTest extends BrowserTestBase { ]); $expected_output = sprintf('{"phase":"create","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'requiring module'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'requiring'); } /** @@ -263,7 +256,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-apply/$this->stageId"); $expected_output = sprintf('{"phase":"apply","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'applying'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); } /** @@ -275,7 +268,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-post_apply/$this->stageId"); $expected_output = sprintf('{"phase":"post apply","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'post apply'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); } /** @@ -287,7 +280,7 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet("/admin/modules/project_browser/install-destroy/$this->stageId"); $expected_output = sprintf('{"phase":"destroy","status":0,"stage_id":"%s"}', $this->stageId); $this->assertSame($expected_output, $this->getSession()->getPage()->getContent()); - $this->assertInstallNotInProgress('awesome_module'); + $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'project_browser_test_mock', 'applying'); } /** @@ -450,7 +443,13 @@ class InstallerControllerTest extends BrowserTestBase { $this->drupalGet('admin/modules/project_browser/install-begin'); $this->assertSession()->statusCodeEquals(418); $this->assertMatchesRegularExpression('/{"message":"The install staging area was locked less than 1 minutes ago. This is recent enough that a legitimate installation may be in progress. Consider waiting before unlocking the installation staging area.","unlock_url":".*admin..modules..project_browser..install..unlock\?token=[a-zA-Z0-9_-]*"}/', $this->getSession()->getPage()->getContent()); - $this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'requiring module'); + $expected = [ + 'project_browser_test_mock/awesome_module' => [ + 'source' => 'project_browser_test_mock', + 'status' => 'requiring', + ], + ]; + $this->assertSame($expected, $this->container->get(InstallState::class)->toArray()); $this->assertFalse($this->installer->isAvailable()); $this->assertFalse($this->installer->isApplying()); TestTime::setFakeTimeByOffset("+800 seconds"); @@ -511,7 +510,7 @@ class InstallerControllerTest extends BrowserTestBase { */ public function testCanBreakStageWithMissingProjectBrowserLock() { $this->doStart(); - $this->sharedTempStore->get('project_browser')->delete('requiring'); + $this->container->get(InstallState::class)->deleteAll(); $content = $this->drupalGet('admin/modules/project_browser/install-begin'); $this->assertSession()->statusCodeEquals(418); $this->assertFalse($this->installer->isAvailable()); @@ -550,67 +549,28 @@ class InstallerControllerTest extends BrowserTestBase { $assert_session->checkboxChecked('edit-modules-views-ui-enable'); } - /** - * Asserts that a module install is not in progress. - * - * @param string $module - * The module machine name. - */ - protected function assertInstallNotInProgress($module) { - $this->assertProjectBrowserTempStatus(NULL, NULL); - $this->drupalGet("/admin/modules/project_browser/install_in_progress/project_browser_test_mock/$module"); - $this->assertSame('{"status":0}', $this->getSession()->getPage()->getContent()); - $this->drupalGet('/admin/modules/project_browser/install_in_progress/project_browser_test_mock/metatag'); - $this->assertSame('{"status":0}', $this->getSession()->getPage()->getContent()); - } - /** * Confirms the project browser in progress input provides the expected value. * * @param string $project_id * The ID of the project being enabled. - * @param string $phase - * The install phase. - */ - protected function assertInstallInProgress($project_id, $phase = NULL) { - $expect_install = ['project_id' => $project_id]; - if (!is_null($phase)) { - $expect_install['phase'] = $phase; - } - $this->assertProjectBrowserTempStatus($expect_install, NULL); + * @param string $source + * The project source. + * @param string $status + * The install state. + */ + protected function assertInstallInProgress(string $project_id, string $source, ?string $status = NULL) { + $expect_install[$project_id] = [ + 'source' => $source, + 'status' => $status, + ]; + $this->assertSame($expect_install, $this->container->get(InstallState::class)->toArray()); $this->drupalGet("/admin/modules/project_browser/install_in_progress/$project_id"); - $this->assertSame(sprintf('{"status":1,"phase":"%s"}', $phase), $this->getSession()->getPage()->getContent()); + $this->assertSame(sprintf('{"status":1,"phase":"%s"}', $status), $this->getSession()->getPage()->getContent()); $this->drupalGet('/admin/modules/project_browser/install_in_progress/project_browser_test_mock/metatag'); $this->assertSame('{"status":0}', $this->getSession()->getPage()->getContent()); } - /** - * Confirms the tempstore install status are as expected. - * - * @param array $expected_requiring - * The expected value of the 'requiring' state. - * @param string $expected_installing - * The expected value of the 'core requiring' state. - */ - protected function assertProjectBrowserTempStatus($expected_requiring, $expected_installing) { - $project_browser_requiring = $this->sharedTempStore->get('project_browser')->get('requiring'); - $project_browser_installing = $this->sharedTempStore->get('project_browser')->get('installing'); - if (is_array($expected_installing)) { - ksort($expected_installing); - } - if (is_array($expected_requiring)) { - ksort($expected_requiring); - } - if (is_array($project_browser_installing)) { - ksort($project_browser_installing); - } - if (is_array($project_browser_requiring)) { - ksort($project_browser_requiring); - } - $this->assertSame($expected_requiring, $project_browser_requiring); - $this->assertSame($expected_installing, $project_browser_installing); - } - /** * Sends a POST request to the specified route with the provided project ID. * diff --git a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php index 93a74779829c7f2cb0248851e5835224cd9c7fd1..abd7f8d30577ff3d5743d22a7105585dea947172 100644 --- a/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php +++ b/tests/src/FunctionalJavascript/ProjectBrowserInstallerUiTest.php @@ -10,6 +10,7 @@ use Drupal\Core\State\StateInterface; use Drupal\FunctionalJavascriptTests\WebDriverTestBase; use Drupal\Tests\project_browser\Traits\PackageManagerFixtureUtilityTrait; use Drupal\project_browser\EnabledSourceHandler; +use Drupal\project_browser\InstallState; use Drupal\system\SystemManager; /** @@ -24,11 +25,11 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { use ProjectBrowserUiTestTrait, PackageManagerFixtureUtilityTrait; /** - * The shared tempstore object. + * The install state service. * - * @var \Drupal\Core\TempStore\SharedTempStore + * @var \Drupal\project_browser\InstallState */ - protected $sharedTempStore; + private InstallState $installState; /** * {@inheritdoc} @@ -54,7 +55,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { $this->initPackageManager(); - $this->sharedTempStore = $this->container->get('tempstore.shared'); + $this->installState = $this->container->get(InstallState::class); $this->config('project_browser.admin_settings')->set('enabled_sources', ['project_browser_test_mock'])->save(TRUE); $this->config('project_browser.admin_settings')->set('allow_ui_install', TRUE)->save(); @@ -196,7 +197,7 @@ class ProjectBrowserInstallerUiTest extends WebDriverTestBase { // Start install begin. $this->drupalGet('admin/modules/project_browser/install-begin'); - $this->sharedTempStore->get('project_browser')->delete('requiring'); + $this->installState->deleteAll(); $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('text', 'Cream cheese on a bagel'); // Try beginning another install while one is in progress, but not yet in