diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/EventSourced/Migration/AddNewProperty_NoDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/EventSourced/Migration/AddNewProperty_NoDimensions.feature index 08f35b9c556..0f6d9270e71 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/EventSourced/Migration/AddNewProperty_NoDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/EventSourced/Migration/AddNewProperty_NoDimensions.feature @@ -13,6 +13,8 @@ Feature: Add New Property properties: text: type: string + dateTime: + type: DateTime """ And using identifier "default", I define a content repository And I am in content repository "default" @@ -64,7 +66,7 @@ Feature: Add New Property - type: 'AddNewProperty' settings: - newPropertyName: 'aDateOutsideSchema' + newPropertyName: 'dateTime' serializedValue: '2013-09-09T12:04:12+00:00' type: 'DateTime' """ @@ -82,6 +84,46 @@ Feature: Add New Property | text | "Original text" | Then I expect a node identified by migration-cs;other;{} to exist in the content graph And I expect this node to have the following properties: - | Key | Value | - | text | "fixed value" | - | aDateOutsideSchema | Date:2013-09-09T12:04:12+00:00 | + | Key | Value | + | text | "fixed value" | + | dateTime | Date:2013-09-09T12:04:12+00:00 | + + Scenario: Adding a property that is not defined in the node type schema + When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs" and exceptions are caught: + """yaml + migration: + - + filters: + - + type: 'NodeType' + settings: + nodeType: 'Neos.ContentRepository.Testing:Document' + transformations: + - + type: 'AddNewProperty' + settings: + newPropertyName: 'aDateOutsideSchema' + serializedValue: '2013-09-09T12:04:12+00:00' + type: 'DateTime' + """ + Then the last command should have thrown an exception of type "PropertyCannotBeSet" + + Scenario: Adding a property with a different type than defined by the node type schema + When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs" and exceptions are caught: + """yaml + migration: + - + filters: + - + type: 'NodeType' + settings: + nodeType: 'Neos.ContentRepository.Testing:Document' + transformations: + - + type: 'AddNewProperty' + settings: + newPropertyName: 'dateTime' + serializedValue: '2013-09-09T12:04:12+00:00' + type: 'string' + """ + Then the last command should have thrown an exception of type "PropertyCannotBeSet" diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/01-ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/01-ConstraintChecks.feature index 7c5c5a7c002..bb15c251e28 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/01-ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/01-ConstraintChecks.feature @@ -73,8 +73,8 @@ Feature: Workspace discarding - complex chained functionality | nodesToDiscard | [{"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 11 | CreateNodeVariant | NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint | + | SequenceNumber | Event | Exception | + | 11 | NodeGeneralizationVariantWasCreated | NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint | When the command DiscardWorkspace is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/02-RebasingWithAutoCreatedNodes.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/02-RebasingWithAutoCreatedNodes.feature index 9a8e3dd3c4e..33bc813c32a 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/02-RebasingWithAutoCreatedNodes.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/02-RebasingWithAutoCreatedNodes.feature @@ -79,8 +79,8 @@ Feature: Rebasing auto-created nodes works # rebase of SetSerializedNodeProperties When the command RebaseWorkspace is executed with payload: - | Key | Value | - | workspaceName | "user-test" | + | Key | Value | + | workspaceName | "user-test" | | rebasedContentStreamId | "user-cs-rebased" | # This should properly work; no error. diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/03-RebasingWithConflictingChanges.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/03-RebasingWithConflictingChanges.feature index bbd1de5307c..7f30b4c68af 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/03-RebasingWithConflictingChanges.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W6-WorkspaceRebasing/03-RebasingWithConflictingChanges.feature @@ -101,8 +101,8 @@ Feature: Workspace rebasing - conflicting changes Then I expect the content stream "user-cs-two" to exist Then I expect the content stream "user-cs-two-rebased" to not exist Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 13 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | + | SequenceNumber | Event | Exception | + | 13 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | When the command RebaseWorkspace is executed with payload: | Key | Value | @@ -169,9 +169,9 @@ Feature: Workspace rebasing - conflicting changes Then I expect the content stream "user-cs-identifier" to exist Then I expect the content stream "user-cs-identifier-rebased" to not exist Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 12 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | - | 14 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | + | SequenceNumber | Event | Exception | + | 12 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | + | 14 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | When the command RebaseWorkspace is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/01-ConstraintChecks.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/01-ConstraintChecks.feature index ac52e28289b..60bf7aadc2b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/01-ConstraintChecks.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/01-ConstraintChecks.feature @@ -83,9 +83,9 @@ Feature: Workspace publication - complex chained functionality | nodesToPublish | [{"dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-nodebelig"}] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 13 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | - | 14 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | + | SequenceNumber | Event | Exception | + | 13 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | + | 14 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | Scenario: Vary to generalization, then publish only the child node so that an exception is thrown. Ensure that the workspace recovers from this When the command CreateNodeVariant is executed with payload: @@ -107,8 +107,8 @@ Feature: Workspace publication - complex chained functionality | nodesToPublish | [{"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "nody-mc-nodeface"}] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 13 | CreateNodeVariant | NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint | + | SequenceNumber | Event | Exception | + | 13 | NodeGeneralizationVariantWasCreated | NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint | When the command PublishWorkspace is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/03-MoreBasicFeatures.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/03-MoreBasicFeatures.feature index b65bee35200..68a71a14266 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/03-MoreBasicFeatures.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/03-MoreBasicFeatures.feature @@ -201,8 +201,8 @@ Feature: Publishing individual nodes (basics) | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "sir-unchanged"}] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 14 | TagSubtree | SubtreeIsAlreadyTagged | + | SequenceNumber | Event | Exception | + | 14 | SubtreeWasTagged | SubtreeIsAlreadyTagged | Scenario: It is possible to publish all nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W9-WorkspaceDiscarding/02-DiscardWorkspace.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W9-WorkspaceDiscarding/02-DiscardWorkspace.feature index 0e180f7d0dd..158cd4dabb5 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W9-WorkspaceDiscarding/02-DiscardWorkspace.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W9-WorkspaceDiscarding/02-DiscardWorkspace.feature @@ -139,8 +139,8 @@ Feature: Workspace discarding - basic functionality | workspaceName | "user-ws-two" | | rebasedContentStreamId | "user-cs-two-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: - | SequenceNumber | Command | Exception | - | 13 | SetSerializedNodeProperties | NodeAggregateCurrentlyDoesNotExist | + | SequenceNumber | Event | Exception | + | 13 | NodePropertiesWereSet | NodeAggregateCurrentlyDoesNotExist | Then workspace user-ws-two has status OUTDATED diff --git a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandBus.php b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandBus.php index 92673a00c82..5621e17fd2d 100644 --- a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandBus.php +++ b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandBus.php @@ -6,6 +6,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\EventStore\EventsToPublish; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; /** * Implementation Detail of {@see ContentRepository::handle}, which does the command dispatching to the different @@ -31,7 +32,7 @@ public function __construct( /** * @return EventsToPublish|\Generator */ - public function handle(CommandInterface $command): EventsToPublish|\Generator + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command): EventsToPublish|\Generator { // multiple handlers must not handle the same command foreach ($this->handlers as $handler) { diff --git a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandHandlerInterface.php b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandHandlerInterface.php index b36d5d3ab75..93cba7111ef 100644 --- a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandHandlerInterface.php +++ b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandHandlerInterface.php @@ -5,6 +5,7 @@ namespace Neos\ContentRepository\Core\CommandHandler; use Neos\ContentRepository\Core\EventStore\EventsToPublish; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; /** * Common interface for all Content Repository command handlers @@ -15,7 +16,7 @@ */ interface CommandHandlerInterface { - public function canHandle(CommandInterface $command): bool; + public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool; /** * "simple" command handlers return EventsToPublish directly @@ -25,5 +26,5 @@ public function canHandle(CommandInterface $command): bool; * * @return EventsToPublish|\Generator */ - public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator; + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator; } diff --git a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandInterface.php b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandInterface.php index fb36c3f6d11..4e1fe2e45d4 100644 --- a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandInterface.php +++ b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandInterface.php @@ -4,10 +4,15 @@ namespace Neos\ContentRepository\Core\CommandHandler; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; + /** - * Common interface for all commands of the Content Repository + * Common interface for all commands of the content repository + * + * Note: Some commands also implement the {@see RebasableToOtherWorkspaceInterface} + * others are converted to the rebasable counter-part at command handling time, serializing their state to make them deterministic * - * @internal because extra commands are no extension point + * @internal sealed interface. Custom commands cannot be handled and are no extension point! */ interface CommandInterface { diff --git a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandSimulator.php b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandSimulator.php index 10add5ff78d..9becebc5a2d 100644 --- a/Neos.ContentRepository.Core/Classes/CommandHandler/CommandSimulator.php +++ b/Neos.ContentRepository.Core/Classes/CommandHandler/CommandSimulator.php @@ -9,8 +9,8 @@ use Neos\ContentRepository\Core\EventStore\EventNormalizer; use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\RebaseableCommand; -use Neos\ContentRepository\Core\Feature\WorkspaceRebase\CommandsThatFailedDuringRebase; -use Neos\ContentRepository\Core\Feature\WorkspaceRebase\CommandThatFailedDuringRebase; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvents; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvent; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphProjectionInterface; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\EventStore\Helper\InMemoryEventStore; @@ -45,7 +45,7 @@ */ final class CommandSimulator { - private CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase; + private ConflictingEvents $conflictingEvents; private readonly InMemoryEventStore $inMemoryEventStore; @@ -56,7 +56,7 @@ public function __construct( private readonly WorkspaceName $workspaceNameToSimulateIn, ) { $this->inMemoryEventStore = new InMemoryEventStore(); - $this->commandsThatFailedDuringRebase = new CommandsThatFailedDuringRebase(); + $this->conflictingEvents = new ConflictingEvents(); } /** @@ -86,9 +86,11 @@ private function handle(RebaseableCommand $rebaseableCommand): void try { $eventsToPublish = $this->commandBus->handle($commandInWorkspace); } catch (\Exception $exception) { - $this->commandsThatFailedDuringRebase = $this->commandsThatFailedDuringRebase->withAppended( - new CommandThatFailedDuringRebase( - $rebaseableCommand->originalCommand, + $originalEvent = $this->eventNormalizer->denormalize($rebaseableCommand->originalEvent); + + $this->conflictingEvents = $this->conflictingEvents->withAppended( + new ConflictingEvent( + $originalEvent, $exception, $rebaseableCommand->originalSequenceNumber ) @@ -155,13 +157,13 @@ public function eventStream(): EventStreamInterface return $this->inMemoryEventStore->load(VirtualStreamName::all()); } - public function hasCommandsThatFailed(): bool + public function hasConflicts(): bool { - return !$this->commandsThatFailedDuringRebase->isEmpty(); + return !$this->conflictingEvents->isEmpty(); } - public function getCommandsThatFailed(): CommandsThatFailedDuringRebase + public function getConflictingEvents(): ConflictingEvents { - return $this->commandsThatFailedDuringRebase; + return $this->conflictingEvents; } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/RebasableToOtherWorkspaceInterface.php b/Neos.ContentRepository.Core/Classes/Feature/Common/RebasableToOtherWorkspaceInterface.php index daf152f5781..a77ad194420 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/RebasableToOtherWorkspaceInterface.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/RebasableToOtherWorkspaceInterface.php @@ -15,19 +15,30 @@ namespace Neos\ContentRepository\Core\Feature\Common; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; +use Neos\ContentRepository\Core\CommandHandler\CommandSimulator; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** - * This interface is implemented by **commands** which can be rebased to other Content Streams. This is basically all - * node-based commands. + * Common (marker) interface for all **commands** that need to be serialized for rebasing to other workspaces + * + * If the api command {@see CommandInterface} is serializable on its own it will directly implement this interface. + * For complex commands a serialized counterpart - which is not api - will be build which implements this interface. + * + * During a rebase, the command (either the original {@see CommandInterface} or its serialized counterpart) will be deserialized + * from array {@see RebasableToOtherWorkspaceInterface::fromArray()} and reapplied via the {@see CommandSimulator} * * Reminder: a rebase can fail, because the target content stream might contain conflicting changes. * * @internal used internally for the rebasing mechanism of content streams */ -interface RebasableToOtherWorkspaceInterface extends CommandInterface +interface RebasableToOtherWorkspaceInterface extends \JsonSerializable { public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self; + + /** + * @param array $array + */ + public static function fromArray(array $array): self; } diff --git a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/Command/AddDimensionShineThrough.php b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/Command/AddDimensionShineThrough.php index 936505d278d..f3e3f55bdad 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/Command/AddDimensionShineThrough.php +++ b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/Command/AddDimensionShineThrough.php @@ -63,9 +63,6 @@ public static function create( return new self($workspaceName, $source, $target); } - /** - * @param array $array - */ public static function fromArray(array $array): self { return new self( diff --git a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php index 4433ac05661..72695af98c7 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/DimensionSpaceAdjustment/DimensionSpaceCommandHandler.php @@ -15,8 +15,8 @@ */ use Neos\ContentRepository\Core\CommandHandler\CommandHandlerInterface; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies; +use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\ContentDimensionZookeeper; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; @@ -26,13 +26,14 @@ use Neos\ContentRepository\Core\DimensionSpace\VariantType; use Neos\ContentRepository\Core\EventStore\Events; use Neos\ContentRepository\Core\EventStore\EventsToPublish; -use Neos\ContentRepository\Core\Feature\RebaseableCommand; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Command\AddDimensionShineThrough; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Command\MoveDimensionSpacePoint; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionShineThroughWasAdded; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Exception\DimensionSpacePointAlreadyExists; +use Neos\ContentRepository\Core\Feature\RebaseableCommand; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\EventStore\Model\EventStream\ExpectedVersion; @@ -48,12 +49,12 @@ public function __construct( ) { } - public function canHandle(CommandInterface $command): bool + public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool { return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName()); } - public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish { /** @phpstan-ignore-next-line */ return match ($command::class) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeAggregateCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/NodeAggregateCommandHandler.php index eea2319dd3a..f014440718c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeAggregateCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeAggregateCommandHandler.php @@ -15,13 +15,14 @@ namespace Neos\ContentRepository\Core\Feature; use Neos\ContentRepository\Core\CommandHandler\CommandHandlerInterface; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies; +use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\Common\ConstraintChecks; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\Common\TetheredNodeInternals; use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNodeAndSerializedProperties; @@ -86,12 +87,12 @@ public function __construct( ) { } - public function canHandle(CommandInterface $command): bool + public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool { return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName()); } - public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish { /** @phpstan-ignore-next-line */ return match ($command::class) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Command/CreateNodeAggregateWithNodeAndSerializedProperties.php b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Command/CreateNodeAggregateWithNodeAndSerializedProperties.php index ddff2d673d6..2eb1324ae2e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Command/CreateNodeAggregateWithNodeAndSerializedProperties.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeCreation/Command/CreateNodeAggregateWithNodeAndSerializedProperties.php @@ -14,7 +14,6 @@ namespace Neos\ContentRepository\Core\Feature\NodeCreation\Command; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; @@ -34,7 +33,6 @@ * @internal implementation detail, use {@see CreateNodeAggregateWithNode} instead. */ final readonly class CreateNodeAggregateWithNodeAndSerializedProperties implements - CommandInterface, \JsonSerializable, MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php index edffe38ad5c..cce5dd61e29 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/NodeDuplicationCommandHandler.php @@ -14,9 +14,8 @@ namespace Neos\ContentRepository\Core\Feature\NodeDuplication; -use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies; -use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\CommandHandler\CommandHandlerInterface; +use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\ContentDimensionZookeeper; @@ -27,17 +26,18 @@ use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\Common\ConstraintChecks; use Neos\ContentRepository\Core\Feature\Common\InterdimensionalSiblings; -use Neos\ContentRepository\Core\Feature\RebaseableCommand; use Neos\ContentRepository\Core\Feature\Common\NodeCreationInternals; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; use Neos\ContentRepository\Core\Feature\NodeDuplication\Command\CopyNodesRecursively; use Neos\ContentRepository\Core\Feature\NodeDuplication\Dto\NodeSubtreeSnapshot; +use Neos\ContentRepository\Core\Feature\RebaseableCommand; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; +use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\SharedModel\Exception\NodeConstraintException; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** * @internal from userland, you'll use ContentRepository::handle to dispatch commands @@ -64,12 +64,12 @@ protected function getAllowedDimensionSubspace(): DimensionSpacePointSet return $this->contentDimensionZookeeper->getAllowedDimensionSubspace(); } - public function canHandle(CommandInterface $command): bool + public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool { return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName()); } - public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish { /** @phpstan-ignore-next-line */ return match ($command::class) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php index f9ca77329d7..c3906bae03f 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php @@ -14,7 +14,6 @@ namespace Neos\ContentRepository\Core\Feature\NodeModification\Command; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; @@ -22,7 +21,6 @@ use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -33,7 +31,6 @@ * @internal implementation detail, use {@see SetNodeProperties} instead. */ final readonly class SetSerializedNodeProperties implements - CommandInterface, \JsonSerializable, MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php index ef2b5b7ed1e..132f1299ed7 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php @@ -14,7 +14,6 @@ namespace Neos\ContentRepository\Core\Feature\NodeReferencing\Command; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; @@ -31,7 +30,6 @@ * @internal implementation detail, use {@see SetNodeReferences} instead. */ final readonly class SetSerializedNodeReferences implements - CommandInterface, \JsonSerializable, MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php index 38d1f195ed6..d19d6210a48 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php @@ -59,9 +59,6 @@ public static function create(WorkspaceName $workspaceName, NodeAggregateId $nod return new self($workspaceName, $nodeAggregateId, $newNodeName); } - /** - * @param array $array - */ public static function fromArray(array $array): self { return new self( diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommand.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommand.php index 210f91b8b8a..b62d85eddd8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommand.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommand.php @@ -9,9 +9,11 @@ use Neos\ContentRepository\Core\EventStore\InitiatingEventMetadata; use Neos\ContentRepository\Core\Feature\Common\PublishableToWorkspaceInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; +use Neos\EventStore\Model\Event; use Neos\EventStore\Model\Event\EventId; use Neos\EventStore\Model\Event\EventMetadata; use Neos\EventStore\Model\Event\SequenceNumber; +use Neos\EventStore\Model\EventEnvelope; /** * @internal @@ -20,17 +22,18 @@ { public function __construct( public RebasableToOtherWorkspaceInterface $originalCommand, + public Event $originalEvent, public EventMetadata $initiatingMetaData, public SequenceNumber $originalSequenceNumber ) { } - public static function extractFromEventMetaData(EventMetadata $eventMetadata, SequenceNumber $sequenceNumber): self + public static function extractFromEventEnvelope(EventEnvelope $eventEnvelope): self { - $commandToRebaseClass = $eventMetadata->value['commandClass'] ?? null; - $commandToRebasePayload = $eventMetadata->value['commandPayload'] ?? null; + $commandToRebaseClass = $eventEnvelope->event->metadata?->value['commandClass'] ?? null; + $commandToRebasePayload = $eventEnvelope->event->metadata?->value['commandPayload'] ?? null; - if ($commandToRebaseClass === null || $commandToRebasePayload === null) { + if ($commandToRebaseClass === null || $commandToRebasePayload === null || $eventEnvelope->event->metadata === null) { throw new \RuntimeException('Command cannot be extracted from metadata, missing commandClass or commandPayload.', 1729847804); } @@ -46,8 +49,9 @@ public static function extractFromEventMetaData(EventMetadata $eventMetadata, Se $commandInstance = $commandToRebaseClass::fromArray($commandToRebasePayload); return new self( $commandInstance, - InitiatingEventMetadata::extractInitiatingMetadata($eventMetadata), - $sequenceNumber + $eventEnvelope->event, + InitiatingEventMetadata::extractInitiatingMetadata($eventEnvelope->event->metadata), + $eventEnvelope->sequenceNumber ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index 5f4146321fe..fd746e051a4 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -30,7 +30,7 @@ public static function extractFromEventStream(EventStreamInterface $eventStream) $commands = []; foreach ($eventStream as $eventEnvelope) { if ($eventEnvelope->event->metadata && isset($eventEnvelope->event->metadata?->value['commandClass'])) { - $commands[] = RebaseableCommand::extractFromEventMetaData($eventEnvelope->event->metadata, $eventEnvelope->sequenceNumber); + $commands[] = RebaseableCommand::extractFromEventEnvelope($eventEnvelope); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php index 20143536272..b1be71e7ce6 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php @@ -20,14 +20,13 @@ use Neos\ContentRepository\Core\CommandHandler\CommandSimulatorFactory; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\EventStore\DecoratedEvent; -use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\EventStore\EventNormalizer; use Neos\ContentRepository\Core\EventStore\Events; use Neos\ContentRepository\Core\EventStore\EventsToPublish; use Neos\ContentRepository\Core\Feature\Common\PublishableToWorkspaceInterface; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamClosing\Event\ContentStreamWasClosed; use Neos\ContentRepository\Core\Feature\ContentStreamClosing\Event\ContentStreamWasReopened; -use Neos\ContentRepository\Core\Feature\ContentStreamForking\Event\ContentStreamWasForked; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateRootWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\RootWorkspaceWasCreated; @@ -62,7 +61,6 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceStatus; use Neos\EventStore\EventStoreInterface; -use Neos\EventStore\Model\Event\EventType; use Neos\EventStore\Model\Event\SequenceNumber; use Neos\EventStore\Model\Event\Version; use Neos\EventStore\Model\EventStream\EventStreamInterface; @@ -82,12 +80,12 @@ public function __construct( ) { } - public function canHandle(CommandInterface $command): bool + public function canHandle(CommandInterface|RebasableToOtherWorkspaceInterface $command): bool { return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName()); } - public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): \Generator + public function handle(CommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): \Generator { /** @phpstan-ignore-next-line */ return match ($command::class) { @@ -238,8 +236,8 @@ static function ($handle) use ($rebaseableCommands): void { } ); - if ($commandSimulator->hasCommandsThatFailed()) { - throw WorkspaceRebaseFailed::duringPublish($commandSimulator->getCommandsThatFailed()); + if ($commandSimulator->hasConflicts()) { + throw WorkspaceRebaseFailed::duringPublish($commandSimulator->getConflictingEvents()); } yield new EventsToPublish( @@ -383,7 +381,7 @@ static function ($handle) use ($rebaseableCommands): void { if ( $command->rebaseErrorHandlingStrategy === RebaseErrorHandlingStrategy::STRATEGY_FAIL - && $commandSimulator->hasCommandsThatFailed() + && $commandSimulator->hasConflicts() ) { yield $this->reopenContentStream( $workspace->currentContentStreamId, @@ -391,7 +389,7 @@ static function ($handle) use ($rebaseableCommands): void { ); // throw an exception that contains all the information about what exactly failed - throw WorkspaceRebaseFailed::duringRebase($commandSimulator->getCommandsThatFailed()); + throw WorkspaceRebaseFailed::duringRebase($commandSimulator->getConflictingEvents()); } // if we got so far without an exception (or if we don't care), we can switch the workspace's active content stream. @@ -506,13 +504,13 @@ static function ($handle) use ($commandSimulator, $matchingCommands, $remainingC } ); - if ($commandSimulator->hasCommandsThatFailed()) { + if ($commandSimulator->hasConflicts()) { yield $this->reopenContentStream( $workspace->currentContentStreamId, $commandHandlingDependencies ); - throw WorkspaceRebaseFailed::duringPublish($commandSimulator->getCommandsThatFailed()); + throw WorkspaceRebaseFailed::duringPublish($commandSimulator->getConflictingEvents()); } // this could be a no-op for the rare case when a command returns empty events e.g. the node was already tagged with this subtree tag, meaning we actually just rebase @@ -624,12 +622,12 @@ static function ($handle) use ($commandsToKeep): void { } ); - if ($commandSimulator->hasCommandsThatFailed()) { + if ($commandSimulator->hasConflicts()) { yield $this->reopenContentStream( $workspace->currentContentStreamId, $commandHandlingDependencies ); - throw WorkspaceRebaseFailed::duringDiscard($commandSimulator->getCommandsThatFailed()); + throw WorkspaceRebaseFailed::duringDiscard($commandSimulator->getConflictingEvents()); } yield from $this->forkNewContentStreamAndApplyEvents( diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandThatFailedDuringRebase.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandThatFailedDuringRebase.php deleted file mode 100644 index bcfd9627256..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandThatFailedDuringRebase.php +++ /dev/null @@ -1,46 +0,0 @@ -sequenceNumber; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvent.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvent.php new file mode 100644 index 00000000000..21a0fe146e9 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvent.php @@ -0,0 +1,74 @@ +event instanceof EmbedsNodeAggregateId + ? $this->event->getNodeAggregateId() + : null; + } + + /** + * The exception for the conflict + */ + public function getException(): \Throwable + { + return $this->exception; + } + + /** + * The event store sequence number of the event containing the command to be rebased + * + * @internal exposed for testing + */ + public function getSequenceNumber(): SequenceNumber + { + return $this->sequenceNumber; + } + + /** + * The event that conflicts + * + * @internal exposed for testing and experimental use cases + */ + public function getEvent(): EventInterface + { + return $this->event; + } +} diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandsThatFailedDuringRebase.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvents.php similarity index 69% rename from Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandsThatFailedDuringRebase.php rename to Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvents.php index 24f1087ee81..1b30aa61007 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/CommandsThatFailedDuringRebase.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/ConflictingEvents.php @@ -15,28 +15,28 @@ namespace Neos\ContentRepository\Core\Feature\WorkspaceRebase; /** - * @implements \IteratorAggregate + * @implements \IteratorAggregate * * @api part of the exception exposed when rebasing failed */ -final readonly class CommandsThatFailedDuringRebase implements \IteratorAggregate, \Countable +final readonly class ConflictingEvents implements \IteratorAggregate, \Countable { /** - * @var array + * @var array */ private array $items; - public function __construct(CommandThatFailedDuringRebase ...$items) + public function __construct(ConflictingEvent ...$items) { $this->items = array_values($items); } - public function withAppended(CommandThatFailedDuringRebase $item): self + public function withAppended(ConflictingEvent $item): self { return new self(...[...$this->items, $item]); } - public function first(): ?CommandThatFailedDuringRebase + public function first(): ?ConflictingEvent { return $this->items[0] ?? null; } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/Exception/WorkspaceRebaseFailed.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/Exception/WorkspaceRebaseFailed.php index b1bae671156..499ea7a247c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/Exception/WorkspaceRebaseFailed.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceRebase/Exception/WorkspaceRebaseFailed.php @@ -14,7 +14,7 @@ namespace Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception; -use Neos\ContentRepository\Core\Feature\WorkspaceRebase\CommandsThatFailedDuringRebase; +use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvents; /** * @api this exception contains information about what exactly went wrong during rebase @@ -22,7 +22,7 @@ final class WorkspaceRebaseFailed extends \Exception { private function __construct( - public readonly CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase, + public readonly ConflictingEvents $conflictingEvents, string $message, int $code, ?\Throwable $previous, @@ -30,39 +30,39 @@ private function __construct( parent::__construct($message, $code, $previous); } - public static function duringRebase(CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase): self + public static function duringRebase(ConflictingEvents $conflictingEvents): self { return new self( - $commandsThatFailedDuringRebase, - sprintf('Rebase failed: %s', self::renderMessage($commandsThatFailedDuringRebase)), + $conflictingEvents, + sprintf('Rebase failed: %s', self::renderMessage($conflictingEvents)), 1729974936, - $commandsThatFailedDuringRebase->first()?->exception + $conflictingEvents->first()?->getException() ); } - public static function duringPublish(CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase): self + public static function duringPublish(ConflictingEvents $conflictingEvents): self { return new self( - $commandsThatFailedDuringRebase, - sprintf('Publication failed: %s', self::renderMessage($commandsThatFailedDuringRebase)), + $conflictingEvents, + sprintf('Publication failed: %s', self::renderMessage($conflictingEvents)), 1729974980, - $commandsThatFailedDuringRebase->first()?->exception + $conflictingEvents->first()?->getException() ); } - public static function duringDiscard(CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase): self + public static function duringDiscard(ConflictingEvents $conflictingEvents): self { return new self( - $commandsThatFailedDuringRebase, - sprintf('Discard failed: %s', self::renderMessage($commandsThatFailedDuringRebase)), + $conflictingEvents, + sprintf('Discard failed: %s', self::renderMessage($conflictingEvents)), 1729974982, - $commandsThatFailedDuringRebase->first()?->exception + $conflictingEvents->first()?->getException() ); } - private static function renderMessage(CommandsThatFailedDuringRebase $commandsThatFailedDuringRebase): string + private static function renderMessage(ConflictingEvents $conflictingEvents): string { - $firstFailure = $commandsThatFailedDuringRebase->first(); - return sprintf('"%s" and %d further failures', $firstFailure?->exception->getMessage(), count($commandsThatFailedDuringRebase) - 1); + $firstConflict = $conflictingEvents->first(); + return sprintf('"%s" and %d further conflicts', $firstConflict?->getException()->getMessage(), count($conflictingEvents) - 1); } } diff --git a/Neos.ContentRepository.NodeMigration/src/NodeMigrationServiceFactory.php b/Neos.ContentRepository.NodeMigration/src/NodeMigrationServiceFactory.php index f6c3f5fee33..7a293ea5ae0 100644 --- a/Neos.ContentRepository.NodeMigration/src/NodeMigrationServiceFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/NodeMigrationServiceFactory.php @@ -39,7 +39,7 @@ public function build(ContentRepositoryServiceFactoryDependencies $serviceFactor $filtersFactory->registerFilter('PropertyNotEmpty', new PropertyNotEmptyFilterFactory()); $filtersFactory->registerFilter('PropertyValue', new PropertyValueFilterFactory()); - $transformationsFactory = new TransformationsFactory($serviceFactoryDependencies->contentRepository); + $transformationsFactory = new TransformationsFactory($serviceFactoryDependencies->contentRepository, $serviceFactoryDependencies->propertyConverter); $transformationsFactory->registerTransformation('AddDimensionShineThrough', new AddDimensionShineThroughTransformationFactory()); $transformationsFactory->registerTransformation('AddNewProperty', new AddNewPropertyTransformationFactory()); $transformationsFactory->registerTransformation('ChangeNodeType', new ChangeNodeTypeTransformationFactory()); diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/AddDimensionShineThroughTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/AddDimensionShineThroughTransformationFactory.php index f8decab4901..ea774b966c1 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/AddDimensionShineThroughTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/AddDimensionShineThroughTransformationFactory.php @@ -17,6 +17,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Command\AddDimensionShineThrough; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -33,7 +34,8 @@ class AddDimensionShineThroughTransformationFactory implements TransformationFac */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { return new class ( DimensionSpacePoint::fromArray($settings['from']), diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/AddNewPropertyTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/AddNewPropertyTransformationFactory.php index b6ad21e946a..1a3dd9cdbc7 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/AddNewPropertyTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/AddNewPropertyTransformationFactory.php @@ -16,9 +16,11 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValue; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -31,13 +33,15 @@ class AddNewPropertyTransformationFactory implements TransformationFactoryInterf */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { return new class ( $settings['newPropertyName'], $settings['type'], $settings['serializedValue'], - $contentRepository + $contentRepository, + $propertyConverter, ) implements NodeBasedTransformationInterface { public function __construct( /** @@ -50,6 +54,7 @@ public function __construct( */ private readonly mixed $serializedValue, private readonly ContentRepository $contentRepository, + private readonly PropertyConverter $propertyConverter, ) { } @@ -63,20 +68,17 @@ public function execute( // we don't need to unset a non-existing property return; } + $deserializedPropertyValue = $this->propertyConverter->deserializePropertyValue(SerializedPropertyValue::create($this->serializedValue, $this->type)); // @phpstan-ignore neos.cr.internal if (!$node->hasProperty($this->newPropertyName)) { $this->contentRepository->handle( - SetSerializedNodeProperties::create( + SetNodeProperties::create( $workspaceNameForWriting, $node->aggregateId, $node->originDimensionSpacePoint, - SerializedPropertyValues::fromArray([ - $this->newPropertyName => SerializedPropertyValue::create( - $this->serializedValue, - $this->type - ) - ]), - PropertyNames::createEmpty() + PropertyValuesToWrite::fromArray([ + $this->newPropertyName => $deserializedPropertyValue, + ]) ) ); } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/ChangeNodeTypeTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/ChangeNodeTypeTransformationFactory.php index 42ec7df2b4c..d6f8a0bb328 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/ChangeNodeTypeTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/ChangeNodeTypeTransformationFactory.php @@ -17,6 +17,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType; use Neos\ContentRepository\Core\Feature\NodeTypeChange\Dto\NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -35,7 +36,8 @@ class ChangeNodeTypeTransformationFactory implements TransformationFactoryInterf */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { // by default, we won't delete anything. $nodeAggregateTypeChangeChildConstraintConflictResolutionStrategy diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/ChangePropertyValueTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/ChangePropertyValueTransformationFactory.php index c0b51852b6b..bd3d31c915b 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/ChangePropertyValueTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/ChangePropertyValueTransformationFactory.php @@ -16,9 +16,12 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValue; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -41,7 +44,8 @@ class ChangePropertyValueTransformationFactory implements TransformationFactoryI */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $newSerializedValue = '{current}'; if (isset($settings['newSerializedValue'])) { @@ -69,7 +73,8 @@ public function build( $search, $replace, $currentValuePlaceholder, - $contentRepository + $contentRepository, + $propertyConverter, ) implements NodeBasedTransformationInterface { public function __construct( /** @@ -96,7 +101,8 @@ public function __construct( * current property value into the new value. */ private readonly string $currentValuePlaceholder, - private readonly ContentRepository $contentRepository + private readonly ContentRepository $contentRepository, + private readonly PropertyConverter $propertyConverter, ) { } @@ -126,19 +132,16 @@ public function execute( $this->replace, $newValueWithReplacedCurrentValue ); + $deserializedPropertyValue = $this->propertyConverter->deserializePropertyValue(SerializedPropertyValue::create($newValueWithReplacedSearch, $currentProperty->type)); // @phpstan-ignore neos.cr.internal $this->contentRepository->handle( - SetSerializedNodeProperties::create( + SetNodeProperties::create( $workspaceNameForWriting, $node->aggregateId, $node->originDimensionSpacePoint, - SerializedPropertyValues::fromArray([ - $this->propertyName => SerializedPropertyValue::create( - $newValueWithReplacedSearch, - $currentProperty->type - ) - ]), - PropertyNames::createEmpty() + PropertyValuesToWrite::fromArray([ + $this->propertyName => $deserializedPropertyValue, + ]) ) ); } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/MoveDimensionSpacePointTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/MoveDimensionSpacePointTransformationFactory.php index 0b7cc1b7679..47699871c57 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/MoveDimensionSpacePointTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/MoveDimensionSpacePointTransformationFactory.php @@ -17,6 +17,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Command\MoveDimensionSpacePoint; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -29,7 +30,8 @@ class MoveDimensionSpacePointTransformationFactory implements TransformationFact */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $from = DimensionSpacePoint::fromArray($settings['from']); $to = DimensionSpacePoint::fromArray($settings['to']); diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/RemoveNodeTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/RemoveNodeTransformationFactory.php index fbd0a2b46fa..0dfca855a8e 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/RemoveNodeTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/RemoveNodeTransformationFactory.php @@ -18,6 +18,7 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -33,7 +34,8 @@ class RemoveNodeTransformationFactory implements TransformationFactoryInterface */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $strategy = null; if (isset($settings['strategy'])) { diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/RemovePropertyTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/RemovePropertyTransformationFactory.php index c0990547e2d..30e182e42bc 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/RemovePropertyTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/RemovePropertyTransformationFactory.php @@ -16,10 +16,10 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; -use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -33,7 +33,8 @@ class RemovePropertyTransformationFactory implements TransformationFactoryInterf */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $propertyName = $settings['property']; return new class ( @@ -56,12 +57,13 @@ public function execute( ): void { if ($node->hasProperty($this->propertyName)) { $this->contentRepository->handle( - SetSerializedNodeProperties::create( + SetNodeProperties::create( $workspaceNameForWriting, $node->aggregateId, $node->originDimensionSpacePoint, - SerializedPropertyValues::createEmpty(), - PropertyNames::fromArray([$this->propertyName]) + PropertyValuesToWrite::fromArray([ + $this->propertyName => null, + ]), ) ); } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/RenameNodeAggregateTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/RenameNodeAggregateTransformationFactory.php index 3e05d002d1b..cd277a27641 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/RenameNodeAggregateTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/RenameNodeAggregateTransformationFactory.php @@ -16,6 +16,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; @@ -28,7 +29,8 @@ class RenameNodeAggregateTransformationFactory implements TransformationFactoryI */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $newNodeName = $settings['newNodeName']; diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/RenamePropertyTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/RenamePropertyTransformationFactory.php index ff70a13bd7f..3d76ca3ba37 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/RenamePropertyTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/RenamePropertyTransformationFactory.php @@ -16,10 +16,10 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; -use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -33,7 +33,8 @@ class RenamePropertyTransformationFactory implements TransformationFactoryInterf */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { return new class ( @@ -62,20 +63,21 @@ public function execute( ContentStreamId $contentStreamForWriting ): void { - $serializedPropertyValue = $node->properties->serialized()->getProperty($this->from); - if ($serializedPropertyValue !== null) { - $this->contentRepository->handle( - SetSerializedNodeProperties::create( - $workspaceNameForWriting, - $node->aggregateId, - $node->originDimensionSpacePoint, - SerializedPropertyValues::fromArray([ - $this->to => $serializedPropertyValue - ]), - PropertyNames::fromArray([$this->from]) - ) - ); + $propertyValue = $node->properties[$this->from]; + if ($propertyValue === null) { + return; } + $this->contentRepository->handle( + SetNodeProperties::create( + $workspaceNameForWriting, + $node->aggregateId, + $node->originDimensionSpacePoint, + PropertyValuesToWrite::fromArray([ + $this->to => $propertyValue, + $this->from => null, + ]), + ) + ); } }; } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/StripTagsOnPropertyTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/StripTagsOnPropertyTransformationFactory.php index 9803c9509b2..6c372de3c3c 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/StripTagsOnPropertyTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/StripTagsOnPropertyTransformationFactory.php @@ -16,11 +16,10 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; -use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; -use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValue; -use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; +use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties; +use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\SharedModel\Node\PropertyNames; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -34,7 +33,8 @@ class StripTagsOnPropertyTransformationFactory implements TransformationFactoryI */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { return new class ( $settings['property'], @@ -55,31 +55,27 @@ public function execute( WorkspaceName $workspaceNameForWriting, ContentStreamId $contentStreamForWriting ): void { - $serializedPropertyValue = $node->properties->serialized()->getProperty($this->propertyName); - if ($serializedPropertyValue !== null) { - $propertyValue = $serializedPropertyValue->value; - if (!is_string($propertyValue)) { - throw new \Exception( - 'StripTagsOnProperty can only be applied to properties of type string.', - 1645391885 - ); - } - $newValue = strip_tags($propertyValue); - $this->contentRepository->handle( - SetSerializedNodeProperties::create( - $workspaceNameForWriting, - $node->aggregateId, - $node->originDimensionSpacePoint, - SerializedPropertyValues::fromArray([ - $this->propertyName => SerializedPropertyValue::create( - $newValue, - $serializedPropertyValue->type - ) - ]), - PropertyNames::createEmpty() - ) + $propertyValue = $node->properties[$this->propertyName]; + if ($propertyValue === null) { + return; + } + if (!is_string($propertyValue)) { + throw new \Exception( + sprintf('StripTagsOnProperty can only be applied to properties of type string. Property "%s" is of type %s', $this->propertyName, get_debug_type($propertyValue)), + 1645391885 ); } + $newValue = strip_tags($propertyValue); + $this->contentRepository->handle( + SetNodeProperties::create( + $workspaceNameForWriting, + $node->aggregateId, + $node->originDimensionSpacePoint, + PropertyValuesToWrite::fromArray([ + $this->propertyName => $newValue, + ]), + ) + ); } }; } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationFactoryInterface.php b/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationFactoryInterface.php index df0c2193ed7..ecaa105d733 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationFactoryInterface.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationFactoryInterface.php @@ -5,6 +5,7 @@ namespace Neos\ContentRepository\NodeMigration\Transformation; use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; interface TransformationFactoryInterface { @@ -13,6 +14,7 @@ interface TransformationFactoryInterface */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface; } diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationsFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationsFactory.php index 915c05cc3fe..56d8d0eaa02 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationsFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/TransformationsFactory.php @@ -5,6 +5,7 @@ namespace Neos\ContentRepository\NodeMigration\Transformation; use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\NodeMigration\MigrationException; use Neos\ContentRepository\NodeMigration\NodeMigrationService; @@ -19,7 +20,8 @@ class TransformationsFactory private array $transformationFactories = []; public function __construct( - private readonly ContentRepository $contentRepository + private readonly ContentRepository $contentRepository, + private readonly PropertyConverter $propertyConverter, ) { } @@ -58,7 +60,7 @@ protected function buildTransformationObject( ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { $transformationFactory = $this->resolveTransformationFactory($transformationConfiguration['type']); - return $transformationFactory->build($transformationConfiguration['settings'] ?? [], $this->contentRepository); + return $transformationFactory->build($transformationConfiguration['settings'] ?? [], $this->contentRepository, $this->propertyConverter); } /** diff --git a/Neos.ContentRepository.NodeMigration/src/Transformation/UpdateRootNodeAggregateDimensionsTransformationFactory.php b/Neos.ContentRepository.NodeMigration/src/Transformation/UpdateRootNodeAggregateDimensionsTransformationFactory.php index 3b4b47867bb..4e735ae7ad3 100644 --- a/Neos.ContentRepository.NodeMigration/src/Transformation/UpdateRootNodeAggregateDimensionsTransformationFactory.php +++ b/Neos.ContentRepository.NodeMigration/src/Transformation/UpdateRootNodeAggregateDimensionsTransformationFactory.php @@ -6,6 +6,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\UpdateRootNodeAggregateDimensions; +use Neos\ContentRepository\Core\Infrastructure\Property\PropertyConverter; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\NodeMigration\MigrationException; @@ -17,7 +18,8 @@ class UpdateRootNodeAggregateDimensionsTransformationFactory implements Transfor */ public function build( array $settings, - ContentRepository $contentRepository + ContentRepository $contentRepository, + PropertyConverter $propertyConverter, ): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface { if (!isset($settings['nodeType'])) { throw new MigrationException( diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php index 8976f80caea..7e150e296e1 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/GenericCommandExecutionAndEventPublication.php @@ -331,11 +331,11 @@ public function theLastCommandShouldHaveThrownTheWorkspaceRebaseFailedWith(Table Assert::assertInstanceOf(WorkspaceRebaseFailed::class, $exception, sprintf('Actual exception: %s (%s): %s', get_class($exception), $exception->getCode(), $exception->getMessage())); $actualComparableHash = []; - foreach ($exception->commandsThatFailedDuringRebase as $commandsThatFailed) { + foreach ($exception->conflictingEvents as $conflictingEvent) { $actualComparableHash[] = [ - 'SequenceNumber' => (string)$commandsThatFailed->getSequenceNumber()->value, - 'Command' => (new \ReflectionClass($commandsThatFailed->command))->getShortName(), - 'Exception' => (new \ReflectionClass($commandsThatFailed->exception))->getShortName(), + 'SequenceNumber' => (string)$conflictingEvent->getSequenceNumber()->value, + 'Event' => (new \ReflectionClass($conflictingEvent->getEvent()))->getShortName(), + 'Exception' => (new \ReflectionClass($conflictingEvent->getException()))->getShortName(), ]; }