From 6a15b6d2c882e66071cb29ff86600dc5f2b0aab2 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Wed, 29 Jan 2025 09:36:29 +0100 Subject: [PATCH] TASK: Provide transparent conflict when attempting to publish legacy copy nodes --- .../Classes/Feature/RebaseableCommands.php | 10 +++-- .../Feature/WorkspaceCommandHandler.php | 43 ++++++++++++++++--- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index 2c47299af7..20df892d60 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -11,7 +11,6 @@ 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\Feature\NodeMove\Command\MoveNodeAggregate; use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences; @@ -24,7 +23,7 @@ use Neos\ContentRepository\Core\Feature\SubtreeTagging\Command\TagSubtree; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Command\UntagSubtree; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; -use Neos\EventStore\Model\EventStream\EventStreamInterface; +use Neos\EventStore\Model\EventEnvelope; /** * @internal @@ -43,11 +42,14 @@ public function __construct( $this->items = $items; } - public static function extractFromEventStream(EventStreamInterface $eventStream): self + /** + * @param iterable $eventStream + */ + public static function extractFromEventStream(iterable $eventStream): self { $commands = []; foreach ($eventStream as $eventEnvelope) { - if ($eventEnvelope->event->metadata && isset($eventEnvelope->event->metadata?->value['commandClass'])) { + if (isset($eventEnvelope->event->metadata?->value['commandClass'])) { $commands[] = RebaseableCommand::extractFromEventEnvelope($eventEnvelope); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php index f06de074a3..4525362273 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php @@ -50,6 +50,7 @@ use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Command\RebaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvent; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvents; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\PartialWorkspaceRebaseFailed; @@ -67,6 +68,7 @@ use Neos\EventStore\Exception\ConcurrencyException; use Neos\EventStore\Model\Event\SequenceNumber; use Neos\EventStore\Model\Event\Version; +use Neos\EventStore\Model\EventEnvelope; use Neos\EventStore\Model\EventStream\EventStreamInterface; use Neos\EventStore\Model\EventStream\ExpectedVersion; @@ -199,10 +201,10 @@ private function handlePublishWorkspace( $baseWorkspaceContentStreamVersion = $this->requireOpenContentStreamAndVersion($baseWorkspace, $commandHandlingDependencies); $rebaseableCommands = RebaseableCommands::extractFromEventStream( - $this->eventStore->load( + $this->triggerConflictForLegacyEvents($this->eventStore->load( ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId) ->getEventStreamName() - ) + ), dropLegacyEvents: false) ); yield $this->closeContentStream( @@ -369,10 +371,10 @@ private function handleRebaseWorkspace( } $rebaseableCommands = RebaseableCommands::extractFromEventStream( - $this->eventStore->load( + $this->triggerConflictForLegacyEvents($this->eventStore->load( ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId) ->getEventStreamName() - ) + ), dropLegacyEvents: $command->rebaseErrorHandlingStrategy === RebaseErrorHandlingStrategy::STRATEGY_FORCE) ); yield $this->closeContentStream( @@ -432,6 +434,35 @@ static function ($handle) use ($rebaseableCommands): void { yield $this->removeContentStreamWithoutConstraintChecks($workspace->currentContentStreamId); } + /** + * This layer can be removed if there are no legacy events expected during rebasing + * @return iterable + */ + private function triggerConflictForLegacyEvents(EventStreamInterface $eventStream, bool $dropLegacyEvents): iterable + { + foreach ($eventStream as $eventEnvelope) { + if (($eventEnvelope->event->metadata?->value['commandClass'] ?? '') === 'Neos\\ContentRepository\\Core\\Feature\\NodeDuplication\\Command\\CopyNodesRecursively') { + // The original draft of the CopyNodesRecursively command was removed. In case events that are created via that command are attempted to be + // normally rebased, published, or discarded we manually cause a conflict to ensure that the event is removed. + if ($dropLegacyEvents === true) { + // a forced rebase drops the legacy events + continue; + } + $originalEvent = $this->eventNormalizer->denormalize($eventEnvelope->event); + throw WorkspaceRebaseFailed::duringRebase( + new ConflictingEvents( + new ConflictingEvent( + $originalEvent, + throw new \RuntimeException('The legacy command CopyNodesRecursively was removed. Its not possible to rebase this event and it must be dropped.', 1738139237), + $eventEnvelope->sequenceNumber + ), + ) + ); + } + yield $eventEnvelope; + } + } + /** * This method is like a combined Rebase and Publish! * @@ -452,10 +483,10 @@ private function handlePublishIndividualNodesFromWorkspace( $baseWorkspaceContentStreamVersion = $this->requireOpenContentStreamAndVersion($baseWorkspace, $commandHandlingDependencies); $rebaseableCommands = RebaseableCommands::extractFromEventStream( - $this->eventStore->load( + $this->triggerConflictForLegacyEvents($this->eventStore->load( ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId) ->getEventStreamName() - ) + ), dropLegacyEvents: false) ); [$matchingCommands, $remainingCommands] = $rebaseableCommands->separateMatchingAndRemainingCommands($command->nodesToPublish);