From 4b42f35d56791c64f05ba484ac839e5cc92e9f1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Wed, 23 Oct 2024 11:59:22 +0200 Subject: [PATCH 1/3] Add event migration for references --- .../MigrateEventsCommandController.php | 7 ++ Classes/Service/EventMigrationService.php | 101 ++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/Classes/Command/MigrateEventsCommandController.php b/Classes/Command/MigrateEventsCommandController.php index c0401b6..7dd38b0 100644 --- a/Classes/Command/MigrateEventsCommandController.php +++ b/Classes/Command/MigrateEventsCommandController.php @@ -97,4 +97,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(...)); + } } diff --git a/Classes/Service/EventMigrationService.php b/Classes/Service/EventMigrationService.php index 9cd7d31..5df7b2d 100644 --- a/Classes/Service/EventMigrationService.php +++ b/Classes/Service/EventMigrationService.php @@ -240,6 +240,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); + 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') { + continue; + } + + // 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 From 3b9d95b0f1e22aebf953f2a368ff83de24891c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Mu=CC=88ller?= Date: Wed, 30 Oct 2024 19:03:43 +0100 Subject: [PATCH 2/3] Code cleanup --- Classes/Service/EventMigrationService.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Classes/Service/EventMigrationService.php b/Classes/Service/EventMigrationService.php index 5df7b2d..e0f18e3 100644 --- a/Classes/Service/EventMigrationService.php +++ b/Classes/Service/EventMigrationService.php @@ -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; @@ -252,7 +253,7 @@ public function migrateReferencesToMultiFormat(\Closure $outputFn): void $this->copyEventTable($backupEventTableName); $streamName = VirtualStreamName::all(); - $eventStream = $this->eventStore->load($streamName); + $eventStream = $this->eventStore->load($streamName, EventStreamFilter::create(EventTypes::create(EventType::fromString(NodeReferencesWereSet::class)))); 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') { From 5d64dd6a4f62d70f07f38b754e761924ade45a05 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 30 Oct 2024 19:52:17 +0100 Subject: [PATCH 3/3] TASK: Fix migrateReferencesToMultiFormat --- Classes/Service/EventMigrationService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Service/EventMigrationService.php b/Classes/Service/EventMigrationService.php index e0f18e3..146e3f0 100644 --- a/Classes/Service/EventMigrationService.php +++ b/Classes/Service/EventMigrationService.php @@ -253,11 +253,11 @@ public function migrateReferencesToMultiFormat(\Closure $outputFn): void $this->copyEventTable($backupEventTableName); $streamName = VirtualStreamName::all(); - $eventStream = $this->eventStore->load($streamName, EventStreamFilter::create(EventTypes::create(EventType::fromString(NodeReferencesWereSet::class)))); + $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') { - continue; + throw new \RuntimeException(sprintf('Unhandled event: %s', $eventEnvelope->event->type->value)); } // migrate payload