Skip to content

Commit

Permalink
Merge pull request #5148 from kitsunet/feature/node-reference-copy
Browse files Browse the repository at this point in the history
!!! FEATURE: References on creation and Copy
  • Loading branch information
kitsunet authored Nov 1, 2024
2 parents 3e7ac78 + 49541dd commit e7f9851
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Classes/Command/MigrateEventsCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,11 @@ public function migratePayloadToValidWorkspaceNamesCommand(string $contentReposi
$eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory);
$eventMigrationService->migratePayloadToValidWorkspaceNames($this->outputLine(...));
}

public function migrateSetReferencesToMultiNameFormatCommand(string $contentRepository = 'default'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory);
$eventMigrationService->migrateReferencesToMultiFormat($this->outputLine(...));
}
}
102 changes: 102 additions & 0 deletions Classes/Service/EventMigrationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties;
use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences;
use Neos\ContentRepository\Core\Feature\NodeReferencing\Event\NodeReferencesWereSet;
use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate;
use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName;
use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType;
Expand Down Expand Up @@ -248,6 +249,107 @@ public function migratePropertiesToUnset(\Closure $outputFn): void
}
}

public function migrateReferencesToMultiFormat(\Closure $outputFn): void
{
$this->eventsModified = [];
$warnings = 0;

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

$this->copyEventTable($backupEventTableName);

$streamName = VirtualStreamName::all();
$eventStream = $this->eventStore->load($streamName, EventStreamFilter::create(EventTypes::create(EventType::fromString('NodeReferencesWereSet'))));
foreach ($eventStream as $eventEnvelope) {
$outputRewriteNotice = fn(string $message) => $outputFn(sprintf('%s@%s %s', $eventEnvelope->sequenceNumber->value, $eventEnvelope->event->type->value, $message));
if ($eventEnvelope->event->type->value !== 'NodeReferencesWereSet') {
throw new \RuntimeException(sprintf('Unhandled event: %s', $eventEnvelope->event->type->value));
}

// migrate payload
$eventData = self::decodeEventPayload($eventEnvelope);
if (!isset($eventData['referenceName'])) {
// this event must have the new format already
continue;
}

$referenceNameString = $eventData['referenceName'];
// double array is not a mistake
$newReferencesPayload = [[
'referenceName' => $referenceNameString,
'references' => []
]];

unset($eventData['referenceName']);

// technically the false case should never happen, but we update it anyway
if (is_array($eventData['references'])) {
foreach ($eventData['references'] as $reference) {
$reference['target'] = $reference['targetNodeAggregateId'];
unset($reference['targetNodeAggregateId']);
if ($reference['properties'] === null) {
unset($reference['properties']);
}
$newReferencesPayload[0]['references'][] = $reference;
}
}

$eventData['references'] = $newReferencesPayload;
$this->updateEventPayload($eventEnvelope->sequenceNumber, $eventData);
$outputRewriteNotice('Payload: references migrated');

// optionally also migrate metadata
$eventMetaData = $eventEnvelope->event->metadata?->value;
if (!isset($eventMetaData['commandClass'])) {
continue;
}

if ($eventMetaData['commandClass'] !== SetSerializedNodeReferences::class) {
$warnings++;
$outputFn(sprintf('WARNING: Cannot migrate event metadata of %s as commandClass %s was not expected.', $eventEnvelope->event->type->value, $eventMetaData['commandClass']));
continue;
}

$referenceNameString = $eventMetaData['commandPayload']['referenceName'];
$newReferencesPayload = [
[
'referenceName' => $referenceNameString,
'references' => []
]
];

unset($eventMetaData['commandPayload']['referenceName']);

// technically the false case should never happen, but we update it anyway
if (is_array($eventMetaData['commandPayload']['references'])) {
foreach ($eventMetaData['commandPayload']['references'] as $reference) {
$reference['target'] = $reference['targetNodeAggregateId'];
unset($reference['targetNodeAggregateId']);
if ($reference['properties'] === null) {
unset($reference['properties']);
}
$newReferencesPayload[0]['references'][] = $reference;
}
}

$eventMetaData['commandPayload']['references'] = $newReferencesPayload;
$outputRewriteNotice('Metadata: references migrated');
$this->updateEventMetaData($eventEnvelope->sequenceNumber, $eventMetaData);
}

if (!count($this->eventsModified)) {
$outputFn('Migration was not necessary.');
return;
}

$outputFn();
$outputFn(sprintf('Migration applied to %s events.', count($this->eventsModified)));
if ($warnings) {
$outputFn(sprintf('WARNING: Finished but %d warnings emitted.', $warnings));
}
}

/**
* Adds a dummy workspace name to the events meta-data, so it can be rebased
Expand Down

0 comments on commit e7f9851

Please sign in to comment.