Skip to content

Commit

Permalink
Merge pull request #5205 from dlubitz/task/5201-workspacename-migration
Browse files Browse the repository at this point in the history
TASK: Case sensitive migration and migration of baseWorkspaceName
  • Loading branch information
nezaniel authored Aug 17, 2024
2 parents 9e98694 + 09faadc commit 48e5d0a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@
*/
final class WorkspaceName implements \JsonSerializable
{
public const MAX_LENGTH = 30;
public const MAX_LENGTH = 36;

private const PATTERN = '/^[a-z][a-z0-9\-]{0,' . (self::MAX_LENGTH - 1) . '}$/';
private const PATTERN = '/^[a-z0-9][a-z0-9\-]{0,' . (self::MAX_LENGTH - 1) . '}$/';

public const WORKSPACE_NAME_LIVE = 'live';

Expand Down Expand Up @@ -89,8 +89,7 @@ public static function transliterateFromString(string $name): self

// If the name is still invalid at this point, we fall back to md5
if (!self::hasValidFormat($name)) {
$prefix = 'workspace-';
$name = $prefix . substr(md5($originalName), 0, self::MAX_LENGTH - strlen($prefix));
$name = substr(md5($originalName), 0, self::MAX_LENGTH);
}

return self::fromString($name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,10 @@ public function tryFromStringReturnsInstanceForValidValues(string $value): void
private static function invalidWorkspaceNames(): iterable
{
yield 'empty string' => [''];
yield 'only digits' => ['123'];
yield 'leading dash' => ['-invalid'];
yield 'upper case characters' => ['thisIsNotAllowed'];
yield 'whitespace' => ['this neither'];
yield 'exceeding max length' => ['this-is-just-a-little-too-long-'];
yield 'exceeding max length' => ['this-is-just-a-little-little-bit-too-long-'];
}

/**
Expand Down Expand Up @@ -99,8 +98,8 @@ private static function transliterateFromStringDataProvider(): iterable
yield 'chinese characters' => ['value' => '北京', 'expectedResult' => 'bei-jing'];
yield 'german umlauts' => ['value' => 'ümläute', 'expectedResult' => 'umlaute'];
yield 'white space' => ['value' => ' Contains spaces ', 'expectedResult' => 'contains-spaces'];
yield 'exceeding max length' => ['value' => 'This name is just a little too long', 'expectedResult' => 'this-name-is-just-a-little-too'];
yield 'only special characters' => ['value' => '-', 'expectedResult' => 'workspace-336d5ebc5436534e61d1'];
yield 'exceeding max length' => ['value' => 'This name is just a little little bit too long', 'expectedResult' => 'this-name-is-just-a-little-little-bi'];
yield 'only special characters' => ['value' => '-', 'expectedResult' => '336d5ebc5436534e61d16e63ddfca327'];
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,24 +385,86 @@ public function migratePayloadToValidWorkspaceNames(\Closure $outputFn): void

$eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId);
$this->connection->beginTransaction();
$statement = <<<SQL
$statementWorkspaceName = <<<SQL
UPDATE {$eventTableName}
SET
payload = JSON_SET(payload, '$.workspaceName', SUBSTR(MD5(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName'))), 1, 30))
payload = JSON_SET(
payload,
'$.workspaceName',
IF(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName')) REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$',
LOWER(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName'))),
MD5(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName')))
)
)
WHERE
JSON_EXTRACT(payload, '$.workspaceName') IS NOT NULL
AND JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName')) NOT REGEXP '^[a-z][a-z0-9\-]{0,30}$'
AND BINARY JSON_UNQUOTE(JSON_EXTRACT(payload, '$.workspaceName')) NOT REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$'
SQL;
$affectedRows = $this->connection->executeStatement($statement);
$affectedRowsWorkspaceName = $this->connection->executeStatement($statementWorkspaceName);

$statementBaseWorkspaceName = <<<SQL
UPDATE {$eventTableName}
SET
payload = JSON_SET(
payload,
'$.baseWorkspaceName',
IF(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.baseWorkspaceName')) REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$',
LOWER(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.baseWorkspaceName'))),
MD5(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.baseWorkspaceName')))
)
)
WHERE
JSON_EXTRACT(payload, '$.baseWorkspaceName') IS NOT NULL
AND BINARY JSON_UNQUOTE(JSON_EXTRACT(payload, '$.baseWorkspaceName')) NOT REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$'
SQL;
$affectedRowsBaseWorkspaceName = $this->connection->executeStatement($statementBaseWorkspaceName);

$sourceWorkspaceNameStatement = <<<SQL
UPDATE {$eventTableName}
SET
payload = JSON_SET(
payload,
'$.sourceWorkspaceName',
IF(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.sourceWorkspaceName')) REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$',
LOWER(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.sourceWorkspaceName'))),
MD5(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.sourceWorkspaceName')))
)
)
WHERE
JSON_EXTRACT(payload, '$.sourceWorkspaceName') IS NOT NULL
AND BINARY JSON_UNQUOTE(JSON_EXTRACT(payload, '$.sourceWorkspaceName')) NOT REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$'
SQL;
$sourceWorkspaceAffectedRows = $this->connection->executeStatement($sourceWorkspaceNameStatement);

$targetWorkspaceNameStatement = <<<SQL
UPDATE {$eventTableName}
SET
payload = JSON_SET(
payload,
'$.targetWorkspaceName',
IF(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.targetWorkspaceName')) REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$',
LOWER(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.targetWorkspaceName'))),
MD5(JSON_UNQUOTE(JSON_EXTRACT(payload, '$.targetWorkspaceName')))
)
)
WHERE
JSON_EXTRACT(payload, '$.targetWorkspaceName') IS NOT NULL
AND BINARY JSON_UNQUOTE(JSON_EXTRACT(payload, '$.targetWorkspaceName')) NOT REGEXP '^[a-z0-9][a-z0-9\-]{0,35}$'
SQL;
$targetWorkspaceAffectedRows = $this->connection->executeStatement($targetWorkspaceNameStatement);
$this->connection->commit();

if ($affectedRows === 0) {
if ($affectedRowsWorkspaceName === 0 && $affectedRowsBaseWorkspaceName === 0 && $sourceWorkspaceAffectedRows === 0 && $targetWorkspaceAffectedRows === 0) {
$outputFn('Migration was not necessary.');
return;
}

$outputFn();
$outputFn(sprintf('Migration applied to %s events.', $affectedRows));
$outputFn(sprintf('Migration applied to %s events and changed the workspaceName.', $affectedRowsWorkspaceName));
$outputFn(sprintf('Migration applied to %s events and changed the baseWorkspaceName.', $affectedRowsBaseWorkspaceName));
$outputFn(sprintf('Migration applied to %s events and changed the sourceWorkspaceName.', $sourceWorkspaceAffectedRows));
$outputFn(sprintf('Migration applied to %s events and changed the targetWorkspaceName.', $targetWorkspaceAffectedRows));
$outputFn(sprintf('You need to replay your projection for workspaces. Please run: ./flow cr:projectionreplay --projection=workspace'));
}

/** ------------------------ */
Expand Down
2 changes: 1 addition & 1 deletion Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ public static function fromAccountIdentifier(string $accountIdentifier): Workspa
1645656253
);
}
return WorkspaceName::fromString($name);
return WorkspaceName::transliterateFromString($name);
}
}

0 comments on commit 48e5d0a

Please sign in to comment.