Skip to content

Commit

Permalink
WIP: Write migration for workspace rename
Browse files Browse the repository at this point in the history
untested.
  • Loading branch information
mhsdesign committed Feb 13, 2024
1 parent 9dcc825 commit 0f02c3c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,18 @@ public function __construct(
}

/**
* Adds affectedDimensionSpacePoints to NodePropertiesWereSet event, by replaying the content graph
* and then reading the dimension space points for the relevant NodeAggregate.
* Add the workspace name to the events meta-data, so it can be replayed.
*
* Needed for #4265: https://github.com/neos/neos-development-collection/issues/4265
* Needed for #4708: https://github.com/neos/neos-development-collection/pull/4708
*
* Included in May 2023 - before Neos 9.0 Beta 1.
* Included in February 2023 - before Neos 9.0 Beta 3.
*
* @param string $contentRepository Identifier of the Content Repository to set up
*/
public function fillAffectedDimensionSpacePointsInNodePropertiesWereSetCommand(string $contentRepository = 'default'): void
public function fillWorkspaceNameInCommandPayloadOfEventMetaDataCommand(string $contentRepository = 'default'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory);
$eventMigrationService->fillAffectedDimensionSpacePointsInNodePropertiesWereSet($this->outputLine(...));
$eventMigrationService->fillWorkspaceNameInCommandPayloadOfEventMetaData($this->outputLine(...));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,19 @@

use Doctrine\DBAL\Connection;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface;
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties;
use Neos\ContentRepository\Core\Projection\CatchUpOptions;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphProjection;
use Neos\ContentRepository\Core\Projection\Projections;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceFinder;
use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjection;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepositoryRegistry\Command\MigrateEventsCommandController;
use Neos\ContentRepositoryRegistry\Factory\EventStore\DoctrineEventStoreFactory;
use Neos\EventStore\EventStoreInterface;
use Neos\EventStore\Model\Event\SequenceNumber;
use Neos\EventStore\Model\EventStream\VirtualStreamName;
use Neos\Neos\FrontendRouting\Projection\DocumentUriPathProjection;

/**
* Content Repository service to perform migrations of events.
Expand All @@ -42,89 +40,95 @@ public function __construct(
}

/**
* Adds affectedDimensionSpacePoints to NodePropertiesWereSet event, by replaying the content graph
* and then reading the dimension space points for the relevant NodeAggregate.
* Add the workspace name to the events meta-data, so it can be replayed.
*
* Needed for #4265: https://github.com/neos/neos-development-collection/issues/4265
* Needed for #4708: https://github.com/neos/neos-development-collection/pull/4708
*
* Included in May 2023 - before Neos 9.0 Beta 1.
* Included in February 2023 - before Neos 9.0 Beta 3.
*
* @param \Closure $outputFn
* @return void
*/
public function fillAffectedDimensionSpacePointsInNodePropertiesWereSet(\Closure $outputFn)
public function fillWorkspaceNameInCommandPayloadOfEventMetaData(\Closure $outputFn)
{

$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 Graph Projection.');
$this->contentRepository->resetProjectionState(ContentGraphProjection::class);
$outputFn('Backup completed. Resetting WorkspaceProjection.');
$this->contentRepository->resetProjectionState(WorkspaceProjection::class);

$contentGraphProjection = $this->projections->get(ContentGraphProjection::class);
$contentGraph = $contentGraphProjection->getState();
assert($contentGraph instanceof ContentGraphInterface);
$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->type->value === 'NodePropertiesWereSet') {
$eventData = json_decode($eventEnvelope->event->data->value, true);
if (!isset($eventData['affectedDimensionSpacePoints'])) {
// Replay the projection until before the current NodePropertiesWereSet event
$this->contentRepository->catchUpProjection(ContentGraphProjection::class, CatchUpOptions::create(maximumSequenceNumber: $eventEnvelope->sequenceNumber->previous()));

// now we can ask the NodeAggregate (read model) for the covered DSPs.
$nodeAggregate = $contentGraph->findNodeAggregateById(
ContentStreamId::fromString($eventData['contentStreamId']),
NodeAggregateId::fromString($eventData['nodeAggregateId'])
);
assert($nodeAggregate !== null);
$affectedDimensionSpacePoints = $nodeAggregate->getCoverageByOccupant(
OriginDimensionSpacePoint::fromArray($eventData['originDimensionSpacePoint'])
);

// ... and update the event
$eventData['affectedDimensionSpacePoints'] = $affectedDimensionSpacePoints->jsonSerialize();
$outputFn(
'Rewriting %s: (%s, Origin: %s) => Affected: %s',
[
$eventEnvelope->sequenceNumber->value,
$eventEnvelope->event->type->value,
json_encode($eventData['originDimensionSpacePoint']),
json_encode($eventData['affectedDimensionSpacePoints'])
]
);
$this->updateEvent($eventEnvelope->sequenceNumber, $eventData);
}
if (!$eventEnvelope->event->metadata) {
continue;
}
}
$eventMetaData = $eventEnvelope->event->metadata->value;

$outputFn('Rewriting completed. Now catching up the GraphProjection to final state.');
$this->contentRepository->catchUpProjection(ContentGraphProjection::class, CatchUpOptions::create());
if (!in_array($eventMetaData['commandClass'] ?? null, [
// todo extend list
SetSerializedNodeProperties::class
])) {
continue;
}

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

if ($this->projections->has(DocumentUriPathProjection::class)) {
$outputFn('Found DocumentUriPathProjection. Will replay this, as it relies on the updated affectedDimensionSpacePoints');
$this->contentRepository->resetProjectionState(DocumentUriPathProjection::class);
$this->contentRepository->catchUpProjection(DocumentUriPathProjection::class, CatchUpOptions::create());
// 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;
}
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']
]
);

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

$outputFn('Rewriting completed. Now catching up the WorkspaceProjection to final state.');
$this->contentRepository->catchUpProjection(WorkspaceProjection::class, CatchUpOptions::create());

$outputFn('All done.');
}

/**
* @param array<mixed> $eventData
* @param array<mixed> $eventMetaData
*/
private function updateEvent(SequenceNumber $sequenceNumber, array $eventData): void
private function updateEventMetaData(SequenceNumber $sequenceNumber, array $eventMetaData): void
{
$eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId);
$this->connection->beginTransaction();
$this->connection->executeStatement(
'UPDATE ' . $eventTableName . ' SET payload=:payload WHERE sequencenumber=:sequenceNumber',
'UPDATE ' . $eventTableName . ' SET metadata=:metadata WHERE sequencenumber=:sequenceNumber',
[
'payload' => json_encode($eventData),
'metadata' => json_encode($eventMetaData),
'sequenceNumber' => $sequenceNumber->value
]
);
Expand Down

0 comments on commit 0f02c3c

Please sign in to comment.