From e575933a07ef25ef36ab9b44ab73e8c853294ef1 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 29 Nov 2024 19:43:11 +0100 Subject: [PATCH 01/13] TASK: Remove possibility to specify dimensions in partial publish or delete --- .../01-ConstraintChecks.feature | 2 +- .../02-BasicFeatures.feature | 8 +-- .../01-ConstraintChecks.feature | 4 +- .../02-BasicFeatures.feature | 4 +- .../03-MoreBasicFeatures.feature | 12 ++-- .../04-AllFeaturePublication.feature | 14 ++--- ...PublishMovedNodesWithoutDimensions.feature | 8 +-- .../Classes/Feature/RebaseableCommands.php | 57 +++++++++++++------ .../DiscardIndividualNodesFromWorkspace.php | 12 ++-- .../PublishIndividualNodesFromWorkspace.php | 12 ++-- .../Event/WorkspaceWasPartiallyDiscarded.php | 16 +++++- .../Event/WorkspaceWasPartiallyPublished.php | 15 ++++- .../SharedModel/Node/NodeAggregateIds.php | 5 ++ .../CatchUpHook/AssetUsageCatchUpHook.php | 12 ++-- .../Domain/AssetUsageRepository.php | 19 +++++++ .../Service/AssetUsageIndexingService.php | 12 ++++ .../Service/WorkspacePublishingService.php | 13 ++--- ...desFromWorkspace_WithoutDimensions.feature | 4 +- ...lNodesFromWorkspace_WithDimensions.feature | 7 ++- ...desFromWorkspace_WithoutDimensions.feature | 8 +-- ...lNodesFromWorkspace_WithDimensions.feature | 19 +++---- .../Features/Bootstrap/AssetUsageTrait.php | 36 ++++++++++-- .../ContentCache/NodesInUserWorkspace.feature | 4 +- .../Security/WorkspacePermissions.feature | 4 +- .../FrontendRouting/PartialPublish.feature | 2 +- .../Controller/WorkspaceController.php | 26 +++------ 26 files changed, 210 insertions(+), 125 deletions(-) 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 bb15c251e28..3a73003673c 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 @@ -70,7 +70,7 @@ Feature: Workspace discarding - complex chained functionality When the command DiscardIndividualNodesFromWorkspace is executed with payload and exceptions are caught: | Key | Value | | workspaceName | "user-ws" | - | nodesToDiscard | [{"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToDiscard | ["sir-david-nodenborough", "sir-david-nodenborough"] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: | SequenceNumber | Event | Exception | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/02-BasicFeatures.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/02-BasicFeatures.feature index 94c2077c647..ba09c5aceb9 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/02-BasicFeatures.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W10-IndividualNodeDiscarding/02-BasicFeatures.feature @@ -96,7 +96,7 @@ Feature: Discard individual nodes (basics) When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToDiscard | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii"] | | newContentStreamId | "user-cs-identifier-new" | Then I expect the content stream "user-cs-identifier" to not exist @@ -126,7 +126,7 @@ Feature: Discard individual nodes (basics) When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToDiscard | [{"dimensionSpacePoint": {}, "nodeAggregateId": "non-existing-node"}, {"dimensionSpacePoint": {}, "nodeAggregateId": "sir-unchanged"}] | + | nodesToDiscard | ["non-existing-node", "sir-unchanged"] | | newContentStreamId | "user-cs-identifier-new-two" | # all nodes are still on the original user cs @@ -156,7 +156,7 @@ Feature: Discard individual nodes (basics) When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToDiscard | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "nody-mc-nodeface"}, {"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToDiscard | ["sir-david-nodenborough", "nody-mc-nodeface", "sir-nodeward-nodington-iii"] | | newContentStreamId | "user-cs-identifier-new" | # when discarding all nodes we expect a full discard via WorkspaceWasDiscarded @@ -186,7 +186,7 @@ Feature: Discard individual nodes (basics) When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToDiscard | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii"] | # live WS does not change because of a discard When I am in workspace "live" and dimension space point {} 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 60bf7aadc2b..02c9299bced 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 @@ -80,7 +80,7 @@ Feature: Workspace publication - complex chained functionality When the command PublishIndividualNodesFromWorkspace is executed with payload and exceptions are caught: | Key | Value | | workspaceName | "user-ws" | - | nodesToPublish | [{"dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-nodebelig"}] | + | nodesToPublish | ["sir-nodebelig"] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: | SequenceNumber | Event | Exception | @@ -104,7 +104,7 @@ Feature: Workspace publication - complex chained functionality When the command PublishIndividualNodesFromWorkspace is executed with payload and exceptions are caught: | Key | Value | | workspaceName | "user-ws" | - | nodesToPublish | [{"workspaceName": "user-ws", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToPublish | ["nody-mc-nodeface"] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: | SequenceNumber | Event | Exception | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/02-BasicFeatures.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/02-BasicFeatures.feature index dfa2d30a523..f9450d4ea03 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/02-BasicFeatures.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/02-BasicFeatures.feature @@ -54,7 +54,7 @@ Feature: Individual node publication | nody-mc-nodeface | Neos.ContentRepository.Testing:Content | $child2Id | nody | {} | When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | And I am in workspace "live" @@ -79,7 +79,7 @@ Feature: Individual node publication When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "non-existing"}] | + | nodesToPublish | ["non-existing"] | | contentStreamIdForRemainingPart | "user-cs-new" | Then workspaces user-test has status OUTDATED 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 68a71a14266..b1cca28db81 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 @@ -97,7 +97,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToPublish | ["sir-nodeward-nodington-iii"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then I expect the content stream "user-cs-identifier" to not exist @@ -142,7 +142,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "non-existing-node"}, {"dimensionSpacePoint": {}, "nodeAggregateId": "sir-unchanged"}] | + | nodesToPublish | ["non-existing-node", "sir-unchanged"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining-two" | When I am in workspace "live" and dimension space point {} @@ -198,7 +198,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload and exceptions are caught: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "sir-unchanged"}] | + | nodesToPublish | ["sir-unchanged"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then the last command should have thrown the WorkspaceRebaseFailed exception with: | SequenceNumber | Event | Exception | @@ -208,7 +208,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "nody-mc-nodeface"}, {"workspaceName": "user-test", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToPublish | ["sir-david-nodenborough", "nody-mc-nodeface", "sir-nodeward-nodington-iii"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | # when publishing all nodes we expect a full discard via WorkspaceWasPublished @@ -252,7 +252,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}, {"dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-nodeward-nodington-iii", "sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then I expect exactly 8 events to be published on stream "ContentStream:cs-identifier" @@ -291,7 +291,7 @@ Feature: Publishing individual nodes (basics) When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"dimensionSpacePoint": {}, "nodeAggregateId": "non-existing"}] | + | nodesToPublish | ["non-existing"] | | contentStreamIdForRemainingPart | "user-cs-new" | Then workspaces user-test has status OUTDATED diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/04-AllFeaturePublication.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/04-AllFeaturePublication.feature index 37de2e73450..761610c3ae4 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/04-AllFeaturePublication.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/04-AllFeaturePublication.feature @@ -100,7 +100,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "remaining-cs-id" | When I am in workspace "live" @@ -151,7 +151,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-modified" | When I am in workspace "live" and dimension space point {} @@ -191,7 +191,7 @@ Feature: Publishing hide/show scenario of nodes # When the command PublishIndividualNodesFromWorkspace is executed with payload: # | Key | Value | # | workspaceName | "user-test" | - # | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "contentStreamId": "user-cs-identifier", "dimensionSpacePoint": {}}] | + # | nodesToPublish | ["sir-david-nodenborough"] | #And the graph projection is fully up to date # When I am in workspace "live" and dimension space point {} @@ -232,7 +232,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-modified" | When I am in workspace "live" and dimension space point {} @@ -270,7 +270,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["sir-david-nodenborough"] | When I am in workspace "live" and dimension space point {} Then I expect node aggregate identifier "sir-david-nodenborough" to lead to no node @@ -307,7 +307,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "sir-david-nodenborough", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-modified" | When I am in workspace "live" and dimension space point {} @@ -366,7 +366,7 @@ Feature: Publishing hide/show scenario of nodes When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-test" | - | nodesToPublish | [{"nodeAggregateId": "new1-agg", "workspaceName": "user-test", "dimensionSpacePoint": {}}] | + | nodesToPublish | ["new1-agg"] | | contentStreamIdForRemainingPart | "user-cs-identifier-modified" | When I am in workspace "live" and dimension space point {} diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/05-PublishMovedNodesWithoutDimensions.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/05-PublishMovedNodesWithoutDimensions.feature index 35203d3d865..dc16fb4711d 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/05-PublishMovedNodesWithoutDimensions.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/W8-IndividualNodePublication/05-PublishMovedNodesWithoutDimensions.feature @@ -78,7 +78,7 @@ Feature: Publishing moved nodes without dimensions And the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user" | - | nodesToPublish | [{"workspaceName": "user", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | Then I expect the graph projection to consist of exactly 4 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph @@ -117,7 +117,7 @@ Feature: Publishing moved nodes without dimensions And the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user" | - | nodesToPublish | [{"workspaceName": "user", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToPublish | ["sir-nodeward-nodington-iii"] | Then I expect the graph projection to consist of exactly 4 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph @@ -166,7 +166,7 @@ Feature: Publishing moved nodes without dimensions And the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user" | - | nodesToPublish | [{"workspaceName": "user", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | Then I expect the graph projection to consist of exactly 5 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph @@ -210,7 +210,7 @@ Feature: Publishing moved nodes without dimensions And the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user" | - | nodesToPublish | [{"workspaceName": "user", "dimensionSpacePoint": {}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToPublish | ["nody-mc-nodeface"] | Then I expect the graph projection to consist of exactly 4 nodes And I expect a node identified by cs-identifier;lady-eleonode-rootford;{} to exist in the content graph diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index fd746e051a4..946255e7640 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -4,8 +4,21 @@ namespace Neos\ContentRepository\Core\Feature; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; +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; +use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; +use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName; +use Neos\ContentRepository\Core\Feature\NodeTypeChange\Command\ChangeNodeAggregateType; +use Neos\ContentRepository\Core\Feature\NodeVariation\Command\CreateNodeVariant; +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; /** @@ -41,20 +54,12 @@ public static function extractFromEventStream(EventStreamInterface $eventStream) * @return array{RebaseableCommands,RebaseableCommands} */ public function separateMatchingAndRemainingCommands( - NodeIdsToPublishOrDiscard $nodeIdsToPublishOrDiscard + NodeAggregateIds $nodeIdsToMatch ): array { $matchingCommands = []; $remainingCommands = []; foreach ($this->items as $extractedCommand) { - $originalCommand = $extractedCommand->originalCommand; - if (!$originalCommand instanceof MatchableWithNodeIdToPublishOrDiscardInterface) { - throw new \Exception( - 'Command class ' . get_class($originalCommand) . ' does not implement ' - . MatchableWithNodeIdToPublishOrDiscardInterface::class, - 1645393655 - ); - } - if (self::commandMatchesAtLeastOneNode($originalCommand, $nodeIdsToPublishOrDiscard)) { + if (self::commandMatchesAtLeastOneNode($extractedCommand->originalCommand, $nodeIdsToMatch)) { $matchingCommands[] = $extractedCommand; } else { $remainingCommands[] = $extractedCommand; @@ -67,11 +72,31 @@ public function separateMatchingAndRemainingCommands( } private static function commandMatchesAtLeastOneNode( - MatchableWithNodeIdToPublishOrDiscardInterface $command, - NodeIdsToPublishOrDiscard $nodeIds, + RebasableToOtherWorkspaceInterface $command, + NodeAggregateIds $nodeIdsToMatch, ): bool { - foreach ($nodeIds as $nodeId) { - if ($command->matchesNodeId($nodeId)) { + foreach ($nodeIdsToMatch as $nodeId) { + $matches = match($command::class) { + CreateNodeAggregateWithNodeAndSerializedProperties::class, + DisableNodeAggregate::class, + EnableNodeAggregate::class, + SetSerializedNodeProperties::class, + MoveNodeAggregate::class, + RemoveNodeAggregate::class, + ChangeNodeAggregateName::class, + ChangeNodeAggregateType::class, + CreateNodeVariant::class, + TagSubtree::class, + UntagSubtree::class + => $command->nodeAggregateId->equals($nodeId), + CopyNodesRecursively::class => $command->nodeAggregateIdMapping->getNewNodeAggregateId( + $command->nodeTreeToInsert->nodeAggregateId + )?->equals($nodeId), + SetSerializedNodeReferences::class => $command->sourceNodeAggregateId->equals($nodeId), + // todo return false instead to allow change to be still kept? + default => throw new \RuntimeException(sprintf('Command %s cannot be matched against a node aggregate id. Partial publish not possible.', $command::class), 1645393655) + }; + if ($matches) { return true; } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/DiscardIndividualNodesFromWorkspace.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/DiscardIndividualNodesFromWorkspace.php index 7f6a2915c3d..220a65aba96 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/DiscardIndividualNodesFromWorkspace.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/DiscardIndividualNodesFromWorkspace.php @@ -15,7 +15,7 @@ namespace Neos\ContentRepository\Core\Feature\WorkspacePublication\Command; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -28,23 +28,23 @@ { /** * @param WorkspaceName $workspaceName Name of the affected workspace - * @param NodeIdsToPublishOrDiscard $nodesToDiscard Ids of the nodes to be discarded + * @param NodeAggregateIds $nodesToDiscard Ids of the nodes to be discarded * @param ContentStreamId $newContentStreamId The id of the new content stream, that will contain the remaining changes which were not discarded */ private function __construct( public WorkspaceName $workspaceName, - public NodeIdsToPublishOrDiscard $nodesToDiscard, + public NodeAggregateIds $nodesToDiscard, public ContentStreamId $newContentStreamId ) { } /** * @param WorkspaceName $workspaceName Name of the affected workspace - * @param NodeIdsToPublishOrDiscard $nodesToDiscard Ids of the nodes to be discarded + * @param NodeAggregateIds $nodesToDiscard Ids of the nodes to be discarded */ public static function create( WorkspaceName $workspaceName, - NodeIdsToPublishOrDiscard $nodesToDiscard, + NodeAggregateIds $nodesToDiscard, ): self { return new self( $workspaceName, @@ -57,7 +57,7 @@ public static function fromArray(array $array): self { return new self( WorkspaceName::fromString($array['workspaceName']), - NodeIdsToPublishOrDiscard::fromArray($array['nodesToDiscard']), + NodeAggregateIds::fromArray($array['nodesToDiscard']), isset($array['newContentStreamId']) ? ContentStreamId::fromString($array['newContentStreamId']) : ContentStreamId::create(), ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/PublishIndividualNodesFromWorkspace.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/PublishIndividualNodesFromWorkspace.php index 7f9cf111dd8..77407e412b2 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/PublishIndividualNodesFromWorkspace.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Command/PublishIndividualNodesFromWorkspace.php @@ -15,7 +15,7 @@ namespace Neos\ContentRepository\Core\Feature\WorkspacePublication\Command; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -28,21 +28,21 @@ { /** * @param WorkspaceName $workspaceName Name of the affected workspace - * @param NodeIdsToPublishOrDiscard $nodesToPublish Ids of the nodes to publish or discard + * @param NodeAggregateIds $nodesToPublish Ids of the nodes to publish or discard * @param ContentStreamId $contentStreamIdForRemainingPart The id of the new content stream that will contain all remaining events {@see self::withContentStreamIdForRemainingPart()} */ private function __construct( public WorkspaceName $workspaceName, - public NodeIdsToPublishOrDiscard $nodesToPublish, + public NodeAggregateIds $nodesToPublish, public ContentStreamId $contentStreamIdForRemainingPart ) { } /** * @param WorkspaceName $workspaceName Name of the affected workspace - * @param NodeIdsToPublishOrDiscard $nodesToPublish Ids of the nodes to publish or discard + * @param NodeAggregateIds $nodesToPublish Ids of the nodes to publish or discard */ - public static function create(WorkspaceName $workspaceName, NodeIdsToPublishOrDiscard $nodesToPublish): self + public static function create(WorkspaceName $workspaceName, NodeAggregateIds $nodesToPublish): self { return new self( $workspaceName, @@ -55,7 +55,7 @@ public static function fromArray(array $array): self { return new self( WorkspaceName::fromString($array['workspaceName']), - NodeIdsToPublishOrDiscard::fromArray($array['nodesToPublish']), + NodeAggregateIds::fromArray($array['nodesToPublish']), isset($array['contentStreamIdForRemainingPart']) ? ContentStreamId::fromString($array['contentStreamIdForRemainingPart']) : ContentStreamId::create(), ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php index d9d88cddfac..f1b154658ec 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php @@ -16,7 +16,7 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -35,7 +35,7 @@ public function __construct( * The old content stream, which contains ALL the data (discarded and non-discarded) */ public ContentStreamId $previousContentStreamId, - public NodeIdsToPublishOrDiscard $discardedNodes, + public NodeAggregateIds $discardedNodes, ) { } @@ -46,11 +46,21 @@ public function getWorkspaceName(): WorkspaceName public static function fromArray(array $values): self { + $discardedNodes = []; + foreach ($values['discardedNodes'] as $discardedNode) { + if (is_array($discardedNode)) { + // legacy case: + $discardedNodes[] = $discardedNode['nodeAggregateId']; + continue; + } + $discardedNodes[] = $discardedNode; + } + return new self( WorkspaceName::fromString($values['workspaceName']), ContentStreamId::fromString($values['newContentStreamId']), ContentStreamId::fromString($values['previousContentStreamId']), - NodeIdsToPublishOrDiscard::fromArray($values['discardedNodes']), + NodeAggregateIds::fromArray($discardedNodes), ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php index f07ef11882d..38ae4f42b6e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php @@ -15,7 +15,7 @@ namespace Neos\ContentRepository\Core\Feature\WorkspacePublication\Event; use Neos\ContentRepository\Core\EventStore\EventInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -41,18 +41,27 @@ public function __construct( * The old content stream, which contains ALL the data (discarded and non-discarded) */ public ContentStreamId $previousSourceContentStreamId, - public NodeIdsToPublishOrDiscard $publishedNodes, + public NodeAggregateIds $publishedNodes, ) { } public static function fromArray(array $values): self { + $publishedNodes = []; + foreach ($values['publishedNodes'] as $publishedNode) { + if (is_array($publishedNode)) { + // legacy case: + $publishedNodes[] = $publishedNode['nodeAggregateId']; + continue; + } + $publishedNodes[] = $publishedNode; + } return new self( WorkspaceName::fromString($values['sourceWorkspaceName']), WorkspaceName::fromString($values['targetWorkspaceName']), ContentStreamId::fromString($values['newSourceContentStreamId']), ContentStreamId::fromString($values['previousSourceContentStreamId']), - NodeIdsToPublishOrDiscard::fromArray($values['publishedNodes']), + NodeAggregateIds::fromArray($publishedNodes), ); } diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAggregateIds.php b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAggregateIds.php index f0e30b17b67..3b400ac31ba 100644 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAggregateIds.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Node/NodeAggregateIds.php @@ -128,4 +128,9 @@ public function count(): int { return count($this->nodeAggregateIds); } + + public function isEmpty(): bool + { + return $this->nodeAggregateIds === []; + } } diff --git a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php index 59e2c733a99..63b7d2faaf5 100644 --- a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php +++ b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php @@ -27,6 +27,7 @@ use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\EventStore\Model\EventEnvelope; use Neos\Neos\AssetUsage\Service\AssetUsageIndexingService; @@ -145,18 +146,13 @@ private function discardWorkspace(WorkspaceName $workspaceName): void $this->assetUsageIndexingService->removeIndexForWorkspace($this->contentRepositoryId, $workspaceName); } - private function discardNodes(WorkspaceName $workspaceName, NodeIdsToPublishOrDiscard $nodeIds): void + private function discardNodes(WorkspaceName $workspaceName, NodeAggregateIds $nodeIds): void { foreach ($nodeIds as $nodeId) { - if (!$nodeId->dimensionSpacePoint) { - // NodeAggregateTypeWasChanged and NodeAggregateNameWasChanged don't impact asset usage - continue; - } - $this->assetUsageIndexingService->removeIndexForWorkspaceNameNodeAggregateIdAndDimensionSpacePoint( + $this->assetUsageIndexingService->removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( $this->contentRepositoryId, $workspaceName, - $nodeId->nodeAggregateId, - $nodeId->dimensionSpacePoint + $nodeId, ); } } diff --git a/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php b/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php index 23e13ffcec6..cca037ed94e 100644 --- a/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php +++ b/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php @@ -204,6 +204,25 @@ public function removeAssetUsagesOfWorkspaceWithAllProperties( ]); } + public function removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( + ContentRepositoryId $contentRepositoryId, + WorkspaceName $workspaceName, + NodeAggregateId $nodeAggregateId, + ): void { + $sql = <<getTableName()} + WHERE contentrepositoryid = :contentRepositoryId + AND workspacename = :workspaceName + AND nodeAggregateId = :nodeAggregateId + SQL; + + $this->dbal->executeStatement($sql, [ + 'contentRepositoryId' => $contentRepositoryId->value, + 'workspaceName' => $workspaceName->value, + 'nodeAggregateId' => $nodeAggregateId->value + ]); + } + /** * @param WorkspaceName[] $workspaceNames */ diff --git a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php index f284ccf34b0..52c01040c69 100644 --- a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php +++ b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php @@ -171,6 +171,18 @@ public function removeIndexForWorkspaceNameNodeAggregateIdAndDimensionSpacePoint ); } + public function removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( + ContentRepositoryId $contentRepositoryId, + WorkspaceName $workspaceName, + NodeAggregateId $nodeAggregateId + ): void { + $this->assetUsageRepository->removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( + $contentRepositoryId, + $workspaceName, + $nodeAggregateId + ); + } + public function removeIndexForWorkspace( ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName diff --git a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php index dea005af9e1..d6f68bbed16 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php @@ -266,7 +266,7 @@ public function changeBaseWorkspace(ContentRepositoryId $contentRepositoryId, Wo private function discardNodes( ContentRepository $contentRepository, WorkspaceName $workspaceName, - NodeIdsToPublishOrDiscard $nodeIdsToDiscard + NodeAggregateIds $nodeIdsToDiscard ): void { $contentRepository->handle( DiscardIndividualNodesFromWorkspace::create( @@ -283,7 +283,7 @@ private function discardNodes( private function publishNodes( ContentRepository $contentRepository, WorkspaceName $workspaceName, - NodeIdsToPublishOrDiscard $nodeIdsToPublish + NodeAggregateIds $nodeIdsToPublish ): void { $contentRepository->handle( PublishIndividualNodesFromWorkspace::create( @@ -341,7 +341,7 @@ private function resolveNodeIdsToPublishOrDiscard( WorkspaceName $workspaceName, NodeAggregateId $ancestorId, NodeTypeName $ancestorNodeTypeName - ): NodeIdsToPublishOrDiscard { + ): NodeAggregateIds { $nodeIdsToPublishOrDiscard = []; foreach ($this->pendingWorkspaceChangesInternal($contentRepository, $workspaceName) as $change) { if ( @@ -356,13 +356,10 @@ private function resolveNodeIdsToPublishOrDiscard( continue; } - $nodeIdsToPublishOrDiscard[] = new NodeIdToPublishOrDiscard( - $change->nodeAggregateId, - $change->originDimensionSpacePoint?->toDimensionSpacePoint() - ); + $nodeIdsToPublishOrDiscard[] = $change->nodeAggregateId; } - return NodeIdsToPublishOrDiscard::create(...$nodeIdsToPublishOrDiscard); + return NodeAggregateIds::create(...$nodeIdsToPublishOrDiscard); } private function pendingWorkspaceChangesInternal(ContentRepository $contentRepository, WorkspaceName $workspaceName): Changes diff --git a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature index 3d56189288c..e6c292625a1 100644 --- a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature @@ -63,7 +63,7 @@ Feature: Publish nodes partially without dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -111,7 +111,7 @@ Feature: Publish nodes partially without dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | And I expect the AssetUsageService to have the following AssetUsages: diff --git a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature index 932602a17dd..3873998b6d6 100644 --- a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature @@ -69,7 +69,7 @@ Feature: Publish nodes partially with dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -135,7 +135,7 @@ Feature: Publish nodes partially with dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -178,9 +178,10 @@ Feature: Publish nodes partially with dimensions | asset-2 | nody-mc-nodeface | assets | user-workspace | {"language": "de"} | | asset-3 | sir-nodeward-nodington-iii | text | user-workspace | {"language": "de"} | + # todo de en is ONE When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-identifier-remaining" | And I expect the AssetUsageService to have the following AssetUsages: diff --git a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature index 059182db7bb..44f5b37a389 100644 --- a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature @@ -68,7 +68,7 @@ Feature: Publish nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iiii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iiii"] | | newContentStreamId | "user-cs-identifier-new" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -105,7 +105,7 @@ Feature: Publish nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iiii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii", "sir-nodeward-nodington-iiii"] | | newContentStreamId | "user-cs-identifier-new" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -157,7 +157,7 @@ Feature: Publish nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iiii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iiii"] | | newContentStreamId | "user-cs-identifier-new" | And I expect the AssetUsageService to have the following AssetUsages: @@ -210,7 +210,7 @@ Feature: Publish nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iiii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii", "sir-nodeward-nodington-iiii"] | | newContentStreamId | "user-cs-identifier-new" | And I expect the AssetUsageService to have the following AssetUsages: diff --git a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature index e6b4bbb2628..53a941b172f 100644 --- a/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/AssetUsage/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature @@ -70,7 +70,7 @@ Feature: Publish nodes partially with dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToDiscard | ["sir-david-nodenborough", "nody-mc-nodeface"] | | newContentStreamId | "user-cs-identifier-new" | Then I expect the AssetUsageService to have the following AssetUsages: @@ -132,16 +132,16 @@ Feature: Publish nodes partially with dimensions | asset-2 | nody-mc-nodeface | assets | user-workspace | {"language": "gsw"} | | asset-3 | sir-nodeward-nodington-iii | text | user-workspace | {"language": "fr"} | + # todo check if test is usefull When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "gsw"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToDiscard | ["nody-mc-nodeface"] | | newContentStreamId | "user-cs-identifier-new" | Then I expect the AssetUsageService to have the following AssetUsages: | assetId | nodeAggregateId | propertyName | workspaceName | originDimensionSpacePoint | | asset-1 | sir-david-nodenborough | asset | user-workspace | {"language": "de"} | - | asset-2 | nody-mc-nodeface | assets | user-workspace | {"language": "de"} | | asset-3 | sir-nodeward-nodington-iii | text | user-workspace | {"language": "fr"} | Scenario: Discard nodes partially from user workspace with live base workspace with new generalization @@ -151,10 +151,6 @@ Feature: Publish nodes partially with dimensions | baseWorkspaceName | "live" | | newContentStreamId | "user-cs-id" | And I am in workspace "user-workspace" - And the command RebaseWorkspace is executed with payload: - | Key | Value | - | workspaceName | "user-workspace" | - Then I am in dimension space point {"language": "de"} And the following CreateNodeAggregateWithNode commands are executed: | nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues | @@ -176,13 +172,12 @@ Feature: Publish nodes partially with dimensions | asset-2 | nody-mc-nodeface | assets | user-workspace | {"language": "de"} | | asset-3 | sir-nodeward-nodington-iii | text | user-workspace | {"language": "de"} | + # todo differnt en de now simpler When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "en"} , "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"} , "nodeAggregateId": "sir-nodeward-nodington-iii"}] | - | newContentStreamId | "user-cs-identifier-new" | + | nodesToDiscard | ["sir-david-nodenborough", "nody-mc-nodeface", "sir-nodeward-nodington-iii"] | + | newContentStreamId | "user-cs-identifier-new" | And I expect the AssetUsageService to have the following AssetUsages: - | assetId | nodeAggregateId | propertyName | workspaceName | originDimensionSpacePoint | - | asset-1 | sir-david-nodenborough | asset | user-workspace | {"language": "de"} | - | asset-2 | nody-mc-nodeface | assets | user-workspace | {"language": "de"} | \ No newline at end of file + | assetId | nodeAggregateId | propertyName | workspaceName | originDimensionSpacePoint | diff --git a/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php b/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php index 53d5e61c49f..04ebd587f63 100644 --- a/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php +++ b/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php @@ -42,7 +42,7 @@ abstract private function getObject(string $className): object; public function iExpectTheAssetUsageServiceToHaveTheFollowingAssetUsages(TableNode $table) { $assetUsageService = $this->getObject(AssetUsageService::class); - $assetUsages = $assetUsageService->findByFilter($this->currentContentRepository->id, AssetUsageFilter::create()); + $assetUsages = iterator_to_array($assetUsageService->findByFilter($this->currentContentRepository->id, AssetUsageFilter::create())); $tableRows = $table->getHash(); foreach ($assetUsages as $assetUsage) { @@ -60,9 +60,37 @@ public function iExpectTheAssetUsageServiceToHaveTheFollowingAssetUsages(TableNo } } - Assert::assertEmpty($tableRows, "Not all given asset usages where found."); - Assert::assertSame($assetUsages->count(), count($table->getHash()), "More asset usages found as given."); + // echo json_encode($tableRows, JSON_PRETTY_PRINT); + // echo json_encode($assetUsages, JSON_PRETTY_PRINT); + Assert::assertEmpty($tableRows, "Not all given asset usages where found: " . json_encode($tableRows, JSON_PRETTY_PRINT)); + Assert::assertSame(count($assetUsages), count($table->getHash()), "More asset usages found as given."); + + } + + public function fewfw(TableNode $table) + { + $assetUsageService = $this->getObject(AssetUsageService::class); + $assetUsages = $assetUsageService->findByFilter($this->currentContentRepository->id, AssetUsageFilter::create()); + + $actual = []; + foreach ($assetUsages as $assetUsage) { + $actual[] = [ + 'assetId' => $assetUsage->assetId, + 'propertyName' => $assetUsage->propertyName, + 'workspaceName' => $assetUsage->workspaceName->value, + 'nodeAggregateId' => $assetUsage->nodeAggregateId->value, + 'originDimensionSpacePoint' => str_replace('":"', '": "', $assetUsage->originDimensionSpacePoint->toJson()), + ]; + } + + $expected = $table->getHash(); + + $sorter = fn ($a, $b) => $a <=> $b; + + usort($expected, $sorter); + usort($actual, $sorter); + Assert::assertSame($expected, $actual, "Not all given asset usages where found."); } /** @@ -75,4 +103,4 @@ public function iRunTheAssetUsageIndexingProcessor(string $rootNodeTypeName) NodeTypeName::fromString($rootNodeTypeName), ); } -} \ No newline at end of file +} diff --git a/Neos.Neos/Tests/Behavior/Features/ContentCache/NodesInUserWorkspace.feature b/Neos.Neos/Tests/Behavior/Features/ContentCache/NodesInUserWorkspace.feature index 014162ccebf..d52b54f4ca8 100644 --- a/Neos.Neos/Tests/Behavior/Features/ContentCache/NodesInUserWorkspace.feature +++ b/Neos.Neos/Tests/Behavior/Features/ContentCache/NodesInUserWorkspace.feature @@ -179,7 +179,7 @@ Feature: Tests for the ContentCacheFlusher and cache flushing when applied in us When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-editor" | - | nodesToDiscard | [{"workspaceName": "user-editor", "dimensionSpacePoint": {}, "nodeAggregateId": "text-node-middle"}] | + | nodesToDiscard | ["text-node-middle"] | | newContentStreamId | "user-cs-id-discard" | Then I expect node aggregate identifier "text-node-middle" to lead to no node @@ -295,4 +295,4 @@ Feature: Tests for the ContentCacheFlusher and cache flushing when applied in us """
secondRender[Text Node at the start of the document]secondRender[Text Node in the middle of the document]secondRender[Text Node at the end of the document]
""" - + diff --git a/Neos.Neos/Tests/Behavior/Features/ContentRepository/Security/WorkspacePermissions.feature b/Neos.Neos/Tests/Behavior/Features/ContentRepository/Security/WorkspacePermissions.feature index 7b02cca647b..c039224874b 100644 --- a/Neos.Neos/Tests/Behavior/Features/ContentRepository/Security/WorkspacePermissions.feature +++ b/Neos.Neos/Tests/Behavior/Features/ContentRepository/Security/WorkspacePermissions.feature @@ -208,9 +208,9 @@ Feature: Workspace permission related features | MoveDimensionSpacePoint | {"source":{"language":"de"},"target":{"language":"ch"}} | | UpdateRootNodeAggregateDimensions | {"nodeAggregateId":"root"} | | DiscardWorkspace | {} | - | DiscardIndividualNodesFromWorkspace | {"nodesToDiscard":[{"nodeAggregateId":"a1"}]} | + | DiscardIndividualNodesFromWorkspace | {"nodesToDiscard":["a1"]} | | PublishWorkspace | {} | - | PublishIndividualNodesFromWorkspace | {"nodesToPublish":[{"nodeAggregateId":"a1"}]} | + | PublishIndividualNodesFromWorkspace | {"nodesToPublish":["a1"]} | | RebaseWorkspace | {} | | CreateWorkspace | {"workspaceName":"new-workspace","baseWorkspaceName":"workspace","newContentStreamId":"any"} | diff --git a/Neos.Neos/Tests/Behavior/Features/FrontendRouting/PartialPublish.feature b/Neos.Neos/Tests/Behavior/Features/FrontendRouting/PartialPublish.feature index 6c82bbeacbb..803b0e8b5eb 100644 --- a/Neos.Neos/Tests/Behavior/Features/FrontendRouting/PartialPublish.feature +++ b/Neos.Neos/Tests/Behavior/Features/FrontendRouting/PartialPublish.feature @@ -54,7 +54,7 @@ Feature: Test cases for partial publish to live and uri path generation And the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "myworkspace" | - | nodesToPublish | [{"nodeAggregateId": "justsomepage", "dimensionSpacePoint": {"example":"source"}}] | + | nodesToPublish | ["justsomepage"] | Then I expect the documenturipath table to contain exactly: # source: 65901ded4f068dac14ad0dce4f459b29 diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index e8f02988acb..8718f28aedb 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -21,14 +21,13 @@ use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindAncestorNodesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; @@ -490,12 +489,7 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo $command = PublishIndividualNodesFromWorkspace::create( $selectedWorkspace, - NodeIdsToPublishOrDiscard::create( - new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ) - ), + NodeAggregateIds::create($nodeAddress->aggregateId), ); $contentRepository->handle($command); @@ -524,11 +518,8 @@ public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWo $command = DiscardIndividualNodesFromWorkspace::create( $selectedWorkspace, - NodeIdsToPublishOrDiscard::create( - new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ) + NodeAggregateIds::create( + $nodeAddress->aggregateId ), ); $contentRepository->handle($command); @@ -560,17 +551,14 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string $nodesToPublishOrDiscard = []; foreach ($nodes as $node) { $nodeAddress = NodeAddress::fromJsonString($node); - $nodesToPublishOrDiscard[] = new NodeIdToPublishOrDiscard( - $nodeAddress->aggregateId, - $nodeAddress->dimensionSpacePoint - ); + $nodesToPublishOrDiscard[] = $nodeAddress->aggregateId; } switch ($action) { case 'publish': $command = PublishIndividualNodesFromWorkspace::create( $selectedWorkspaceName, - NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), + NodeAggregateIds::create(...$nodesToPublishOrDiscard), ); $contentRepository->handle($command); $this->addFlashMessage($this->translator->translateById( @@ -585,7 +573,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string case 'discard': $command = DiscardIndividualNodesFromWorkspace::create( $selectedWorkspaceName, - NodeIdsToPublishOrDiscard::create(...$nodesToPublishOrDiscard), + NodeAggregateIds::create(...$nodesToPublishOrDiscard), ); $contentRepository->handle($command); $this->addFlashMessage($this->translator->translateById( From d78b1ddc004657a81336ee30f1df3f1d578f9320 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:33:09 +0100 Subject: [PATCH 02/13] TASK: Remove obsolete `MatchableWithNodeIdToPublishOrDiscardInterface` the logic is carried out inside `RebaseableCommands::commandMatchesAtLeastOneNode` The reasoning behind this is that asking the commands which node aggregate id is affected _does_ work but the capabilities stop if we want to ask which dimensions are affected. For that we must use the event instead - at least in the future if we want to reintroduce dimension based publishing again. The matching list from commands to node id will also be simplified once we use the `EmbedsNodeAggregateId` interface instead and operate on events. --- ...eWithNodeIdToPublishOrDiscardInterface.php | 30 ------------------- ...gregateWithNodeAndSerializedProperties.php | 11 ------- .../Command/DisableNodeAggregate.php | 12 -------- .../Command/EnableNodeAggregate.php | 12 -------- .../Command/CopyNodesRecursively.php | 15 ---------- .../Command/SetSerializedNodeProperties.php | 11 ------- .../NodeMove/Command/MoveNodeAggregate.php | 10 ------- .../Command/SetSerializedNodeReferences.php | 10 ------- .../Command/RemoveNodeAggregate.php | 11 ------- .../Command/ChangeNodeAggregateName.php | 8 ----- .../Command/ChangeNodeAggregateType.php | 9 ------ .../Command/CreateNodeVariant.php | 9 ------ .../Classes/Feature/RebaseableCommands.php | 6 ++++ .../SubtreeTagging/Command/TagSubtree.php | 11 +------ .../SubtreeTagging/Command/UntagSubtree.php | 11 +------ 15 files changed, 8 insertions(+), 168 deletions(-) delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/Common/MatchableWithNodeIdToPublishOrDiscardInterface.php diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/MatchableWithNodeIdToPublishOrDiscardInterface.php b/Neos.ContentRepository.Core/Classes/Feature/Common/MatchableWithNodeIdToPublishOrDiscardInterface.php deleted file mode 100644 index edde6697d10..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/MatchableWithNodeIdToPublishOrDiscardInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $nodeIdToPublish->dimensionSpacePoint?->equals($this->originDimensionSpacePoint) - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/DisableNodeAggregate.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/DisableNodeAggregate.php index 90c725e6a69..9b2bd395bd1 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/DisableNodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/DisableNodeAggregate.php @@ -16,12 +16,9 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -32,7 +29,6 @@ final readonly class DisableNodeAggregate implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -78,14 +74,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return ( - $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint - && $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/EnableNodeAggregate.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/EnableNodeAggregate.php index 37c8407b0c7..25d1919960e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/EnableNodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDisabling/Command/EnableNodeAggregate.php @@ -16,12 +16,9 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -32,7 +29,6 @@ final readonly class EnableNodeAggregate implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -78,14 +74,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return ( - $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint - && $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php index e3219fa695a..ef175733c40 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeDuplication/Command/CopyNodesRecursively.php @@ -15,10 +15,8 @@ namespace Neos\ContentRepository\Core\Feature\NodeDuplication\Command; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\NodeDuplication\Dto\NodeSubtreeSnapshot; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; @@ -38,7 +36,6 @@ */ final readonly class CopyNodesRecursively implements \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -121,18 +118,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - $targetNodeAggregateId = $this->nodeAggregateIdMapping->getNewNodeAggregateId( - $this->nodeTreeToInsert->nodeAggregateId - ); - return ( - !is_null($targetNodeAggregateId) - && $nodeIdToPublish->dimensionSpacePoint?->equals($this->targetDimensionSpacePoint) - && $targetNodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - ); - } - public function withNodeAggregateIdMapping( NodeAggregateIdMapping $nodeAggregateIdMapping ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php index c3906bae03f..9420f3a4069 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeModification/Command/SetSerializedNodeProperties.php @@ -15,10 +15,8 @@ namespace Neos\ContentRepository\Core\Feature\NodeModification\Command; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\NodeModification\Dto\SerializedPropertyValues; -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\WorkspaceName; @@ -32,7 +30,6 @@ */ final readonly class SetSerializedNodeProperties implements \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -94,14 +91,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return ( - $nodeIdToPublish->dimensionSpacePoint?->equals($this->originDimensionSpacePoint) - && $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/Command/MoveNodeAggregate.php b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/Command/MoveNodeAggregate.php index 054316a2022..c7c60eebba9 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeMove/Command/MoveNodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeMove/Command/MoveNodeAggregate.php @@ -16,12 +16,9 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\NodeMove\Dto\RelationDistributionStrategy; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -45,7 +42,6 @@ final readonly class MoveNodeAggregate implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -111,12 +107,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $this->dimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint; - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php index 132f1299ed7..42f3d5b5a95 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeReferencing/Command/SetSerializedNodeReferences.php @@ -15,10 +15,8 @@ namespace Neos\ContentRepository\Core\Feature\NodeReferencing\Command; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\NodeReferencing\Dto\SerializedNodeReferences; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -31,7 +29,6 @@ */ final readonly class SetSerializedNodeReferences implements \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -78,13 +75,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return ($nodeIdToPublish->dimensionSpacePoint?->equals($this->sourceOriginDimensionSpacePoint) - && $this->sourceNodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php index 92292bd34da..ad4948d6a49 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRemoval/Command/RemoveNodeAggregate.php @@ -16,9 +16,7 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -29,7 +27,6 @@ final readonly class RemoveNodeAggregate implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -97,14 +94,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return ( - $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint - ); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php index d19d6210a48..027f7294be2 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Command/ChangeNodeAggregateName.php @@ -15,9 +15,7 @@ namespace Neos\ContentRepository\Core\Feature\NodeRenaming\Command; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -34,7 +32,6 @@ final readonly class ChangeNodeAggregateName implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -76,11 +73,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Command/ChangeNodeAggregateType.php b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Command/ChangeNodeAggregateType.php index 4a13da56621..736802a3e6c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Command/ChangeNodeAggregateType.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Command/ChangeNodeAggregateType.php @@ -15,14 +15,11 @@ namespace Neos\ContentRepository\Core\Feature\NodeTypeChange\Command; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\NodeCreation\Dto\NodeAggregateIdsByNodePaths; use Neos\ContentRepository\Core\Feature\NodeTypeChange\Dto\NodeAggregateTypeChangeChildConstraintConflictResolutionStrategy; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; /** @@ -31,7 +28,6 @@ final readonly class ChangeNodeAggregateType implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -74,11 +70,6 @@ public static function fromArray(array $array): self ); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId); - } - /** * @return array */ diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php index 2064e99e0b2..581bfd8b6d8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeVariation/Command/CreateNodeVariant.php @@ -16,9 +16,7 @@ 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; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -32,7 +30,6 @@ final readonly class CreateNodeVariant implements CommandInterface, \JsonSerializable, - MatchableWithNodeIdToPublishOrDiscardInterface, RebasableToOtherWorkspaceInterface { /** @@ -78,12 +75,6 @@ public function jsonSerialize(): array return get_object_vars($this); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $nodeIdToPublish->dimensionSpacePoint?->equals($this->targetOrigin); - } - public function createCopyForWorkspace( WorkspaceName $targetWorkspaceName, ): self { diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index 946255e7640..3c63542f569 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -76,6 +76,12 @@ private static function commandMatchesAtLeastOneNode( NodeAggregateIds $nodeIdsToMatch, ): bool { foreach ($nodeIdsToMatch as $nodeId) { + /** + * This match must contain all commands which are working with individual nodes, such that they are + * filterable whether they are applying their action to a NodeIdToPublish. + * + * This is needed to publish and discard individual nodes. + */ $matches = match($command::class) { CreateNodeAggregateWithNodeAndSerializedProperties::class, DisableNodeAggregate::class, diff --git a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/TagSubtree.php b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/TagSubtree.php index 824bb402ca9..0c1adaa6ab6 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/TagSubtree.php +++ b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/TagSubtree.php @@ -16,10 +16,8 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -32,8 +30,7 @@ final readonly class TagSubtree implements CommandInterface, \JsonSerializable, - RebasableToOtherWorkspaceInterface, - MatchableWithNodeIdToPublishOrDiscardInterface + RebasableToOtherWorkspaceInterface { /** * @param WorkspaceName $workspaceName The workspace in which the tagging operation is to be performed @@ -85,12 +82,6 @@ public function createCopyForWorkspace(WorkspaceName $targetWorkspaceName): self ); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $nodeIdToPublish->dimensionSpacePoint === $this->coveredDimensionSpacePoint; - } - /** * @return array */ diff --git a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/UntagSubtree.php b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/UntagSubtree.php index 02bea45d58b..d0d49a47d5b 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/UntagSubtree.php +++ b/Neos.ContentRepository.Core/Classes/Feature/SubtreeTagging/Command/UntagSubtree.php @@ -16,10 +16,8 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface; use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -33,8 +31,7 @@ final readonly class UntagSubtree implements CommandInterface, \JsonSerializable, - RebasableToOtherWorkspaceInterface, - MatchableWithNodeIdToPublishOrDiscardInterface + RebasableToOtherWorkspaceInterface { /** * @param WorkspaceName $workspaceName The workspace in which the remove tag operation is to be performed @@ -86,12 +83,6 @@ public function createCopyForWorkspace(WorkspaceName $targetWorkspaceName): self ); } - public function matchesNodeId(NodeIdToPublishOrDiscard $nodeIdToPublish): bool - { - return $this->nodeAggregateId->equals($nodeIdToPublish->nodeAggregateId) - && $this->coveredDimensionSpacePoint === $nodeIdToPublish->dimensionSpacePoint; - } - /** * @return array */ From 1b6ab9dd96b5db199b3dddbd864f40045e323fdc Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:45:29 +0100 Subject: [PATCH 03/13] TASK: Improve asset usage test match error --- .../Features/Bootstrap/AssetUsageTrait.php | 35 +++---------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php b/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php index 04ebd587f63..d798c5cae98 100644 --- a/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php +++ b/Neos.Neos/Tests/Behavior/Features/Bootstrap/AssetUsageTrait.php @@ -60,37 +60,10 @@ public function iExpectTheAssetUsageServiceToHaveTheFollowingAssetUsages(TableNo } } - // echo json_encode($tableRows, JSON_PRETTY_PRINT); - // echo json_encode($assetUsages, JSON_PRETTY_PRINT); - Assert::assertEmpty($tableRows, "Not all given asset usages where found: " . json_encode($tableRows, JSON_PRETTY_PRINT)); - Assert::assertSame(count($assetUsages), count($table->getHash()), "More asset usages found as given."); - - } - - public function fewfw(TableNode $table) - { - $assetUsageService = $this->getObject(AssetUsageService::class); - $assetUsages = $assetUsageService->findByFilter($this->currentContentRepository->id, AssetUsageFilter::create()); - - $actual = []; - foreach ($assetUsages as $assetUsage) { - $actual[] = [ - 'assetId' => $assetUsage->assetId, - 'propertyName' => $assetUsage->propertyName, - 'workspaceName' => $assetUsage->workspaceName->value, - 'nodeAggregateId' => $assetUsage->nodeAggregateId->value, - 'originDimensionSpacePoint' => str_replace('":"', '": "', $assetUsage->originDimensionSpacePoint->toJson()), - ]; - } - - $expected = $table->getHash(); - - $sorter = fn ($a, $b) => $a <=> $b; - - usort($expected, $sorter); - usort($actual, $sorter); - - Assert::assertSame($expected, $actual, "Not all given asset usages where found."); + Assert::assertTrue( + $tableRows === [] && count($assetUsages) === count($table->getHash()), + sprintf('Mismatch between all actual asset usages %s and leftover asset usages to match %s', json_encode($assetUsages, JSON_PRETTY_PRINT), json_encode($tableRows, JSON_PRETTY_PRINT)) + ); } /** From 16da882e7925c8807cd0f385dabdbb455e399856 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 30 Nov 2024 20:48:44 +0100 Subject: [PATCH 04/13] TASK: Revert catchup hook adjustment to rely on `NodeAggregateIds` instead of `NodeIdsToPublishOrDiscard` ... as a 3rd option the followup commit will just flush the full asset usage workspace! --- .../CatchUpHook/AssetUsageCatchUpHook.php | 13 ++++++++----- .../Domain/AssetUsageRepository.php | 19 ------------------- .../Service/AssetUsageIndexingService.php | 12 ------------ 3 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php index 63b7d2faaf5..2cee2a58c01 100644 --- a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php +++ b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php @@ -7,7 +7,6 @@ use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\EventStore\EventInterface; -use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamId; use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; @@ -27,7 +26,6 @@ use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\EventStore\Model\EventEnvelope; use Neos\Neos\AssetUsage\Service\AssetUsageIndexingService; @@ -146,13 +144,18 @@ private function discardWorkspace(WorkspaceName $workspaceName): void $this->assetUsageIndexingService->removeIndexForWorkspace($this->contentRepositoryId, $workspaceName); } - private function discardNodes(WorkspaceName $workspaceName, NodeAggregateIds $nodeIds): void + private function discardNodes(WorkspaceName $workspaceName, NodeIdsToPublishOrDiscard $nodeIds): void { foreach ($nodeIds as $nodeId) { - $this->assetUsageIndexingService->removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( + if (!$nodeId->dimensionSpacePoint) { + // NodeAggregateTypeWasChanged and NodeAggregateNameWasChanged don't impact asset usage + continue; + } + $this->assetUsageIndexingService->removeIndexForWorkspaceNameNodeAggregateIdAndDimensionSpacePoint( $this->contentRepositoryId, $workspaceName, - $nodeId, + $nodeId->nodeAggregateId, + $nodeId->dimensionSpacePoint ); } } diff --git a/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php b/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php index cca037ed94e..23e13ffcec6 100644 --- a/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php +++ b/Neos.Neos/Classes/AssetUsage/Domain/AssetUsageRepository.php @@ -204,25 +204,6 @@ public function removeAssetUsagesOfWorkspaceWithAllProperties( ]); } - public function removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( - ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, - NodeAggregateId $nodeAggregateId, - ): void { - $sql = <<getTableName()} - WHERE contentrepositoryid = :contentRepositoryId - AND workspacename = :workspaceName - AND nodeAggregateId = :nodeAggregateId - SQL; - - $this->dbal->executeStatement($sql, [ - 'contentRepositoryId' => $contentRepositoryId->value, - 'workspaceName' => $workspaceName->value, - 'nodeAggregateId' => $nodeAggregateId->value - ]); - } - /** * @param WorkspaceName[] $workspaceNames */ diff --git a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php index 52c01040c69..f284ccf34b0 100644 --- a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php +++ b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageIndexingService.php @@ -171,18 +171,6 @@ public function removeIndexForWorkspaceNameNodeAggregateIdAndDimensionSpacePoint ); } - public function removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( - ContentRepositoryId $contentRepositoryId, - WorkspaceName $workspaceName, - NodeAggregateId $nodeAggregateId - ): void { - $this->assetUsageRepository->removeAssetUsagesOfWorkspaceWithAllPropertiesInAllDimensions( - $contentRepositoryId, - $workspaceName, - $nodeAggregateId - ); - } - public function removeIndexForWorkspace( ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName From ac2c35993dbe077e4ef3021178cf55b6d918e77c Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:03:03 +0100 Subject: [PATCH 05/13] TASK: Remove information which nodes were partially published or discarded in the events (for now) Introduced with https://github.com/neos/neos-development-collection/commit/f2c66e08c92be23ad0b860547c5d3c1b46210a66 - its not important for the graph projection and only used by the asset usage catchup hook which can be adjusted. If we want to re-introduce `$publishedNodes` to the event should be also careful that we calculate them from the _actual_ events so we can know for sure there is no gibberish in the event. Previously for example discarding a non-existing node or a node in a not valid DSP would just save that directly into the event, which is not correct as that did not happen like that. It was merely a bad intention. --- .../Feature/WorkspaceCommandHandler.php | 2 - .../Dto/NodeIdToPublishOrDiscard.php | 58 -------------- .../Dto/NodeIdsToPublishOrDiscard.php | 76 ------------------- .../Event/WorkspaceWasPartiallyDiscarded.php | 17 +---- .../Event/WorkspaceWasPartiallyPublished.php | 12 --- .../Service/WorkspacePublishingService.php | 2 - 6 files changed, 2 insertions(+), 165 deletions(-) delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdToPublishOrDiscard.php delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdsToPublishOrDiscard.php diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php index a29b7777544..8af4e023a74 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php @@ -544,7 +544,6 @@ static function ($handle) use ($commandSimulator, $matchingCommands, $remainingC $baseWorkspace->workspaceName, $command->contentStreamIdForRemainingPart, $workspace->currentContentStreamId, - $command->nodesToPublish ) ]), ExpectedVersion::ANY() @@ -644,7 +643,6 @@ static function ($handle) use ($commandsToKeep): void { $command->workspaceName, $command->newContentStreamId, $workspace->currentContentStreamId, - $command->nodesToDiscard, ) ), ExpectedVersion::ANY() diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdToPublishOrDiscard.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdToPublishOrDiscard.php deleted file mode 100644 index b2b5c346be7..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdToPublishOrDiscard.php +++ /dev/null @@ -1,58 +0,0 @@ - $array - */ - public static function fromArray(array $array): self - { - return new self( - NodeAggregateId::fromString($array['nodeAggregateId']), - is_array($array['dimensionSpacePoint'] ?? null) - ? DimensionSpacePoint::fromArray($array['dimensionSpacePoint']) - : null, - ); - } - - /** - * @return array - */ - public function jsonSerialize(): array - { - return get_object_vars($this); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdsToPublishOrDiscard.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdsToPublishOrDiscard.php deleted file mode 100644 index 53aa7a73363..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Dto/NodeIdsToPublishOrDiscard.php +++ /dev/null @@ -1,76 +0,0 @@ - - * @api used as part of commands - */ -final readonly class NodeIdsToPublishOrDiscard implements \IteratorAggregate, \Countable, \JsonSerializable -{ - /** - * @param array $nodeIds - */ - private function __construct( - public array $nodeIds - ) { - } - - public static function create(NodeIdToPublishOrDiscard ...$nodeIds): self - { - return new self($nodeIds); - } - - /** - * @param array> $nodeIdData - */ - public static function fromArray(array $nodeIdData): self - { - return new self(array_map( - fn (array $nodeIdDatum): NodeIdToPublishOrDiscard => NodeIdToPublishOrDiscard::fromArray($nodeIdDatum), - $nodeIdData - )); - } - - public function merge(self $other): self - { - return new self(array_merge($this->nodeIds, $other->nodeIds)); - } - - public function getIterator(): \Traversable - { - yield from $this->nodeIds; - } - - public function isEmpty(): bool - { - return $this->nodeIds === []; - } - - public function count(): int - { - return count($this->nodeIds); - } - - /** - * @return array - */ - public function jsonSerialize(): array - { - return $this->nodeIds; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php index f1b154658ec..28c113e8b7e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php @@ -16,7 +16,6 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -34,8 +33,7 @@ public function __construct( /** * The old content stream, which contains ALL the data (discarded and non-discarded) */ - public ContentStreamId $previousContentStreamId, - public NodeAggregateIds $discardedNodes, + public ContentStreamId $previousContentStreamId ) { } @@ -46,21 +44,10 @@ public function getWorkspaceName(): WorkspaceName public static function fromArray(array $values): self { - $discardedNodes = []; - foreach ($values['discardedNodes'] as $discardedNode) { - if (is_array($discardedNode)) { - // legacy case: - $discardedNodes[] = $discardedNode['nodeAggregateId']; - continue; - } - $discardedNodes[] = $discardedNode; - } - return new self( WorkspaceName::fromString($values['workspaceName']), ContentStreamId::fromString($values['newContentStreamId']), - ContentStreamId::fromString($values['previousContentStreamId']), - NodeAggregateIds::fromArray($discardedNodes), + ContentStreamId::fromString($values['previousContentStreamId']) ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php index 38ae4f42b6e..bd6b2273296 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php @@ -15,7 +15,6 @@ namespace Neos\ContentRepository\Core\Feature\WorkspacePublication\Event; use Neos\ContentRepository\Core\EventStore\EventInterface; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateIds; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -41,27 +40,16 @@ public function __construct( * The old content stream, which contains ALL the data (discarded and non-discarded) */ public ContentStreamId $previousSourceContentStreamId, - public NodeAggregateIds $publishedNodes, ) { } public static function fromArray(array $values): self { - $publishedNodes = []; - foreach ($values['publishedNodes'] as $publishedNode) { - if (is_array($publishedNode)) { - // legacy case: - $publishedNodes[] = $publishedNode['nodeAggregateId']; - continue; - } - $publishedNodes[] = $publishedNode; - } return new self( WorkspaceName::fromString($values['sourceWorkspaceName']), WorkspaceName::fromString($values['targetWorkspaceName']), ContentStreamId::fromString($values['newSourceContentStreamId']), ContentStreamId::fromString($values['previousSourceContentStreamId']), - NodeAggregateIds::fromArray($publishedNodes), ); } diff --git a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php index d6f68bbed16..49c2e5ce40c 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php @@ -21,8 +21,6 @@ use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Command\RebaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; From e4ad10b4484bd9d8357b2c17e33a25538778a365 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:13:12 +0100 Subject: [PATCH 06/13] TASK: Simplify asset usage catchup hook to not optimise partial discard Instead of fine granular updating only the usage references on the nodes that were discarded, well throw away everything and then rely on the reapplied events what is kept. This is more consistent to how other parts react to publishing like the graph just forks a new content stream and doesnt care about the old data, the change projection behaves similar to the graph. And for the content cache flusher we had the discussion to use `discardedNodes` but opted against it and do a full flush there as well: https://github.com/neos/neos-development-collection/issues/5175#issuecomment-2386698992 This change allows us to get rid of the `discardedNodes` property (with dimensions) in the `WorkspaceWasPartiallyDiscarded` event. --- .../CatchUpHook/AssetUsageCatchUpHook.php | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php index 2cee2a58c01..2beeb84187b 100644 --- a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php +++ b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php @@ -15,7 +15,6 @@ use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeGeneralizationVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodePeerVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeSpecializationVariantWasCreated; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; use Neos\ContentRepository\Core\Projection\CatchUpHookInterface; @@ -59,7 +58,6 @@ public function onBeforeEvent(EventInterface $eventInstance, EventEnvelope $even match ($eventInstance::class) { NodeAggregateWasRemoved::class => $this->removeNodes($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->affectedCoveredDimensionSpacePoints), - WorkspaceWasPartiallyDiscarded::class => $this->discardNodes($eventInstance->getWorkspaceName(), $eventInstance->discardedNodes), default => null }; } @@ -81,7 +79,8 @@ public function onAfterEvent(EventInterface $eventInstance, EventEnvelope $event NodeGeneralizationVariantWasCreated::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->generalizationOrigin->toDimensionSpacePoint()), NodeSpecializationVariantWasCreated::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->specializationOrigin->toDimensionSpacePoint()), NodePropertiesWereSet::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->originDimensionSpacePoint->toDimensionSpacePoint()), - WorkspaceWasDiscarded::class => $this->discardWorkspace($eventInstance->getWorkspaceName()), + WorkspaceWasDiscarded::class, + WorkspaceWasPartiallyDiscarded::class => $this->discardWorkspace($eventInstance->getWorkspaceName()), DimensionSpacePointWasMoved::class => $this->updateDimensionSpacePoint($eventInstance->getWorkspaceName(), $eventInstance->source, $eventInstance->target), default => null }; @@ -144,22 +143,6 @@ private function discardWorkspace(WorkspaceName $workspaceName): void $this->assetUsageIndexingService->removeIndexForWorkspace($this->contentRepositoryId, $workspaceName); } - private function discardNodes(WorkspaceName $workspaceName, NodeIdsToPublishOrDiscard $nodeIds): void - { - foreach ($nodeIds as $nodeId) { - if (!$nodeId->dimensionSpacePoint) { - // NodeAggregateTypeWasChanged and NodeAggregateNameWasChanged don't impact asset usage - continue; - } - $this->assetUsageIndexingService->removeIndexForWorkspaceNameNodeAggregateIdAndDimensionSpacePoint( - $this->contentRepositoryId, - $workspaceName, - $nodeId->nodeAggregateId, - $nodeId->dimensionSpacePoint - ); - } - } - private function updateDimensionSpacePoint(WorkspaceName $workspaceName, DimensionSpacePoint $source, DimensionSpacePoint $target): void { $this->assetUsageIndexingService->updateDimensionSpacePointInIndex($this->contentRepositoryId, $workspaceName, $source, $target); From 4f8a9f6392120c33a3d26b931afa4af79baec9fb Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:25:51 +0100 Subject: [PATCH 07/13] WIP: Simplify publishing, that partial and full publish is the same reasoning: - the `publishedNodeIds` were removed on the event, as we dont need it, and it would rise a discussion if we need to keep dimension space points there too or not: see https://github.com/neos/neos-development-collection/pull/5386 how i archived it. - instead of now trying to make `WorkspaceWasPartiallyPublished` contain all the information we dont use or need, the idea is to make it dumb. Now its basically just a `WorkspaceWasPublished` and then the other remaining events are applied as if they were carried out by a user. If we need to add more metadata to the partial publish at some point, that would need a new event id say as we woouldnt be able to mess with the `WorkspaceWasPartiallyPublished` once released. So by just combining them into one we have the option open to create a `WorkspaceWasPartiallyPublished` again but differently which wil be breaking too but differently. Its just a new event then, no need to migrate data. --- .../DoctrineDbalContentGraphProjection.php | 16 ----- .../Classes/EventStore/EventNormalizer.php | 4 -- .../Feature/WorkspaceCommandHandler.php | 6 +- .../Event/WorkspaceWasPartiallyDiscarded.php | 58 ------------------ .../Event/WorkspaceWasPartiallyPublished.php | 60 ------------------- .../Classes/Service/ContentStreamPruner.php | 24 -------- .../CatchUpHook/AssetUsageCatchUpHook.php | 4 +- ...phProjectorCatchUpHookForCacheFlushing.php | 3 - 8 files changed, 3 insertions(+), 172 deletions(-) delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php delete mode 100644 Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index 39186aa616f..28aef165b3f 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -56,8 +56,6 @@ use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; @@ -196,8 +194,6 @@ public function canHandle(EventInterface $event): bool WorkspaceRebaseFailed::class, WorkspaceWasCreated::class, WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class, - WorkspaceWasPartiallyPublished::class, WorkspaceWasPublished::class, WorkspaceWasRebased::class, WorkspaceWasRemoved::class, @@ -233,8 +229,6 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void WorkspaceRebaseFailed::class => $this->whenWorkspaceRebaseFailed($event), WorkspaceWasCreated::class => $this->whenWorkspaceWasCreated($event), WorkspaceWasDiscarded::class => $this->whenWorkspaceWasDiscarded($event), - WorkspaceWasPartiallyDiscarded::class => $this->whenWorkspaceWasPartiallyDiscarded($event), - WorkspaceWasPartiallyPublished::class => $this->whenWorkspaceWasPartiallyPublished($event), WorkspaceWasPublished::class => $this->whenWorkspaceWasPublished($event), WorkspaceWasRebased::class => $this->whenWorkspaceWasRebased($event), WorkspaceWasRemoved::class => $this->whenWorkspaceWasRemoved($event), @@ -774,16 +768,6 @@ private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void $this->updateWorkspaceContentStreamId($event->workspaceName, $event->newContentStreamId); } - private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscarded $event): void - { - $this->updateWorkspaceContentStreamId($event->workspaceName, $event->newContentStreamId); - } - - private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void - { - $this->updateWorkspaceContentStreamId($event->sourceWorkspaceName, $event->newSourceContentStreamId); - } - private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void { $this->updateWorkspaceContentStreamId($event->sourceWorkspaceName, $event->newSourceContentStreamId); diff --git a/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php b/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php index 52f23d63910..0ba11175c3b 100644 --- a/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php +++ b/Neos.ContentRepository.Core/Classes/EventStore/EventNormalizer.php @@ -36,8 +36,6 @@ use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRenamed; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceRebaseFailed; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; @@ -102,8 +100,6 @@ public function __construct() WorkspaceWasCreated::class, WorkspaceWasRenamed::class, WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class, - WorkspaceWasPartiallyPublished::class, WorkspaceWasPublished::class, WorkspaceWasRebased::class, WorkspaceWasRemoved::class, diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php index 8af4e023a74..00911a70cc9 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php @@ -47,8 +47,6 @@ use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Command\RebaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; @@ -539,7 +537,7 @@ static function ($handle) use ($commandSimulator, $matchingCommands, $remainingC new EventsToPublish( WorkspaceEventStreamName::fromWorkspaceName($command->workspaceName)->getEventStreamName(), Events::fromArray([ - new WorkspaceWasPartiallyPublished( + new WorkspaceWasPublished( $command->workspaceName, $baseWorkspace->workspaceName, $command->contentStreamIdForRemainingPart, @@ -639,7 +637,7 @@ static function ($handle) use ($commandsToKeep): void { new EventsToPublish( WorkspaceEventStreamName::fromWorkspaceName($command->workspaceName)->getEventStreamName(), Events::with( - new WorkspaceWasPartiallyDiscarded( + new WorkspaceWasDiscarded( $command->workspaceName, $command->newContentStreamId, $workspace->currentContentStreamId, diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php deleted file mode 100644 index 28c113e8b7e..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyDiscarded.php +++ /dev/null @@ -1,58 +0,0 @@ -workspaceName; - } - - public static function fromArray(array $values): self - { - return new self( - WorkspaceName::fromString($values['workspaceName']), - ContentStreamId::fromString($values['newContentStreamId']), - ContentStreamId::fromString($values['previousContentStreamId']) - ); - } - - public function jsonSerialize(): array - { - return get_object_vars($this); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php deleted file mode 100644 index bd6b2273296..00000000000 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspacePublication/Event/WorkspaceWasPartiallyPublished.php +++ /dev/null @@ -1,60 +0,0 @@ -withStatus(ContentStreamStatus::NO_LONGER_IN_USE); } break; - case WorkspaceWasPartiallyDiscarded::class: - if (isset($cs[$domainEvent->newContentStreamId->value])) { - $cs[$domainEvent->newContentStreamId->value] = $cs[$domainEvent->newContentStreamId->value] - ->withStatus(ContentStreamStatus::IN_USE_BY_WORKSPACE); - } - if (isset($cs[$domainEvent->previousContentStreamId->value])) { - $cs[$domainEvent->previousContentStreamId->value] = $cs[$domainEvent->previousContentStreamId->value] - ->withStatus(ContentStreamStatus::NO_LONGER_IN_USE); - } - break; - case WorkspaceWasPartiallyPublished::class: - if (isset($cs[$domainEvent->newSourceContentStreamId->value])) { - $cs[$domainEvent->newSourceContentStreamId->value] = $cs[$domainEvent->newSourceContentStreamId->value] - ->withStatus(ContentStreamStatus::IN_USE_BY_WORKSPACE); - } - if (isset($cs[$domainEvent->previousSourceContentStreamId->value])) { - $cs[$domainEvent->previousSourceContentStreamId->value] = $cs[$domainEvent->previousSourceContentStreamId->value] - ->withStatus(ContentStreamStatus::NO_LONGER_IN_USE); - } - break; case WorkspaceWasPublished::class: if (isset($cs[$domainEvent->newSourceContentStreamId->value])) { $cs[$domainEvent->newSourceContentStreamId->value] = $cs[$domainEvent->newSourceContentStreamId->value] diff --git a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php index 2beeb84187b..631175e83d8 100644 --- a/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php +++ b/Neos.Neos/Classes/AssetUsage/CatchUpHook/AssetUsageCatchUpHook.php @@ -16,7 +16,6 @@ use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodePeerVariantWasCreated; use Neos\ContentRepository\Core\Feature\NodeVariation\Event\NodeSpecializationVariantWasCreated; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; use Neos\ContentRepository\Core\Projection\CatchUpHookInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphReadModelInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindDescendantNodesFilter; @@ -79,8 +78,7 @@ public function onAfterEvent(EventInterface $eventInstance, EventEnvelope $event NodeGeneralizationVariantWasCreated::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->generalizationOrigin->toDimensionSpacePoint()), NodeSpecializationVariantWasCreated::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->specializationOrigin->toDimensionSpacePoint()), NodePropertiesWereSet::class => $this->updateNode($eventInstance->getWorkspaceName(), $eventInstance->nodeAggregateId, $eventInstance->originDimensionSpacePoint->toDimensionSpacePoint()), - WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class => $this->discardWorkspace($eventInstance->getWorkspaceName()), + WorkspaceWasDiscarded::class => $this->discardWorkspace($eventInstance->getWorkspaceName()), DimensionSpacePointWasMoved::class => $this->updateDimensionSpacePoint($eventInstance->getWorkspaceName(), $eventInstance->source, $eventInstance->target), default => null }; diff --git a/Neos.Neos/Classes/Fusion/Cache/GraphProjectorCatchUpHookForCacheFlushing.php b/Neos.Neos/Classes/Fusion/Cache/GraphProjectorCatchUpHookForCacheFlushing.php index 4acf758411e..ec1b8ac4c5c 100644 --- a/Neos.Neos/Classes/Fusion/Cache/GraphProjectorCatchUpHookForCacheFlushing.php +++ b/Neos.Neos/Classes/Fusion/Cache/GraphProjectorCatchUpHookForCacheFlushing.php @@ -32,7 +32,6 @@ use Neos\ContentRepository\Core\Feature\SubtreeTagging\Event\SubtreeWasTagged; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Event\SubtreeWasUntagged; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; use Neos\ContentRepository\Core\Projection\CatchUpHookInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphReadModelInterface; @@ -130,7 +129,6 @@ public function canHandle(EventInterface $event): bool SubtreeWasTagged::class, SubtreeWasUntagged::class, WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class, WorkspaceWasRebased::class ]); } @@ -190,7 +188,6 @@ public function onAfterEvent(EventInterface $eventInstance, EventEnvelope $event if ( $eventInstance instanceof WorkspaceWasDiscarded - || $eventInstance instanceof WorkspaceWasPartiallyDiscarded || $eventInstance instanceof WorkspaceWasRebased ) { $this->scheduleCacheFlushJobForWorkspaceName($this->contentRepositoryId, $eventInstance->workspaceName); From e5a9100d1e6889ba0ec6c247918f5d5c421e4453 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 30 Nov 2024 21:34:01 +0100 Subject: [PATCH 08/13] Linter in the winter --- .../Classes/Feature/RebaseableCommands.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php index 3c63542f569..48029a0ed50 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php +++ b/Neos.ContentRepository.Core/Classes/Feature/RebaseableCommands.php @@ -82,7 +82,7 @@ private static function commandMatchesAtLeastOneNode( * * This is needed to publish and discard individual nodes. */ - $matches = match($command::class) { + $matches = match ($command::class) { CreateNodeAggregateWithNodeAndSerializedProperties::class, DisableNodeAggregate::class, EnableNodeAggregate::class, From 07b053e0cc70f7a33502ec9b96fef7a6fe31fefa Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 1 Dec 2024 09:45:46 +0100 Subject: [PATCH 09/13] TASK: Introduce migration to rename WorkspaceWasPartiallyPublished and WorkspaceWasPartiallyDiscarded --- .../MigrateEventsCommandController.php | 21 +++++ .../Classes/Service/EventMigrationService.php | 81 +++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/MigrateEventsCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/MigrateEventsCommandController.php index 01cbc8ccf87..d4748896276 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Command/MigrateEventsCommandController.php +++ b/Neos.ContentRepositoryRegistry/Classes/Command/MigrateEventsCommandController.php @@ -168,4 +168,25 @@ public function copyNodesStatusCommand(string $contentRepository = 'default'): v $eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory); $eventMigrationService->copyNodesStatus($this->outputLine(...)); } + + /** + * Renames partial publish and discard events to a publish or discard + * + * Needed for BUGFIX: Simplify PartialPublish & Discard: https://github.com/neos/neos-development-collection/pull/5385 + * + * Both events share the same properties their counter, parts have. + * Well keep the $publishedNodes and $discardedNodes fields in the database as they don't do harm. + * + * Included in November 2024 - before final Neos 9.0 release + * + * This migration is only required in case you want to replay. It does not fix anything. + * + * @param string $contentRepository Identifier of the Content Repository to migrate + */ + public function migratePartialPublishAndPartialDiscardEventsCommand(string $contentRepository = 'default'): void + { + $contentRepositoryId = ContentRepositoryId::fromString($contentRepository); + $eventMigrationService = $this->contentRepositoryRegistry->buildService($contentRepositoryId, $this->eventMigrationServiceFactory); + $eventMigrationService->migratePartialPublishAndPartialDiscardEvents($this->outputLine(...)); + } } diff --git a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php index 80d849bb17c..a29b069ab36 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php +++ b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php @@ -922,6 +922,72 @@ public function copyNodesStatus(\Closure $outputFn): void $outputFn('NOTE: To reduce the number of matched content streams and to cleanup the event store run `./flow contentStream:removeDangling` and `./flow contentStream:pruneRemovedFromEventStream`'); } + /** + * Renames partial publish and discard events to a publish or discard + * + * Needed for BUGFIX: Simplify PartialPublish & Discard: https://github.com/neos/neos-development-collection/pull/5385 + * + * Both events share the same properties their counter, parts have. + * Well keep the $publishedNodes and $discardedNodes fields in the database as they don't do harm. + * + * WorkspaceWasPartiallyPublished -> WorkspaceWasPublished + * + * - WorkspaceName $sourceWorkspaceName + * - WorkspaceName $targetWorkspaceName + * - ContentStreamId $newSourceContentStreamId + * - ContentStreamId $previousSourceContentStreamId + * - NodeIdsToPublishOrDiscard $publishedNodes + * + * WorkspaceWasPartiallyDiscarded -> WorkspaceWasDiscarded + * + * - WorkspaceName $workspaceName + * - ContentStreamId $newContentStreamId + * - ContentStreamId $previousContentStreamId + * - NodeIdsToPublishOrDiscard $discardedNodes + * + * + * Included in November 2024 - before final Neos 9.0 release + * + * This migration is only required in case you want to replay. It does not fix anything. + * + * @param \Closure $outputFn + * @return void + */ + public function migratePartialPublishAndPartialDiscardEvents(\Closure $outputFn): void + { + $backupEventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId) . '_bkp_' . date('Y_m_d_H_i_s'); + $outputFn('Backup: copying events table to %s', [$backupEventTableName]); + $this->copyEventTable($backupEventTableName); + + $numberOfMigratedEvents = 0; + + $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); + $this->connection->beginTransaction(); + $numberOfMigratedEvents += $this->connection->executeStatement( + 'UPDATE ' . $eventTableName . ' SET type=:updateType WHERE type=:currentType', + [ + 'currentType' => 'WorkspaceWasPartiallyPublished', + 'updateType' => 'WorkspaceWasPublished' + ] + ); + $numberOfMigratedEvents += $this->connection->executeStatement( + 'UPDATE ' . $eventTableName . ' SET type=:updateType WHERE type=:currentType', + [ + 'currentType' => 'WorkspaceWasPartiallyDiscarded', + 'updateType' => 'WorkspaceWasDiscarded' + ] + ); + $this->connection->commit(); + + if ($numberOfMigratedEvents === 0) { + $outputFn('Migration was not necessary.'); + return; + } + + $outputFn(); + $outputFn(sprintf('Migration applied to %s events.', $numberOfMigratedEvents)); + } + /** ------------------------ */ /** @@ -972,6 +1038,21 @@ private function updateEventMetaData(SequenceNumber $sequenceNumber, array $even $this->eventsModified[$sequenceNumber->value] = true; } + private function updateEventType(SequenceNumber $sequenceNumber, EventType $type): void + { + $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); + $this->connection->beginTransaction(); + $this->connection->executeStatement( + 'UPDATE ' . $eventTableName . ' SET type=:type WHERE sequencenumber=:sequenceNumber', + [ + 'sequenceNumber' => $sequenceNumber->value, + 'type' => $type->value + ] + ); + $this->connection->commit(); + $this->eventsModified[$sequenceNumber->value] = true; + } + private function copyEventTable(string $backupEventTableName): void { $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); From d8e0edee8422a38b44b37689ac36d72b26712105 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 2 Dec 2024 08:50:55 +0100 Subject: [PATCH 10/13] TASK: Phpstanisize event migrations --- .../Classes/Service/EventMigrationService.php | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php index a29b069ab36..b0ac5b7a431 100644 --- a/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php +++ b/Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php @@ -963,14 +963,14 @@ public function migratePartialPublishAndPartialDiscardEvents(\Closure $outputFn) $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); $this->connection->beginTransaction(); - $numberOfMigratedEvents += $this->connection->executeStatement( + $numberOfMigratedEvents += (int)$this->connection->executeStatement( 'UPDATE ' . $eventTableName . ' SET type=:updateType WHERE type=:currentType', [ 'currentType' => 'WorkspaceWasPartiallyPublished', 'updateType' => 'WorkspaceWasPublished' ] ); - $numberOfMigratedEvents += $this->connection->executeStatement( + $numberOfMigratedEvents += (int)$this->connection->executeStatement( 'UPDATE ' . $eventTableName . ' SET type=:updateType WHERE type=:currentType', [ 'currentType' => 'WorkspaceWasPartiallyDiscarded', @@ -1038,21 +1038,6 @@ private function updateEventMetaData(SequenceNumber $sequenceNumber, array $even $this->eventsModified[$sequenceNumber->value] = true; } - private function updateEventType(SequenceNumber $sequenceNumber, EventType $type): void - { - $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); - $this->connection->beginTransaction(); - $this->connection->executeStatement( - 'UPDATE ' . $eventTableName . ' SET type=:type WHERE sequencenumber=:sequenceNumber', - [ - 'sequenceNumber' => $sequenceNumber->value, - 'type' => $type->value - ] - ); - $this->connection->commit(); - $this->eventsModified[$sequenceNumber->value] = true; - } - private function copyEventTable(string $backupEventTableName): void { $eventTableName = DoctrineEventStoreFactory::databaseTableName($this->contentRepositoryId); From ec017237240a0c9b5919099691aed7e08775dbc7 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:42:39 +0100 Subject: [PATCH 11/13] TASK: Adjust new change projection tests to dont handle discarding one dimension for now --- .../01-ConstraintChecks.feature | 2 +- .../Bootstrap/ChangeProjectionTrait.php | 17 +++++++---------- ...NodesFromWorkspace_WithoutDimensions.feature | 4 ++-- ...ualNodesFromWorkspace_WithDimensions.feature | 8 ++++---- ...NodesFromWorkspace_WithoutDimensions.feature | 8 ++++---- ...ualNodesFromWorkspace_WithDimensions.feature | 9 +++------ 6 files changed, 21 insertions(+), 27 deletions(-) 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 c56a908e15f..1eefc590c1d 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 @@ -144,7 +144,7 @@ Feature: Workspace publication - complex chained functionality When the command PublishIndividualNodesFromWorkspace is executed with payload and exceptions are caught: | Key | Value | | workspaceName | "user-ws" | - | nodesToPublish | [{"dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | newContentStreamId | "user-cs-id-rebased" | Then the last command should have thrown the PartialWorkspaceRebaseFailed exception with: | SequenceNumber | Event | Exception | diff --git a/Neos.Neos/Tests/Behavior/Features/Bootstrap/ChangeProjectionTrait.php b/Neos.Neos/Tests/Behavior/Features/Bootstrap/ChangeProjectionTrait.php index b7828cbf7a6..db4d422f975 100644 --- a/Neos.Neos/Tests/Behavior/Features/Bootstrap/ChangeProjectionTrait.php +++ b/Neos.Neos/Tests/Behavior/Features/Bootstrap/ChangeProjectionTrait.php @@ -40,7 +40,7 @@ abstract private function getObject(string $className): object; public function iExpectTheChangeProjectionToHaveTheFollowingChangesInContentStream(TableNode $table, string $contentStreamId) { $changeFinder = $this->currentContentRepository->projectionState(ChangeFinder::class); - $changes = $changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId)); + $changes = iterator_to_array($changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId))); $tableRows = $table->getHash(); foreach ($changes as $change) { @@ -63,14 +63,11 @@ public function iExpectTheChangeProjectionToHaveTheFollowingChangesInContentStre } } - if (count($tableRows) !== 0) { - $tableHeader = array_combine(array_values($table->getRow(0)), array_values($table->getRow(0))); - $tableRemain = $tableRows; - array_unshift($tableRemain, $tableHeader); - Assert::assertEmpty($tableRows, "Not all given changes where found." . PHP_EOL . (new TableNode($tableRemain))->getTableAsString()); - } - Assert::assertSame(count($table->getHash()), $changes->count(), "More changes found as given."); + Assert::assertTrue( + $tableRows === [] && count($table->getHash()) === count($changes), + sprintf('Mismatch between all actual changes usages %s and leftover changes to match %s', json_encode($changes, JSON_PRETTY_PRINT), json_encode($tableRows, JSON_PRETTY_PRINT)) + ); } /** @@ -79,8 +76,8 @@ public function iExpectTheChangeProjectionToHaveTheFollowingChangesInContentStre public function iExpectTheChangeProjectionToHaveNoChangesInContentStream(string $contentStreamId) { $changeFinder = $this->currentContentRepository->projectionState(ChangeFinder::class); - $changes = $changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId)); + $changes = iterator_to_array($changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId))); - Assert::assertSame(0, $changes->count(), "No changes expected."); + Assert::assertEmpty($changes, "No changes expected, got: " . json_encode($changes, JSON_PRETTY_PRINT)); } } diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature index 98e67598008..62c2cbb1cf1 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/03-PublishIndividualNodesFromWorkspace_WithoutDimensions.feature @@ -50,7 +50,7 @@ Feature: Publish nodes partially without dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -99,7 +99,7 @@ Feature: Publish nodes partially without dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToPublish | ["nody-mc-nodeface"] | | contentStreamIdForRemainingPart | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature index a0227c2e0ad..846c9fec035 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W01-WorkspacePublication/04-PublishIndividualNodesFromWorkspace_WithDimensions.feature @@ -60,7 +60,7 @@ Feature: Publish nodes partially with dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -129,12 +129,11 @@ Feature: Publish nodes partially with dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToPublish | ["nody-mc-nodeface"] | | contentStreamIdForRemainingPart | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"gsw"} | | sir-nodeward-nodington-iv | 1 | 1 | 0 | 0 | {"language":"de"} | | sir-nodeward-nodington-iii | 1 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "user-cs-id" @@ -142,6 +141,7 @@ Feature: Publish nodes partially with dimensions | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | | sir-david-nodenborough | 1 | 1 | 0 | 0 | {"language":"de"} | | nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"de"} | + | nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"gsw"} | And I expect the ChangeProjection to have no changes in "cs-identifier" Scenario: Publish nodes partially from user workspace to live with new generalization @@ -174,7 +174,7 @@ Feature: Publish nodes partially with dimensions When the command PublishIndividualNodesFromWorkspace is executed with payload: | Key | Value | - | nodesToPublish | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "en"}, "nodeAggregateId": "sir-david-nodenborough"}] | + | nodesToPublish | ["sir-david-nodenborough", "sir-david-nodenborough"] | | contentStreamIdForRemainingPart | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature index 1f47143ce90..71d6382226c 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/03-DiscardIndividualNodesFromWorkspace_WithoutDimensions.feature @@ -53,7 +53,7 @@ Feature: Discard nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -85,7 +85,7 @@ Feature: Discard nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iii"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iv"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iii", "sir-nodeward-nodington-iv"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -131,7 +131,7 @@ Feature: Discard nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iv"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iv"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -182,7 +182,7 @@ Feature: Discard nodes partially without dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-iv"},{"workspaceName": "user-workspace", "dimensionSpacePoint": {}, "nodeAggregateId": "sir-nodeward-nodington-v"}] | + | nodesToDiscard | ["sir-nodeward-nodington-iv", "sir-nodeward-nodington-v"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature index 522aaf2028c..520f8296bd8 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/W02-WorkspaceDiscarding/04-DiscardIndividualNodesFromWorkspace_WithDimensions.feature @@ -60,7 +60,7 @@ Feature: Discard nodes partially with dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToDiscard | ["sir-david-nodenborough", "nody-mc-nodeface"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": @@ -129,7 +129,7 @@ Feature: Discard nodes partially with dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "gsw"}, "nodeAggregateId": "nody-mc-nodeface"}] | + | nodesToDiscard | ["nody-mc-nodeface"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "review-cs-id": @@ -137,7 +137,6 @@ Feature: Discard nodes partially with dimensions | sir-david-nodenborough | 1 | 1 | 0 | 0 | {"language":"de"} | And I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"de"} | | sir-nodeward-nodington-iii | 1 | 1 | 0 | 0 | {"language":"fr"} | | sir-nodeward-nodington-iv | 1 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "user-cs-id" @@ -176,13 +175,11 @@ Feature: Discard nodes partially with dimensions When the command DiscardIndividualNodesFromWorkspace is executed with payload: | Key | Value | | workspaceName | "user-workspace" | - | nodesToDiscard | [{"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "en"} , "nodeAggregateId": "sir-david-nodenborough"}, {"workspaceName": "user-workspace", "dimensionSpacePoint": {"language": "de"} , "nodeAggregateId": "sir-nodeward-nodington-iii"}] | + | nodesToDiscard | ["sir-david-nodenborough", "nody-mc-nodeface", "sir-nodeward-nodington-iii"] | | newContentStreamId | "user-cs-id-remaining" | Then I expect the ChangeProjection to have the following changes in "user-cs-id-remaining": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 1 | 1 | 0 | 0 | {"language":"de"} | - | nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"de"} | | sir-nodeward-nodington-iv | 1 | 1 | 0 | 0 | {"language":"de"} | And I expect the ChangeProjection to have no changes in "user-cs-id" And I expect the ChangeProjection to have no changes in "cs-identifier" From 0337e9676bc428696b8a73874ea318a87f78d08b Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:19:04 +0100 Subject: [PATCH 12/13] TASK: Introduce `$affectedOriginDimensionSpacePoints` to `NodeAggregateTypeWasChanged` (and rename) This information is required for the change projection and in the future to select changes for one dimension to publish --- .../Classes/Feature/Common/TetheredNodeInternals.php | 1 + .../NodeRenaming/Event/NodeAggregateNameWasChanged.php | 5 +++++ .../Classes/Feature/NodeRenaming/NodeRenaming.php | 3 ++- .../NodeTypeChange/Event/NodeAggregateTypeWasChanged.php | 9 +++++++-- .../Classes/Feature/NodeTypeChange/NodeTypeChange.php | 1 + 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php index 57b99797015..d92d1d96fda 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/TetheredNodeInternals.php @@ -279,6 +279,7 @@ protected function createEventsForWronglyTypedNodeAggregate( $contentGraph->getContentStreamId(), $nodeAggregate->nodeAggregateId, $newNodeTypeName, + $nodeAggregate->occupiedDimensionSpacePoints ); # Handle property adjustments diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Event/NodeAggregateNameWasChanged.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Event/NodeAggregateNameWasChanged.php index 73956b43f54..c61112ccab2 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Event/NodeAggregateNameWasChanged.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/Event/NodeAggregateNameWasChanged.php @@ -14,6 +14,7 @@ namespace Neos\ContentRepository\Core\Feature\NodeRenaming\Event; +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet; use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamId; use Neos\ContentRepository\Core\Feature\Common\EmbedsNodeAggregateId; @@ -39,6 +40,8 @@ public function __construct( public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, public NodeName $newNodeName, + /** All dimensions are always affected. All origins this node occupied. */ + public OriginDimensionSpacePointSet $affectedOriginDimensionSpacePoints, ) { } @@ -64,6 +67,7 @@ public function withWorkspaceNameAndContentStreamId(WorkspaceName $targetWorkspa $contentStreamId, $this->nodeAggregateId, $this->newNodeName, + $this->affectedOriginDimensionSpacePoints ); } @@ -74,6 +78,7 @@ public static function fromArray(array $values): self ContentStreamId::fromString($values['contentStreamId']), NodeAggregateId::fromString($values['nodeAggregateId']), NodeName::fromString($values['newNodeName']), + OriginDimensionSpacePointSet::fromArray($values['affectedOriginDimensionSpacePoints']) ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/NodeRenaming.php b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/NodeRenaming.php index f3531340345..e8201a1d99c 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/NodeRenaming.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeRenaming/NodeRenaming.php @@ -54,8 +54,9 @@ private function handleChangeNodeAggregateName(ChangeNodeAggregateName $command, new NodeAggregateNameWasChanged( $contentGraph->getWorkspaceName(), $contentGraph->getContentStreamId(), - $command->nodeAggregateId, + $nodeAggregate->nodeAggregateId, $command->newNodeName, + $nodeAggregate->occupiedDimensionSpacePoints ), ); diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Event/NodeAggregateTypeWasChanged.php b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Event/NodeAggregateTypeWasChanged.php index 0b09e68fc2d..0c31123a982 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Event/NodeAggregateTypeWasChanged.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/Event/NodeAggregateTypeWasChanged.php @@ -14,6 +14,7 @@ namespace Neos\ContentRepository\Core\Feature\NodeTypeChange\Event; +use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePointSet; use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamId; use Neos\ContentRepository\Core\Feature\Common\EmbedsNodeAggregateId; @@ -38,7 +39,9 @@ public function __construct( public WorkspaceName $workspaceName, public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, - public NodeTypeName $newNodeTypeName + public NodeTypeName $newNodeTypeName, + /** All dimensions are always affected. All origins this node occupied. */ + public OriginDimensionSpacePointSet $affectedOriginDimensionSpacePoints, ) { } @@ -63,7 +66,8 @@ public function withWorkspaceNameAndContentStreamId(WorkspaceName $targetWorkspa $targetWorkspaceName, $contentStreamId, $this->nodeAggregateId, - $this->newNodeTypeName + $this->newNodeTypeName, + $this->affectedOriginDimensionSpacePoints ); } @@ -74,6 +78,7 @@ public static function fromArray(array $values): self ContentStreamId::fromString($values['contentStreamId']), NodeAggregateId::fromString($values['nodeAggregateId']), NodeTypeName::fromString($values['newNodeTypeName']), + OriginDimensionSpacePointSet::fromArray($values['affectedOriginDimensionSpacePoints']) ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php index 5436b048236..b389de54753 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php +++ b/Neos.ContentRepository.Core/Classes/Feature/NodeTypeChange/NodeTypeChange.php @@ -195,6 +195,7 @@ private function handleChangeNodeAggregateType( $contentGraph->getContentStreamId(), $command->nodeAggregateId, $command->newNodeTypeName, + $nodeAggregate->occupiedDimensionSpacePoints ), ]; From 97f36d29ac0068b954f9701495ff79112dd82b8e Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 5 Dec 2024 10:37:30 +0100 Subject: [PATCH 13/13] BUGFIX: Simplify change projection to mark aggregate changes as change in all occupied dimensions --- .../Service/WorkspacePublishingService.php | 42 ++++------- .../PendingChangesProjection/Change.php | 17 ++--- .../ChangeProjection.php | 71 ++++--------------- .../Bootstrap/ChangeProjectionTrait.php | 6 +- ...odeAggregateName_WithoutDimensions.feature | 3 +- ...geNodeAggregateName_WithDimensions.feature | 5 +- ...odeAggregateType_WithoutDimensions.feature | 3 +- ...geNodeAggregateType_WithDimensions.feature | 5 +- .../Controller/WorkspaceController.php | 7 +- 9 files changed, 43 insertions(+), 116 deletions(-) diff --git a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php index 49c2e5ce40c..743024af294 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php @@ -387,38 +387,20 @@ private function isChangePublishableWithinAncestorScope( } } - if ($change->originDimensionSpacePoint) { - $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( - $change->originDimensionSpacePoint->toDimensionSpacePoint(), - VisibilityConstraints::withoutRestrictions() - ); - - // A Change is publishable if the respective node (or the respective - // removal attachment point) has a closest ancestor that matches our - // current ancestor scope (Document/Site) - $actualAncestorNode = $subgraph->findClosestNode( - $change->removalAttachmentPoint ?? $change->nodeAggregateId, - FindClosestNodeFilter::create(nodeTypes: $ancestorNodeTypeName->value) - ); - - return $actualAncestorNode?->aggregateId->equals($ancestorId) ?? false; - } else { - return $this->findAncestorAggregateIds( - $contentRepository->getContentGraph($workspaceName), - $change->nodeAggregateId - )->contain($ancestorId); - } - } + $subgraph = $contentRepository->getContentGraph($workspaceName)->getSubgraph( + $change->originDimensionSpacePoint->toDimensionSpacePoint(), + VisibilityConstraints::withoutRestrictions() + ); - private function findAncestorAggregateIds(ContentGraphInterface $contentGraph, NodeAggregateId $descendantNodeAggregateId): NodeAggregateIds - { - $nodeAggregateIds = NodeAggregateIds::create($descendantNodeAggregateId); - foreach ($contentGraph->findParentNodeAggregates($descendantNodeAggregateId) as $parentNodeAggregate) { - $nodeAggregateIds = $nodeAggregateIds->merge(NodeAggregateIds::create($parentNodeAggregate->nodeAggregateId)); - $nodeAggregateIds = $nodeAggregateIds->merge($this->findAncestorAggregateIds($contentGraph, $parentNodeAggregate->nodeAggregateId)); - } + // A Change is publishable if the respective node (or the respective + // removal attachment point) has a closest ancestor that matches our + // current ancestor scope (Document/Site) + $actualAncestorNode = $subgraph->findClosestNode( + $change->removalAttachmentPoint ?? $change->nodeAggregateId, + FindClosestNodeFilter::create(nodeTypes: $ancestorNodeTypeName->value) + ); - return $nodeAggregateIds; + return $actualAncestorNode?->aggregateId->equals($ancestorId) ?? false; } /** diff --git a/Neos.Neos/Classes/PendingChangesProjection/Change.php b/Neos.Neos/Classes/PendingChangesProjection/Change.php index 798902f5517..acee1cda5e6 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/Change.php +++ b/Neos.Neos/Classes/PendingChangesProjection/Change.php @@ -30,16 +30,13 @@ */ final class Change { - public const AGGREGATE_DIMENSIONSPACEPOINT_HASH_PLACEHOLDER = 'AGGREGATE'; - /** * @param NodeAggregateId|null $removalAttachmentPoint {@see RemoveNodeAggregate::$removalAttachmentPoint} for docs */ public function __construct( public ContentStreamId $contentStreamId, public NodeAggregateId $nodeAggregateId, - // null for aggregate scoped changes (e.g. NodeAggregateNameWasChanged, NodeAggregateTypeWasChanged) - public ?OriginDimensionSpacePoint $originDimensionSpacePoint, + public OriginDimensionSpacePoint $originDimensionSpacePoint, public bool $created, public bool $changed, public bool $moved, @@ -58,8 +55,8 @@ public function addToDatabase(Connection $databaseConnection, string $tableName) $databaseConnection->insert($tableName, [ 'contentStreamId' => $this->contentStreamId->value, 'nodeAggregateId' => $this->nodeAggregateId->value, - 'originDimensionSpacePoint' => $this->originDimensionSpacePoint?->toJson(), - 'originDimensionSpacePointHash' => $this->originDimensionSpacePoint?->hash ?: self::AGGREGATE_DIMENSIONSPACEPOINT_HASH_PLACEHOLDER, + 'originDimensionSpacePoint' => $this->originDimensionSpacePoint->toJson(), + 'originDimensionSpacePointHash' => $this->originDimensionSpacePoint->hash, 'created' => (int)$this->created, 'changed' => (int)$this->changed, 'moved' => (int)$this->moved, @@ -86,8 +83,8 @@ public function updateToDatabase(Connection $databaseConnection, string $tableNa [ 'contentStreamId' => $this->contentStreamId->value, 'nodeAggregateId' => $this->nodeAggregateId->value, - 'originDimensionSpacePoint' => $this->originDimensionSpacePoint?->toJson(), - 'originDimensionSpacePointHash' => $this->originDimensionSpacePoint?->hash ?: self::AGGREGATE_DIMENSIONSPACEPOINT_HASH_PLACEHOLDER, + 'originDimensionSpacePoint' => $this->originDimensionSpacePoint->toJson(), + 'originDimensionSpacePointHash' => $this->originDimensionSpacePoint->hash, ] ); } catch (DbalException $e) { @@ -103,9 +100,7 @@ public static function fromDatabaseRow(array $databaseRow): self return new self( ContentStreamId::fromString($databaseRow['contentStreamId']), NodeAggregateId::fromString($databaseRow['nodeAggregateId']), - $databaseRow['originDimensionSpacePoint'] ?? null - ? OriginDimensionSpacePoint::fromJsonString($databaseRow['originDimensionSpacePoint']) - : null, + OriginDimensionSpacePoint::fromJsonString($databaseRow['originDimensionSpacePoint']), (bool)$databaseRow['created'], (bool)$databaseRow['changed'], (bool)$databaseRow['moved'], diff --git a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php index da5c068067b..bef2ce02dc9 100644 --- a/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php +++ b/Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php @@ -415,10 +415,13 @@ private function whenNodeAggregateTypeWasChanged(NodeAggregateTypeWasChanged $ev if ($event->workspaceName->isLive()) { return; } - $this->markAggregateAsChanged( - $event->contentStreamId, - $event->nodeAggregateId, - ); + foreach ($event->affectedOriginDimensionSpacePoints as $originDimensionSpacePoint) { + $this->markAsChanged( + $event->contentStreamId, + $event->nodeAggregateId, + $originDimensionSpacePoint + ); + } } private function whenNodeAggregateNameWasChanged(NodeAggregateNameWasChanged $event): void @@ -426,10 +429,13 @@ private function whenNodeAggregateNameWasChanged(NodeAggregateNameWasChanged $ev if ($event->workspaceName->isLive()) { return; } - $this->markAggregateAsChanged( - $event->contentStreamId, - $event->nodeAggregateId, - ); + foreach ($event->affectedOriginDimensionSpacePoints as $originDimensionSpacePoint) { + $this->markAsChanged( + $event->contentStreamId, + $event->nodeAggregateId, + $originDimensionSpacePoint + ); + } } private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event): void @@ -452,19 +458,6 @@ static function (Change $change) { ); } - private function markAggregateAsChanged( - ContentStreamId $contentStreamId, - NodeAggregateId $nodeAggregateId, - ): void { - $this->modifyChangeForAggregate( - $contentStreamId, - $nodeAggregateId, - static function (Change $change) { - $change->changed = true; - } - ); - } - private function markAsCreated( ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, @@ -514,23 +507,6 @@ private function modifyChange( } } - private function modifyChangeForAggregate( - ContentStreamId $contentStreamId, - NodeAggregateId $nodeAggregateId, - callable $modifyFn - ): void { - $change = $this->getChangeForAggregate($contentStreamId, $nodeAggregateId); - - if ($change === null) { - $change = new Change($contentStreamId, $nodeAggregateId, null, false, false, false, false); - $modifyFn($change); - $change->addToDatabase($this->dbal, $this->tableNamePrefix); - } else { - $modifyFn($change); - $change->updateToDatabase($this->dbal, $this->tableNamePrefix); - } - } - private function getChange( ContentStreamId $contentStreamId, NodeAggregateId $nodeAggregateId, @@ -552,25 +528,6 @@ private function getChange( return $changeRow ? Change::fromDatabaseRow($changeRow) : null; } - private function getChangeForAggregate( - ContentStreamId $contentStreamId, - NodeAggregateId $nodeAggregateId, - ): ?Change { - $changeRow = $this->dbal->executeQuery( - 'SELECT n.* FROM ' . $this->tableNamePrefix . ' n -WHERE n.contentStreamId = :contentStreamId -AND n.nodeAggregateId = :nodeAggregateId -AND n.origindimensionspacepointhash = :origindimensionspacepointhash', - [ - 'contentStreamId' => $contentStreamId->value, - 'nodeAggregateId' => $nodeAggregateId->value, - 'origindimensionspacepointhash' => Change::AGGREGATE_DIMENSIONSPACEPOINT_HASH_PLACEHOLDER - ] - )->fetchAssociative(); - - return $changeRow ? Change::fromDatabaseRow($changeRow) : null; - } - private function removeChangesForContentStreamId(ContentStreamId $contentStreamId): void { $statement = <<deleted !== (bool)$tableRow['deleted'] || $change->changed !== (bool)$tableRow['changed'] || $change->moved !== (bool)$tableRow['moved'] - || ( - ($change->originDimensionSpacePoint === null && strtolower($tableRow['originDimensionSpacePoint']) !== "null") - && - ($change->originDimensionSpacePoint !== null && strtolower($tableRow['originDimensionSpacePoint']) !== "null" && !$change->originDimensionSpacePoint->equals(DimensionSpacePoint::fromJsonString($tableRow['originDimensionSpacePoint']))) - ) + || !$change->originDimensionSpacePoint->equals(DimensionSpacePoint::fromJsonString($tableRow['originDimensionSpacePoint'])) ) { continue; } diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/01-ChangeNodeAggregateName_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/01-ChangeNodeAggregateName_WithoutDimensions.feature index 4082df00bd5..da8ebf49bec 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/01-ChangeNodeAggregateName_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/01-ChangeNodeAggregateName_WithoutDimensions.feature @@ -56,7 +56,7 @@ Feature: Change node aggregate name without dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {} | And I expect the ChangeProjection to have no changes in "cs-identifier" Scenario: Change the node aggregate name with already applied changes @@ -74,6 +74,5 @@ Feature: Change node aggregate name without dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | | sir-david-nodenborough | 0 | 1 | 0 | 0 | {} | And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/02-ChangeNodeAggregateName_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/02-ChangeNodeAggregateName_WithDimensions.feature index 06897a88ee1..988d45c3940 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/02-ChangeNodeAggregateName_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/08-NodeRenaming/02-ChangeNodeAggregateName_WithDimensions.feature @@ -69,7 +69,8 @@ Feature: Change node aggregate name with dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"de"} | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "cs-identifier" Scenario: Change the node aggregate type with already applied changes @@ -87,6 +88,6 @@ Feature: Change node aggregate name with dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"de"} | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/01-ChangeNodeAggregateType_WithoutDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/01-ChangeNodeAggregateType_WithoutDimensions.feature index ee0793e9ca9..caab5b873ec 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/01-ChangeNodeAggregateType_WithoutDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/01-ChangeNodeAggregateType_WithoutDimensions.feature @@ -58,7 +58,7 @@ Feature: Change node aggregate type without dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {} | And I expect the ChangeProjection to have no changes in "cs-identifier" Scenario: Change the node aggregate type with already applied changes @@ -78,6 +78,5 @@ Feature: Change node aggregate type without dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | | sir-david-nodenborough | 0 | 1 | 0 | 0 | {} | And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/02-ChangeNodeAggregateType_WithDimensions.feature b/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/02-ChangeNodeAggregateType_WithDimensions.feature index fa37c9700ca..5c86289775f 100644 --- a/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/02-ChangeNodeAggregateType_WithDimensions.feature +++ b/Neos.Neos/Tests/Behavior/Features/PendingChanges/09-NodeTypeChange/02-ChangeNodeAggregateType_WithDimensions.feature @@ -71,7 +71,8 @@ Feature: Change node aggregate type with dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"de"} | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "cs-identifier" Scenario: Change the node aggregate type with already applied changes @@ -91,6 +92,6 @@ Feature: Change node aggregate type with dimensions Then I expect the ChangeProjection to have the following changes in "user-cs-id": | nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint | - | sir-david-nodenborough | 0 | 1 | 0 | 0 | null | | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"de"} | + | sir-david-nodenborough | 0 | 1 | 0 | 0 | {"language":"fr"} | And I expect the ChangeProjection to have no changes in "cs-identifier" diff --git a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php index 8718f28aedb..eb106bdb588 100644 --- a/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php +++ b/Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php @@ -672,9 +672,6 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos ->findByContentStreamId( $selectedWorkspace->currentContentStreamId ); - $dimensionSpacePoints = iterator_to_array($contentRepository->getVariationGraph()->getDimensionSpacePoints()); - /** @var DimensionSpacePoint $arbitraryDimensionSpacePoint */ - $arbitraryDimensionSpacePoint = reset($dimensionSpacePoints); $selectedWorkspaceContentGraph = $contentRepository->getContentGraph($selectedWorkspace->workspaceName); // If we deleted a node, there is no way for us to anymore find the deleted node in the ContentStream @@ -688,7 +685,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos foreach ($changes as $change) { $contentGraph = $change->deleted ? $baseWorkspaceContentGraph : $selectedWorkspaceContentGraph; $subgraph = $contentGraph->getSubgraph( - $change->originDimensionSpacePoint?->toDimensionSpacePoint() ?: $arbitraryDimensionSpacePoint, + $change->originDimensionSpacePoint->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions() ); @@ -756,7 +753,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos $nodeAddress = NodeAddress::create( $contentRepository->id, $selectedWorkspace->workspaceName, - $change->originDimensionSpacePoint?->toDimensionSpacePoint() ?: $arbitraryDimensionSpacePoint, + $change->originDimensionSpacePoint->toDimensionSpacePoint(), $change->nodeAggregateId );