Skip to content

Commit

Permalink
TASK: Simplify `migrateevents:fillworkspacenameincommandpayloadofeven…
Browse files Browse the repository at this point in the history
…tmetadata`
  • Loading branch information
mhsdesign committed Mar 13, 2024
1 parent 7e41bb6 commit 610b512
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public function migratePropertiesToUnsetCommand(string $contentRepository = 'def
}

/**
* Add the workspace name to the events meta-data, so it can be replayed.
* Adds a dummy workspace name to the events meta-data, so it can be replayed.
*
* Needed for #4708: https://github.com/neos/neos-development-collection/pull/4708
*
* Included in February 2023 - before Neos 9.0 Beta 3.
* Included in February 2023 - before final Neos 9.0 release
*
* @param string $contentRepository Identifier of the Content Repository to set up
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,22 @@
namespace Neos\ContentRepositoryRegistry\Service;

use Doctrine\DBAL\Connection;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;
use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNodeAndSerializedProperties;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\DisableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeDisabling\Command\EnableNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively;
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties;
use Neos\ContentRepository\Core\Projection\CatchUpOptions;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphProjection;
use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceFinder;
use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjection;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences;
use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName;
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType;
use Neos\ContentRepository\Core\Feature\NodeVariation\Command\CreateNodeVariant;
use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\CreateRootNodeAggregateWithNode;
use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\UpdateRootNodeAggregateDimensions;
use Neos\ContentRepositoryRegistry\Command\MigrateEventsCommandController;
use Neos\ContentRepositoryRegistry\Factory\EventStore\DoctrineEventStoreFactory;
use Neos\EventStore\EventStoreInterface;
Expand Down Expand Up @@ -227,82 +232,84 @@ public function migratePropertiesToUnset(\Closure $outputFn)


/**
* Add the workspace name to the events meta-data, so it can be replayed.
* Adds a dummy workspace name to the events meta-data, so it can be replayed.
*
* The value of the payload for `workspaceName` is only required to successfully instantiate a command by its metadata.
* This is only necessary for rebasing where directly override the workspace name to the target one.
* Thus, we simply enter a dummy string "migrated-missing".
*
* Needed for #4708: https://github.com/neos/neos-development-collection/pull/4708
*
* Included in February 2023 - before Neos 9.0 Beta 3.
* Included in February 2023 - before final Neos 9.0 release
*
* @param \Closure $outputFn
* @return void
*/
public function fillWorkspaceNameInCommandPayloadOfEventMetaData(\Closure $outputFn)
{
$this->eventsModified = [];

$backupEventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId)
. '_bak_' . date('Y_m_d_H_i_s');
$outputFn('Backup: copying events table to %s', [$backupEventTableName]);

$this->copyEventTable($backupEventTableName);

$outputFn('Backup completed. Resetting WorkspaceProjection.');
$this->contentRepository->resetProjectionState(WorkspaceProjection::class);

$workspaceProjection = $this->projections->get(WorkspaceProjection::class);
$workspaceFinder = $workspaceProjection->getState();
assert($workspaceFinder instanceof WorkspaceFinder);

$streamName = VirtualStreamName::all();
$eventStream = $this->eventStore->load($streamName);
foreach ($eventStream as $eventEnvelope) {
if (!$eventEnvelope->event->metadata) {
$outputRewriteNotice = fn(string $message) => $outputFn(sprintf('%s@%s %s', $eventEnvelope->sequenceNumber->value, $eventEnvelope->event->type->value, $message));
$eventMetaData = $eventEnvelope->event->metadata?->value;

if (!$eventMetaData || !($commandClassName = $eventMetaData['commandClass'] ?? null)) {
continue;
}
$eventMetaData = $eventEnvelope->event->metadata->value;

if (!in_array($eventMetaData['commandClass'] ?? null, [
// todo extend list
SetSerializedNodeProperties::class
/**
* Nearly all implementations of {@see RebasableToOtherWorkspaceInterface::createCopyForWorkspace()} have to migrate.
* The following commands all require the `$workspaceName` field and have no `$contentStreamId`.
* The commands AddDimensionShineThrough and MoveDimensionSpacePoint are exceptions to the rule, which don't
* require workspaces but still operate on content streams.
*/
if (!in_array($commandClassName, [
CreateNodeAggregateWithNodeAndSerializedProperties::class,
DisableNodeAggregate::class,
EnableNodeAggregate::class,
CopyNodesRecursively::class,
SetSerializedNodeProperties::class,
MoveNodeAggregate::class,
SetSerializedNodeReferences::class,
RemoveNodeAggregate::class,
ChangeNodeAggregateName::class,
ChangeNodeAggregateType::class,
CreateNodeVariant::class,
CreateRootNodeAggregateWithNode::class,
UpdateRootNodeAggregateDimensions::class,
])) {
continue;
}

if (!isset($eventMetaData['commandPayload']['contentStreamId']) || isset($eventMetaData['commandPayload']['workspaceName'])) {
if (isset($eventMetaData['commandPayload']['workspaceName'])) {
continue;
}

// Replay the projection until before the current event
$this->contentRepository->catchUpProjection(WorkspaceProjection::class, CatchUpOptions::create(maximumSequenceNumber: $eventEnvelope->sequenceNumber->previous()));

// now we can ask the read model
$workspace = $workspaceFinder->findOneByCurrentContentStreamId(ContentStreamId::fromString($eventMetaData['commandPayload']['contentStreamId']));

// ... and update the event
if (!$workspace) {
// todo does not exist, but as the value is not important when rebasing, as bernhard said, we will just enter a dummy.
$eventMetaData['commandPayload']['workspaceName'] = 'dummystring';
} else {
$eventMetaData['commandPayload']['workspaceName'] = $workspace->workspaceName->value;
}
// the payload is only used for rebasing where we override the workspace either way:
$eventMetaData['commandPayload']['workspaceName'] = 'missing';
unset($eventMetaData['commandPayload']['contentStreamId']);

$outputFn(
'Rewriting %s: (%s, ContentStreamId: %s) => WorkspaceName: %s',
[
$eventEnvelope->sequenceNumber->value,
$eventEnvelope->event->type->value,
$eventMetaData['commandPayload']['contentStreamId'],
$eventMetaData['commandPayload']['workspaceName']
]
);
$outputRewriteNotice(sprintf('Metadata: Added `workspaceName`'));

$this->updateEventMetaData($eventEnvelope->sequenceNumber, $eventMetaData);
}

$outputFn('Rewriting completed. Now catching up the WorkspaceProjection to final state.');
$this->contentRepository->catchUpProjection(WorkspaceProjection::class, CatchUpOptions::create());
if (!count($this->eventsModified)) {
$outputFn('Migration was not necessary.');
return;
}

$outputFn('All done.');
$outputFn();
$outputFn(sprintf('Migration applied to %s events.', count($this->eventsModified)));
}

/**
Expand Down

0 comments on commit 610b512

Please sign in to comment.