From e96ce9909b8461b65163efe0f0d19791c27da152 Mon Sep 17 00:00:00 2001 From: Wilhelm Behncke Date: Tue, 23 Apr 2024 12:51:52 +0200 Subject: [PATCH] TASK: Move logic to create Conflicts from WorkspaceRebaseFailed into dedicated Factory --- ...PublishChangesInDocumentCommandHandler.php | 9 +- .../PublishChangesInSiteCommandHandler.php | 10 +- Classes/Application/Shared/Conflict.php | 2 + Classes/Application/Shared/Conflicts.php | 13 +- .../Application/Shared/ConflictsBuilder.php | 260 +--------------- .../SyncWorkspaceCommandHandler.php | 10 +- .../ContentRepository/ConflictsFactory.php | 280 ++++++++++++++++++ 7 files changed, 301 insertions(+), 283 deletions(-) create mode 100644 Classes/Infrastructure/ContentRepository/ConflictsFactory.php diff --git a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php index e79e12459b..4f0d7f59ab 100644 --- a/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php +++ b/Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommandHandler.php @@ -22,6 +22,7 @@ use Neos\Neos\Ui\Application\Shared\Conflicts; use Neos\Neos\Ui\Application\Shared\ConflictsOccurred; use Neos\Neos\Ui\Application\Shared\PublishSucceeded; +use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory; /** * The application layer level command handler to perform publication of @@ -61,19 +62,15 @@ public function handle( 1682762156 ); } catch (WorkspaceRebaseFailed $e) { - $conflictsBuilder = Conflicts::builder( + $conflictsFactory = new ConflictsFactory( contentRepository: $this->contentRepositoryRegistry ->get($command->contentRepositoryId), workspaceName: $command->workspaceName, preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint ); - foreach ($e->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { - $conflictsBuilder->addCommandThatFailedDuringRebase($commandThatFailedDuringRebase); - } - return new ConflictsOccurred( - conflicts: $conflictsBuilder->build() + conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e) ); } } diff --git a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php index 5b333c6d30..a5877d8645 100644 --- a/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php +++ b/Classes/Application/PublishChangesInSite/PublishChangesInSiteCommandHandler.php @@ -18,9 +18,9 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Workspace\WorkspaceProvider; -use Neos\Neos\Ui\Application\Shared\Conflicts; use Neos\Neos\Ui\Application\Shared\ConflictsOccurred; use Neos\Neos\Ui\Application\Shared\PublishSucceeded; +use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory; /** * The application layer level command handler to perform publication of @@ -52,19 +52,15 @@ public function handle( baseWorkspaceName: $workspace->getCurrentBaseWorkspaceName()?->value ); } catch (WorkspaceRebaseFailed $e) { - $conflictsBuilder = Conflicts::builder( + $conflictsFactory = new ConflictsFactory( contentRepository: $this->contentRepositoryRegistry ->get($command->contentRepositoryId), workspaceName: $command->workspaceName, preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint ); - foreach ($e->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { - $conflictsBuilder->addCommandThatFailedDuringRebase($commandThatFailedDuringRebase); - } - return new ConflictsOccurred( - conflicts: $conflictsBuilder->build() + conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e) ); } } diff --git a/Classes/Application/Shared/Conflict.php b/Classes/Application/Shared/Conflict.php index 3f77f0ebeb..faf13b690a 100644 --- a/Classes/Application/Shared/Conflict.php +++ b/Classes/Application/Shared/Conflict.php @@ -14,6 +14,7 @@ namespace Neos\Neos\Ui\Application\Shared; +use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\Flow\Annotations as Flow; /** @@ -25,6 +26,7 @@ final readonly class Conflict implements \JsonSerializable { public function __construct( + public string $key, public ?IconLabel $affectedSite, public ?IconLabel $affectedDocument, public ?IconLabel $affectedNode, diff --git a/Classes/Application/Shared/Conflicts.php b/Classes/Application/Shared/Conflicts.php index 4d0697cebd..fd3590688a 100644 --- a/Classes/Application/Shared/Conflicts.php +++ b/Classes/Application/Shared/Conflicts.php @@ -33,16 +33,9 @@ public function __construct(Conflict ...$items) $this->items = $items; } - public static function builder( - ContentRepository $contentRepository, - WorkspaceName $workspaceName, - ?DimensionSpacePoint $preferredDimensionSpacePoint, - ): ConflictsBuilder { - return new ConflictsBuilder( - contentRepository: $contentRepository, - workspaceName: $workspaceName, - preferredDimensionSpacePoint: $preferredDimensionSpacePoint - ); + public static function builder(): ConflictsBuilder + { + return new ConflictsBuilder(); } public function jsonSerialize(): mixed diff --git a/Classes/Application/Shared/ConflictsBuilder.php b/Classes/Application/Shared/ConflictsBuilder.php index 43fb70e2e3..379b6a441a 100644 --- a/Classes/Application/Shared/ConflictsBuilder.php +++ b/Classes/Application/Shared/ConflictsBuilder.php @@ -14,36 +14,7 @@ namespace Neos\Neos\Ui\Application\Shared; -use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; -use Neos\ContentRepository\Core\Feature\NodeCreation\Command\CreateNodeAggregateWithNode; -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\NodeModification\Command\SetNodeProperties; -use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetSerializedNodeProperties; -use Neos\ContentRepository\Core\Feature\NodeMove\Command\MoveNodeAggregate; -use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetNodeReferences; -use Neos\ContentRepository\Core\Feature\NodeReferencing\Command\SetSerializedNodeReferences; -use Neos\ContentRepository\Core\Feature\NodeRemoval\Command\RemoveNodeAggregate; -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\Feature\WorkspaceRebase\CommandThatFailedDuringRebase; -use Neos\ContentRepository\Core\NodeType\NodeTypeManager; -use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; -use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter; -use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; -use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; -use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist; -use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\Flow\Annotations as Flow; -use Neos\Neos\Domain\Service\NodeTypeNameFactory; /** * @internal @@ -51,10 +22,6 @@ #[Flow\Proxy(false)] final class ConflictsBuilder { - private NodeTypeManager $nodeTypeManager; - - private ?Workspace $workspace; - /** * @var Conflict[] */ @@ -63,233 +30,20 @@ final class ConflictsBuilder /** * @var array */ - private array $itemsByAffectedNodeAggregateId = []; - - public function __construct( - private ContentRepository $contentRepository, - WorkspaceName $workspaceName, - private ?DimensionSpacePoint $preferredDimensionSpacePoint, - ) { - $this->nodeTypeManager = $contentRepository->getNodeTypeManager(); - - $this->workspace = $contentRepository->getWorkspaceFinder() - ->findOneByName($workspaceName); - } + private array $itemsByKey = []; - public function addCommandThatFailedDuringRebase( - CommandThatFailedDuringRebase $commandThatFailedDuringRebase - ): void { - $nodeAggregateId = $this->extractNodeAggregateIdFromCommand( - $commandThatFailedDuringRebase->command - ); - - if ($nodeAggregateId && isset($this->itemsByAffectedNodeAggregateId[$nodeAggregateId->value])) { - return; + public function addConflict(Conflict $conflict): self + { + if (!isset($this->itemsByKey[$conflict->key])) { + $this->itemsByKey[$conflict->key] = $conflict; + $this->items[] = $conflict; } - $conflict = $this->createConflictFromCommandThatFailedDuringRebase( - $commandThatFailedDuringRebase - ); - - $this->items[] = $conflict; - - if ($nodeAggregateId) { - $this->itemsByAffectedNodeAggregateId[$nodeAggregateId->value] = $conflict; - } + return $this; } public function build(): Conflicts { return new Conflicts(...$this->items); } - - private function createConflictFromCommandThatFailedDuringRebase( - CommandThatFailedDuringRebase $commandThatFailedDuringRebase - ): Conflict { - $nodeAggregateId = $this->extractNodeAggregateIdFromCommand( - $commandThatFailedDuringRebase->command - ); - $subgraph = $this->acquireSubgraphFromCommand( - $commandThatFailedDuringRebase->command, - $nodeAggregateId - ); - $affectedSite = $nodeAggregateId - ? $subgraph->findClosestNode( - $nodeAggregateId, - FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE) - ) - : null; - $affectedDocument = $nodeAggregateId - ? $subgraph->findClosestNode( - $nodeAggregateId, - FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_DOCUMENT) - ) - : null; - $affectedNode = $nodeAggregateId - ? $subgraph?->findNodeById($nodeAggregateId) - : null; - - return new Conflict( - affectedSite: $affectedSite - ? $this->createIconLabelForNode($affectedSite) - : null, - affectedDocument: $affectedDocument - ? $this->createIconLabelForNode($affectedDocument) - : null, - affectedNode: $affectedNode - ? $this->createIconLabelForNode($affectedNode) - : null, - typeOfChange: $this->createTypeOfChangeFromCommand( - $commandThatFailedDuringRebase->command - ), - reasonForConflict: $this->createReasonForConflictFromException( - $commandThatFailedDuringRebase->exception - ) - ); - } - - private function extractNodeAggregateIdFromCommand(CommandInterface $command): ?NodeAggregateId - { - return match (true) { - $command instanceof MoveNodeAggregate, - $command instanceof SetNodeProperties, - $command instanceof SetSerializedNodeProperties, - $command instanceof CreateNodeAggregateWithNode, - $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties, - $command instanceof TagSubtree, - $command instanceof DisableNodeAggregate, - $command instanceof UntagSubtree, - $command instanceof EnableNodeAggregate, - $command instanceof RemoveNodeAggregate, - $command instanceof ChangeNodeAggregateType, - $command instanceof CreateNodeVariant => - $command->nodeAggregateId, - $command instanceof SetNodeReferences, - $command instanceof SetSerializedNodeReferences => - $command->sourceNodeAggregateId, - default => null - }; - } - - private function acquireSubgraphFromCommand( - CommandInterface $command, - ?NodeAggregateId $nodeAggregateIdForDimensionFallback - ): ?ContentSubgraphInterface { - if ($this->workspace === null) { - return null; - } - - $dimensionSpacePoint = match (true) { - $command instanceof MoveNodeAggregate => - $command->dimensionSpacePoint, - $command instanceof SetNodeProperties, - $command instanceof SetSerializedNodeProperties, - $command instanceof CreateNodeAggregateWithNode, - $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties => - $command->originDimensionSpacePoint->toDimensionSpacePoint(), - $command instanceof SetNodeReferences, - $command instanceof SetSerializedNodeReferences => - $command->sourceOriginDimensionSpacePoint->toDimensionSpacePoint(), - $command instanceof TagSubtree, - $command instanceof DisableNodeAggregate, - $command instanceof UntagSubtree, - $command instanceof EnableNodeAggregate, - $command instanceof RemoveNodeAggregate => - $command->coveredDimensionSpacePoint, - $command instanceof ChangeNodeAggregateType => - null, - $command instanceof CreateNodeVariant => - $command->targetOrigin->toDimensionSpacePoint(), - default => null - }; - - if ($dimensionSpacePoint === null) { - if ($nodeAggregateIdForDimensionFallback === null) { - return null; - } - - $nodeAggregate = $this->contentRepository->getContentGraph() - ->findNodeAggregateById( - $this->workspace->currentContentStreamId, - $nodeAggregateIdForDimensionFallback - ); - - if ($nodeAggregate) { - $dimensionSpacePoint = $this->extractValidDimensionSpacePointFromNodeAggregate( - $nodeAggregate - ); - - if ($dimensionSpacePoint === null) { - return null; - } - } - } - - return $this->contentRepository->getContentGraph()->getSubgraph( - $this->workspace->currentContentStreamId, - $dimensionSpacePoint, - VisibilityConstraints::withoutRestrictions() - ); - } - - private function extractValidDimensionSpacePointFromNodeAggregate( - NodeAggregate $nodeAggregate - ): ?DimensionSpacePoint { - $result = null; - - foreach ($nodeAggregate->coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { - if ($this->preferredDimensionSpacePoint?->equals($coveredDimensionSpacePoint)) { - return $coveredDimensionSpacePoint; - } - $result ??= $coveredDimensionSpacePoint; - } - - return $result; - } - - private function createIconLabelForNode(Node $node): IconLabel - { - $nodeType = $this->nodeTypeManager->getNodeType($node->nodeTypeName); - - return new IconLabel( - icon: $nodeType?->getConfiguration('ui.icon') ?? 'questionmark', - label: $node->getLabel() - ); - } - - private function createTypeOfChangeFromCommand( - CommandInterface $command - ): ?TypeOfChange { - return match (true) { - $command instanceof CreateNodeAggregateWithNode, - $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties, - $command instanceof CreateNodeVariant => - TypeOfChange::NODE_HAS_BEEN_CREATED, - $command instanceof SetNodeProperties, - $command instanceof SetSerializedNodeProperties, - $command instanceof SetNodeReferences, - $command instanceof SetSerializedNodeReferences, - $command instanceof TagSubtree, - $command instanceof DisableNodeAggregate, - $command instanceof UntagSubtree, - $command instanceof EnableNodeAggregate, - $command instanceof ChangeNodeAggregateType => - TypeOfChange::NODE_HAS_BEEN_CHANGED, - $command instanceof MoveNodeAggregate => - TypeOfChange::NODE_HAS_BEEN_MOVED, - $command instanceof RemoveNodeAggregate => - TypeOfChange::NODE_HAS_BEEN_DELETED, - default => null - }; - } - - private function createReasonForConflictFromException( - \Throwable $exception - ): ?ReasonForConflict { - return match ($exception::class) { - NodeAggregateCurrentlyDoesNotExist::class => - ReasonForConflict::NODE_HAS_BEEN_DELETED, - default => null - }; - } } diff --git a/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php b/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php index 17530ec912..8f2fb7226f 100644 --- a/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php +++ b/Classes/Application/SyncWorkspace/SyncWorkspaceCommandHandler.php @@ -18,8 +18,8 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Workspace\WorkspaceProvider; -use Neos\Neos\Ui\Application\Shared\Conflicts; use Neos\Neos\Ui\Application\Shared\ConflictsOccurred; +use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory; /** * The application layer level command handler to for rebasing the workspace @@ -48,19 +48,15 @@ public function handle( return new SyncingSucceeded(); } catch (WorkspaceRebaseFailed $e) { - $conflictsBuilder = Conflicts::builder( + $conflictsFactory = new ConflictsFactory( contentRepository: $this->contentRepositoryRegistry ->get($command->contentRepositoryId), workspaceName: $command->workspaceName, preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint ); - foreach ($e->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { - $conflictsBuilder->addCommandThatFailedDuringRebase($commandThatFailedDuringRebase); - } - return new ConflictsOccurred( - conflicts: $conflictsBuilder->build() + conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e) ); } } diff --git a/Classes/Infrastructure/ContentRepository/ConflictsFactory.php b/Classes/Infrastructure/ContentRepository/ConflictsFactory.php new file mode 100644 index 0000000000..c5cbe58500 --- /dev/null +++ b/Classes/Infrastructure/ContentRepository/ConflictsFactory.php @@ -0,0 +1,280 @@ +nodeTypeManager = $contentRepository->getNodeTypeManager(); + + $this->workspace = $contentRepository->getWorkspaceFinder() + ->findOneByName($workspaceName); + } + + public function fromWorkspaceRebaseFailed( + WorkspaceRebaseFailed $workspaceRebaseFailed + ): Conflicts { + $conflictsBuilder = Conflicts::builder(); + + foreach ($workspaceRebaseFailed->commandsThatFailedDuringRebase as $commandThatFailedDuringRebase) { + $conflict = $this->createConflictFromCommandThatFailedDuringRebase($commandThatFailedDuringRebase); + $conflictsBuilder->addConflict($conflict); + } + + return $conflictsBuilder->build(); + } + + private function createConflictFromCommandThatFailedDuringRebase( + CommandThatFailedDuringRebase $commandThatFailedDuringRebase + ): Conflict { + $nodeAggregateId = $this->extractNodeAggregateIdFromCommand( + $commandThatFailedDuringRebase->command + ); + $subgraph = $this->acquireSubgraphFromCommand( + $commandThatFailedDuringRebase->command, + $nodeAggregateId + ); + $affectedSite = $nodeAggregateId + ? $subgraph->findClosestNode( + $nodeAggregateId, + FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_SITE) + ) + : null; + $affectedDocument = $nodeAggregateId + ? $subgraph->findClosestNode( + $nodeAggregateId, + FindClosestNodeFilter::create(nodeTypes: NodeTypeNameFactory::NAME_DOCUMENT) + ) + : null; + $affectedNode = $nodeAggregateId + ? $subgraph?->findNodeById($nodeAggregateId) + : null; + + return new Conflict( + key: $affectedNode + ? $affectedNode->nodeAggregateId->value + : 'command-' . $commandThatFailedDuringRebase->sequenceNumber, + affectedSite: $affectedSite + ? $this->createIconLabelForNode($affectedSite) + : null, + affectedDocument: $affectedDocument + ? $this->createIconLabelForNode($affectedDocument) + : null, + affectedNode: $affectedNode + ? $this->createIconLabelForNode($affectedNode) + : null, + typeOfChange: $this->createTypeOfChangeFromCommand( + $commandThatFailedDuringRebase->command + ), + reasonForConflict: $this->createReasonForConflictFromException( + $commandThatFailedDuringRebase->exception + ) + ); + } + + private function extractNodeAggregateIdFromCommand(CommandInterface $command): ?NodeAggregateId + { + return match (true) { + $command instanceof MoveNodeAggregate, + $command instanceof SetNodeProperties, + $command instanceof SetSerializedNodeProperties, + $command instanceof CreateNodeAggregateWithNode, + $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties, + $command instanceof TagSubtree, + $command instanceof DisableNodeAggregate, + $command instanceof UntagSubtree, + $command instanceof EnableNodeAggregate, + $command instanceof RemoveNodeAggregate, + $command instanceof ChangeNodeAggregateType, + $command instanceof CreateNodeVariant => + $command->nodeAggregateId, + $command instanceof SetNodeReferences, + $command instanceof SetSerializedNodeReferences => + $command->sourceNodeAggregateId, + default => null + }; + } + + private function acquireSubgraphFromCommand( + CommandInterface $command, + ?NodeAggregateId $nodeAggregateIdForDimensionFallback + ): ?ContentSubgraphInterface { + if ($this->workspace === null) { + return null; + } + + $dimensionSpacePoint = match (true) { + $command instanceof MoveNodeAggregate => + $command->dimensionSpacePoint, + $command instanceof SetNodeProperties, + $command instanceof SetSerializedNodeProperties, + $command instanceof CreateNodeAggregateWithNode, + $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties => + $command->originDimensionSpacePoint->toDimensionSpacePoint(), + $command instanceof SetNodeReferences, + $command instanceof SetSerializedNodeReferences => + $command->sourceOriginDimensionSpacePoint->toDimensionSpacePoint(), + $command instanceof TagSubtree, + $command instanceof DisableNodeAggregate, + $command instanceof UntagSubtree, + $command instanceof EnableNodeAggregate, + $command instanceof RemoveNodeAggregate => + $command->coveredDimensionSpacePoint, + $command instanceof ChangeNodeAggregateType => + null, + $command instanceof CreateNodeVariant => + $command->targetOrigin->toDimensionSpacePoint(), + default => null + }; + + if ($dimensionSpacePoint === null) { + if ($nodeAggregateIdForDimensionFallback === null) { + return null; + } + + $nodeAggregate = $this->contentRepository->getContentGraph() + ->findNodeAggregateById( + $this->workspace->currentContentStreamId, + $nodeAggregateIdForDimensionFallback + ); + + if ($nodeAggregate) { + $dimensionSpacePoint = $this->extractValidDimensionSpacePointFromNodeAggregate( + $nodeAggregate + ); + + if ($dimensionSpacePoint === null) { + return null; + } + } + } + + return $this->contentRepository->getContentGraph()->getSubgraph( + $this->workspace->currentContentStreamId, + $dimensionSpacePoint, + VisibilityConstraints::withoutRestrictions() + ); + } + + private function extractValidDimensionSpacePointFromNodeAggregate( + NodeAggregate $nodeAggregate + ): ?DimensionSpacePoint { + $result = null; + + foreach ($nodeAggregate->coveredDimensionSpacePoints as $coveredDimensionSpacePoint) { + if ($this->preferredDimensionSpacePoint?->equals($coveredDimensionSpacePoint)) { + return $coveredDimensionSpacePoint; + } + $result ??= $coveredDimensionSpacePoint; + } + + return $result; + } + + private function createIconLabelForNode(Node $node): IconLabel + { + $nodeType = $this->nodeTypeManager->getNodeType($node->nodeTypeName); + + return new IconLabel( + icon: $nodeType?->getConfiguration('ui.icon') ?? 'questionmark', + label: $node->getLabel() + ); + } + + private function createTypeOfChangeFromCommand( + CommandInterface $command + ): ?TypeOfChange { + return match (true) { + $command instanceof CreateNodeAggregateWithNode, + $command instanceof CreateNodeAggregateWithNodeAndSerializedProperties, + $command instanceof CreateNodeVariant => + TypeOfChange::NODE_HAS_BEEN_CREATED, + $command instanceof SetNodeProperties, + $command instanceof SetSerializedNodeProperties, + $command instanceof SetNodeReferences, + $command instanceof SetSerializedNodeReferences, + $command instanceof TagSubtree, + $command instanceof DisableNodeAggregate, + $command instanceof UntagSubtree, + $command instanceof EnableNodeAggregate, + $command instanceof ChangeNodeAggregateType => + TypeOfChange::NODE_HAS_BEEN_CHANGED, + $command instanceof MoveNodeAggregate => + TypeOfChange::NODE_HAS_BEEN_MOVED, + $command instanceof RemoveNodeAggregate => + TypeOfChange::NODE_HAS_BEEN_DELETED, + default => null + }; + } + + private function createReasonForConflictFromException( + \Throwable $exception + ): ?ReasonForConflict { + return match ($exception::class) { + NodeAggregateCurrentlyDoesNotExist::class => + ReasonForConflict::NODE_HAS_BEEN_DELETED, + default => null + }; + } +}