Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/centralise rebaseable command enrich with command #5356

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -202,14 +202,14 @@ Feature: Create node aggregate with node
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

Given the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
Given the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:NodeWithoutTetheredChildNodes" |
| originDimensionSpacePoint | {} |
| parentNodeAggregateId | "lady-eleonode-rootford" |
| nodeName | "node" |
And the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
And the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-nodeward-nodington-iii" |
| nodeTypeName | "Neos.ContentRepository.Testing:NodeWithoutTetheredChildNodes" |
Expand Down Expand Up @@ -280,7 +280,7 @@ Feature: Create node aggregate with node
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:NodeWithTetheredChildNodes" |
Expand Down Expand Up @@ -459,7 +459,7 @@ Feature: Create node aggregate with node
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:NodeWithTetheredChildNodes" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Feature: Add New Property
properties:
text:
type: string
dateTime:
type: DateTime
"""
And using identifier "default", I define a content repository
And I am in content repository "default"
Expand Down Expand Up @@ -64,7 +66,7 @@ Feature: Add New Property
-
type: 'AddNewProperty'
settings:
newPropertyName: 'aDateOutsideSchema'
newPropertyName: 'dateTime'
serializedValue: '2013-09-09T12:04:12+00:00'
type: 'DateTime'
"""
Expand All @@ -82,6 +84,46 @@ Feature: Add New Property
| text | "Original text" |
Then I expect a node identified by migration-cs;other;{} to exist in the content graph
And I expect this node to have the following properties:
| Key | Value |
| text | "fixed value" |
| aDateOutsideSchema | Date:2013-09-09T12:04:12+00:00 |
| Key | Value |
| text | "fixed value" |
| dateTime | Date:2013-09-09T12:04:12+00:00 |

Scenario: Adding a property that is not defined in the node type schema
When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs" and exceptions are caught:
"""yaml
migration:
-
filters:
-
type: 'NodeType'
settings:
nodeType: 'Neos.ContentRepository.Testing:Document'
transformations:
-
type: 'AddNewProperty'
settings:
newPropertyName: 'aDateOutsideSchema'
serializedValue: '2013-09-09T12:04:12+00:00'
type: 'DateTime'
"""
Then the last command should have thrown an exception of type "PropertyCannotBeSet"

Scenario: Adding a property with a different type than defined by the node type schema
When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs" and exceptions are caught:
"""yaml
migration:
-
filters:
-
type: 'NodeType'
settings:
nodeType: 'Neos.ContentRepository.Testing:Document'
transformations:
-
type: 'AddNewProperty'
settings:
newPropertyName: 'dateTime'
serializedValue: '2013-09-09T12:04:12+00:00'
type: 'string'
"""
Then the last command should have thrown an exception of type "PropertyCannotBeSet"
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Feature: Disable a node aggregate
| affectedOccupiedDimensionSpacePoints | [{}] |
| affectedCoveredDimensionSpacePoints | [{}] |

When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "nody-mc-nodeface" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ Feature: Remove NodeAggregate
| nodeTypeName | "Neos.ContentRepository:Root" |
# We have to add another node since root nodes are in all dimension space points and thus cannot be varied
# Node /document
And the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
And the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "nody-mc-nodeface" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
| parentNodeAggregateId | "lady-eleonode-nodesworth" |
| nodeName | "document" |
# We also want to add a child node to make sure it is correctly removed when the parent is removed
# Node /document/child-document
And the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
And the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "nodimus-prime" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Feature: Dimension mismatch

Scenario: Generalization detection
# Node /document
When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Feature: Properties
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |
# Node /document
When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Feature: Tethered Nodes Reordering Structure changes
| Key | Value |
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |
And the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
And the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ Feature: Rebasing auto-created nodes works
And I expect this node to be a child of node user-cs-identifier;nody-mc-nodeface;{}

# - then, for the auto-created child node, set a property.
When the command "SetSerializedNodeProperties" is executed with payload:
| Key | Value |
| workspaceName | "user-test" |
| nodeAggregateId | $this->currentNodeAggregateId |
| originDimensionSpacePoint | {} |
| propertyValues | {"text": {"value":"Modified","type":"string"}} |
| propertiesToUnset | {} |
When the command "SetNodeProperties" is executed with payload:
| Key | Value |
| workspaceName | "user-test" |
| nodeAggregateId | $this->currentNodeAggregateId |
| originDimensionSpacePoint | {} |
| propertyValues | {"text": "Modified"} |
| propertiesToUnset | {} |

# ensure that live is outdated so the rebase is required:
When the command CreateNodeAggregateWithNode is executed with payload:
Expand All @@ -80,8 +80,8 @@ Feature: Rebasing auto-created nodes works

# rebase of SetSerializedNodeProperties
When the command RebaseWorkspace is executed with payload:
| Key | Value |
| workspaceName | "user-test" |
| Key | Value |
| workspaceName | "user-test" |
| rebasedContentStreamId | "user-cs-rebased" |
# This should properly work; no error.

Original file line number Diff line number Diff line change
Expand Up @@ -346,15 +346,15 @@ Feature: Publishing hide/show scenario of nodes
| newContentStreamId | "user-cs-identifier" |

# SETUP: set two new nodes in USER workspace
When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| workspaceName | "user-test" |
| nodeAggregateId | "new1-agg" |
| nodeTypeName | "Neos.ContentRepository.Testing:Content" |
| originDimensionSpacePoint | {} |
| parentNodeAggregateId | "lady-eleonode-rootford" |
| nodeName | "foo" |
When the command CreateNodeAggregateWithNodeAndSerializedProperties is executed with payload:
When the command CreateNodeAggregateWithNode is executed with payload:
| Key | Value |
| workspaceName | "user-test" |
| nodeAggregateId | "new2-agg" |
Expand Down
50 changes: 46 additions & 4 deletions Neos.ContentRepository.Core/Classes/CommandHandler/CommandBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\EventStore\EventsToPublish;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;
use Neos\ContentRepository\Core\Feature\RebaseableCommand;

/**
* Implementation Detail of {@see ContentRepository::handle}, which does the command dispatching to the different
Expand All @@ -20,26 +22,66 @@
*/
private array $handlers;

/**
* @var CommandSerializerInterface[]
*/
private array $serializers;

public function __construct(
// todo pass $commandHandlingDependencies in each command handler instead of into the commandBus
private CommandHandlingDependencies $commandHandlingDependencies,
CommandHandlerInterface ...$handlers
) {
$this->handlers = $handlers;
$serializers = [];
foreach ($handlers as $handler) {
if ($handler instanceof CommandSerializerInterface) {
$serializers[] = $handler;
}
}
$this->serializers = $serializers;
}

/**
* @return EventsToPublish|\Generator<int, EventsToPublish>
*/
public function handle(CommandInterface $command): EventsToPublish|\Generator
public function handle(PublicCommandInterface|RebasableToOtherWorkspaceInterface $command): EventsToPublish|\Generator
{
$possiblySerializedCommand = $command;
if (!$command instanceof RebasableToOtherWorkspaceInterface) {
foreach ($this->serializers as $serializer) {
if ($serializer->canSerialize($command)) {
$possiblySerializedCommand = $serializer->serialize($command, $this->commandHandlingDependencies);
break;
}
}
}

// multiple handlers must not handle the same command
foreach ($this->handlers as $handler) {
if ($handler->canHandle($command)) {
return $handler->handle($command, $this->commandHandlingDependencies);
if ($handler->canHandle($possiblySerializedCommand)) {
$eventsToPublish = $handler->handle($possiblySerializedCommand, $this->commandHandlingDependencies);

if (!$eventsToPublish instanceof EventsToPublish) {
// generator todo?
return $eventsToPublish;
}

if ($possiblySerializedCommand instanceof RebasableToOtherWorkspaceInterface) {
return new EventsToPublish(
$eventsToPublish->streamName,
RebaseableCommand::enrichWithCommand(
$possiblySerializedCommand,
$eventsToPublish->events
),
$eventsToPublish->expectedVersion
);
}

return $eventsToPublish;
}
}
throw new \RuntimeException(sprintf('No handler found for Command "%s"', get_debug_type($command)), 1649582778);
throw new \RuntimeException(sprintf('No handler found for Command "%s"', get_debug_type($possiblySerializedCommand)), 1649582778);
}

public function withAdditionalHandlers(CommandHandlerInterface ...$handlers): self
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Neos\ContentRepository\Core\CommandHandler;

use Neos\ContentRepository\Core\EventStore\EventsToPublish;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;

/**
* Common interface for all Content Repository command handlers
Expand All @@ -15,7 +16,7 @@
*/
interface CommandHandlerInterface
{
public function canHandle(CommandInterface $command): bool;
public function canHandle(PublicCommandInterface|RebasableToOtherWorkspaceInterface $command): bool;

/**
* "simple" command handlers return EventsToPublish directly
Expand All @@ -25,5 +26,5 @@ public function canHandle(CommandInterface $command): bool;
*
* @return EventsToPublish|\Generator<int, EventsToPublish>
*/
public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator;
public function handle(PublicCommandInterface|RebasableToOtherWorkspaceInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\CommandHandler;

use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;

/**
* Optional interface for all content repository command serializer
*
* See {@see RebasableToOtherWorkspaceInterface} for more details regarding serialized commands.
*
* @internal no public API, because commands are no extension points of the CR
*/
interface CommandSerializerInterface
{
public function canSerialize(PublicCommandInterface $command): bool;

public function serialize(PublicCommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): RebasableToOtherWorkspaceInterface;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\CommandHandler;

/**
* Common (marker) interface for all api commands of the content repository
*
* @internal sealed interface. Custom commands cannot be handled and are no extension point!
*/
interface PublicCommandInterface
{
}
6 changes: 3 additions & 3 deletions Neos.ContentRepository.Core/Classes/ContentRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace Neos\ContentRepository\Core;

use Neos\ContentRepository\Core\CommandHandler\CommandBus;
use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\CommandHandler\PublicCommandInterface;
use Neos\ContentRepository\Core\Dimension\ContentDimensionSourceInterface;
use Neos\ContentRepository\Core\DimensionSpace\InterDimensionalVariationGraph;
use Neos\ContentRepository\Core\EventStore\EventNormalizer;
Expand Down Expand Up @@ -90,9 +90,9 @@ public function __construct(
/**
* The only API to send commands (mutation intentions) to the system.
*
* @param CommandInterface $command
* @param PublicCommandInterface $command
*/
public function handle(CommandInterface $command): void
public function handle(PublicCommandInterface $command): void
{
// the commands only calculate which events they want to have published, but do not do the
// publishing themselves
Expand Down
Loading
Loading