Skip to content
Snippets Groups Projects
Commit 8da48ed6 authored by Fran Garcia-Linares's avatar Fran Garcia-Linares Committed by Chris Wells
Browse files

Issue #3503258 by fjgarlin, bhumikavarshney, phenaproxima, chrisfromredfin,...

Issue #3503258 by fjgarlin, bhumikavarshney, phenaproxima, chrisfromredfin, tolstoydotcom: Truncate project IDs so that they can always fit into a `varchar` database column
parent 2be51269
Branches
Tags
No related merge requests found
Pipeline #510510 passed
......@@ -361,8 +361,8 @@ final class InstallerController extends ControllerBase {
* Status message.
*/
public function apply(string $stage_id): JsonResponse {
foreach (array_keys($this->installState->toArray()) as $project_id) {
$this->installState->setState($project_id, 'applying');
foreach ($this->installState->toArray() as $project) {
$this->installState->setState($project['project_id'], 'applying');
}
try {
$this->installer->claim($stage_id)->apply();
......
......@@ -143,14 +143,14 @@ final class EnabledSourceHandler implements LoggerAwareInterface, EventSubscribe
// Cache all the projects individually so they can be loaded by
// ::getStoredProject().
foreach ($results->list as $project) {
$storage->setIfNotExists($project->id, $project);
$this->storeProject($source_id, $project);
}
// If there were no query errors, store the results as a set of arguments
// to ProjectsResultsPage.
if (empty($results->error)) {
$storage->set($cache_key, [
$results->totalResults,
array_column($results->list, 'id'),
array_map(Project::normalizeId(...), array_column($results->list, 'id')),
$results->pluginLabel,
$source_id,
$results->error,
......@@ -201,6 +201,19 @@ final class EnabledSourceHandler implements LoggerAwareInterface, EventSubscribe
return $enabled_sources[$source_id]->getProjects($query);
}
/**
* Store a project in the non-volatile data store.
*
* @param string $source_id
* The ID of the source plugin to store the project in.
* @param \Drupal\project_browser\ProjectBrowser\Project $project
* Project to store.
*/
private function storeProject(string $source_id, Project $project): void {
$id = Project::normalizeId($project->id);
$this->keyValue($source_id)->setIfNotExists($id, $project);
}
/**
* Looks up a previously stored project by its ID.
*
......@@ -215,6 +228,7 @@ final class EnabledSourceHandler implements LoggerAwareInterface, EventSubscribe
*/
public function getStoredProject(string $id): Project {
[$source_id, $local_id] = explode('/', $id, 2);
$local_id = Project::normalizeId($local_id);
return $this->keyValue($source_id)->get($local_id) ?? throw new \RuntimeException("Project '$id' was not found in non-volatile storage.");
}
......
......@@ -75,25 +75,29 @@ final class InstallState {
*/
public function setState(string $project_id, ?string $status): void {
$this->keyValue->setIfNotExists('__timestamp', $this->time->getRequestTime());
$normalized_id = Project::normalizeId($project_id);
if (is_string($status)) {
$this->keyValue->set($project_id, ['status' => $status]);
$this->keyValue->set($normalized_id, [
'status' => $status,
'project_id' => $project_id,
]);
}
else {
$this->keyValue->delete($project_id);
$this->keyValue->delete($normalized_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.
* @param string $project_id
* The project ID to retrieve.
*
* @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);
public function getStatus(string $project_id): ?string {
$project_data = $this->keyValue->get(Project::normalizeId($project_id));
return $project_data['status'] ?? NULL;
}
......
......@@ -121,6 +121,23 @@ final class Project {
$this->id = $id;
}
/**
* Normalizes a project ID for database storage.
*
* Ensures the ID is no more than 104 characters long, so that it can fit into
* a `varchar` database column. We append a partial hash of the original,
* full-length ID in order to guarantee uniqueness.
*
* @param string $id
* A project ID to normalize.
*
* @return string
* The normalized project ID.
*/
public static function normalizeId(string $id): string {
return substr($id, 0, 96) . '-' . substr(sha1($id), 0, 8);
}
/**
* Set the project short description.
*
......
......@@ -444,13 +444,7 @@ final class InstallerControllerTest extends BrowserTestBase {
$response = $this->drupalGet('admin/modules/project_browser/install-begin', $request_options);
$this->assertSession()->statusCodeEquals(418);
$assert_unlock_response($response, "The process for adding the project that was locked less than a minute ago might still be in progress. Consider waiting a few more minutes before using [+unlock link].");
$expected = [
'project_browser_test_mock/awesome_module' => [
'status' => 'requiring',
],
];
$install_state = $this->container->get(InstallState::class)->toArray();
$this->assertSame($expected, $install_state);
$this->assertInstallInProgress('project_browser_test_mock/awesome_module', 'requiring');
$this->assertFalse($this->installer->isAvailable());
$this->assertFalse($this->installer->isApplying());
......@@ -591,16 +585,14 @@ final class InstallerControllerTest extends BrowserTestBase {
*
* @param string $project_id
* The ID of the project being enabled.
* @param string|null $status
* @param string|null $expected_status
* The install state.
*/
protected function assertInstallInProgress(string $project_id, ?string $status = NULL): void {
$expect_install[$project_id] = [
'status' => $status,
];
$install_state = $this->container->get(InstallState::class)
->toArray();
$this->assertSame($expect_install, $install_state);
protected function assertInstallInProgress(string $project_id, ?string $expected_status = NULL): void {
$this->assertSame(
$expected_status,
$this->container->get(InstallState::class)->getStatus($project_id),
);
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment