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: Serializable Commands #5348

Merged
merged 21 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9714f47
WIP: PoC: Serializable Commands
bwaidelich Nov 7, 2024
b81032c
Merge branch '9.0' into task/serializable-commands
bwaidelich Nov 8, 2024
2ecfe5d
Fix node migration transformations and tests
bwaidelich Nov 8, 2024
38c0b9b
Merge branch '9.0' into task/serializable-commands
bwaidelich Nov 8, 2024
bd60fb8
Fine grained PHPStan ignores
bwaidelich Nov 8, 2024
35b02dc
Fix tests
bwaidelich Nov 8, 2024
c36073e
Avoid union type in `CommandThatFailedDuringRebase`
bwaidelich Nov 8, 2024
8e9b333
TASK: Add docs for Serialized Command
mhsdesign Nov 8, 2024
e9d576b
TASK: Remove `SerializedCommandInterface` again and use `RebasableToO…
mhsdesign Nov 8, 2024
ddf2a52
TASK: Adjust documentation of `CommandInterface`
mhsdesign Nov 8, 2024
d79fe10
TASK: Overhaul api of `CommandThatFailedDuringRebase`
mhsdesign Nov 9, 2024
43e9501
Merge pull request #5355 from mhsdesign/task/serializable-commands
mhsdesign Nov 9, 2024
d6ff6bb
TASK: Do not expose that `Command`'s failed during rebase
mhsdesign Nov 9, 2024
48ebbda
TASK: Rename `EventThatFailedDuringRebase` to `ConflictingEvent`
mhsdesign Nov 9, 2024
930266d
TASK: Remove hacky method again
mhsdesign Nov 11, 2024
fbbf138
Merge branch '9.0' into task/serializable-commands
bwaidelich Nov 12, 2024
ad9f66b
Merge branch '9.0' into task/serializable-commands
bwaidelich Nov 12, 2024
255b38e
TASK: Adjust changed `WorkspaceRebaseFailed`
mhsdesign Nov 12, 2024
dbbb7f1
TASK: Remove obsolete array declaration due to interface
mhsdesign Nov 12, 2024
9f9f946
Fix test
bwaidelich Nov 12, 2024
1aa0e04
Merge branch 'task/serializable-commands' of https://github.com/neos/…
bwaidelich Nov 12, 2024
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'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skurfuerst you had originally introduced this test case with 0f88ed6 but in our opinion node migrations should not be allowed to set properties that are not allowed according to the node type schema. Do you agree to that assumption?

newPropertyName: 'dateTime'
serializedValue: '2013-09-09T12:04:12+00:00'
type: 'DateTime'
bwaidelich marked this conversation as resolved.
Show resolved Hide resolved
"""
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
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function __construct(
/**
* @return EventsToPublish|\Generator<int, EventsToPublish>
*/
public function handle(CommandInterface $command): EventsToPublish|\Generator
public function handle(CommandInterface|SerializedCommandInterface $command): EventsToPublish|\Generator
{
// multiple handlers must not handle the same command
foreach ($this->handlers as $handler) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
interface CommandHandlerInterface
{
public function canHandle(CommandInterface $command): bool;
public function canHandle(CommandInterface|SerializedCommandInterface $command): bool;

/**
* "simple" command handlers return EventsToPublish directly
Expand All @@ -25,5 +25,5 @@ public function canHandle(CommandInterface $command): bool;
*
* @return EventsToPublish|\Generator<int, EventsToPublish>
*/
public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator;
public function handle(CommandInterface|SerializedCommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish|\Generator;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
namespace Neos\ContentRepository\Core\CommandHandler;

/**
* Common (marker) interface for all commands of the Content Repository
* Common (marker) interface for all commands of the content repository
*
* @internal because extra commands are no extension point
* @internal sealed interface. Custom commands cannot be handled and are no extension point!
*/
interface CommandInterface
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\Core\CommandHandler;

/**
* Common (marker) interface for all commands that need to be serialized for rebasing
*
* During a rebase, the command (either {@see CommandInterface} or this serialized counterpart) will be deserialized
* from array {@see SerializedCommandInterface::fromArray()} and reapplied {@see CommandSimulator}
*
* @internal
*/
interface SerializedCommandInterface
mhsdesign marked this conversation as resolved.
Show resolved Hide resolved
{
/**
* called during deserialization from metadata
* @param array<string,mixed> $array
*/
public static function fromArray(array $array): self;
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@
namespace Neos\ContentRepository\Core\Feature\Common;

use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\CommandHandler\SerializedCommandInterface;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
* This interface is implemented by **commands** which can be rebased to other Content Streams. This is basically all
* node-based commands.
* This interface is implemented by **commands** which can be rebased to other workspaces.
*
* Reminder: a rebase can fail, because the target content stream might contain conflicting changes.
*
* @internal used internally for the rebasing mechanism of content streams
*/
interface RebasableToOtherWorkspaceInterface extends CommandInterface
interface RebasableToOtherWorkspaceInterface
{
public function createCopyForWorkspace(
WorkspaceName $targetWorkspaceName,
): self;
): (RebasableToOtherWorkspaceInterface&CommandInterface)|(RebasableToOtherWorkspaceInterface&SerializedCommandInterface);
mhsdesign marked this conversation as resolved.
Show resolved Hide resolved

/**
* called during deserialization from metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Neos\ContentRepository\Core\CommandHandler\CommandHandlerInterface;
use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies;
use Neos\ContentRepository\Core\CommandHandler\SerializedCommandInterface;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\DimensionSpace\ContentDimensionZookeeper;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
Expand Down Expand Up @@ -48,12 +49,12 @@ public function __construct(
) {
}

public function canHandle(CommandInterface $command): bool
public function canHandle(CommandInterface|SerializedCommandInterface $command): bool
{
return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName());
}

public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish
public function handle(CommandInterface|SerializedCommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish
{
/** @phpstan-ignore-next-line */
return match ($command::class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Neos\ContentRepository\Core\CommandHandler\CommandHandlerInterface;
use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\CommandHandler\CommandHandlingDependencies;
use Neos\ContentRepository\Core\CommandHandler\SerializedCommandInterface;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\DimensionSpace;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet;
Expand Down Expand Up @@ -86,12 +87,12 @@ public function __construct(
) {
}

public function canHandle(CommandInterface $command): bool
public function canHandle(CommandInterface|SerializedCommandInterface $command): bool
{
return method_exists($this, 'handle' . (new \ReflectionClass($command))->getShortName());
}

public function handle(CommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish
public function handle(CommandInterface|SerializedCommandInterface $command, CommandHandlingDependencies $commandHandlingDependencies): EventsToPublish
{
/** @phpstan-ignore-next-line */
return match ($command::class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace Neos\ContentRepository\Core\Feature\NodeCreation\Command;

use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
use Neos\ContentRepository\Core\CommandHandler\SerializedCommandInterface;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\Feature\Common\MatchableWithNodeIdToPublishOrDiscardInterface;
use Neos\ContentRepository\Core\Feature\Common\RebasableToOtherWorkspaceInterface;
Expand All @@ -34,7 +34,7 @@
* @internal implementation detail, use {@see CreateNodeAggregateWithNode} instead.
*/
final readonly class CreateNodeAggregateWithNodeAndSerializedProperties implements
CommandInterface,
SerializedCommandInterface,
\JsonSerializable,
MatchableWithNodeIdToPublishOrDiscardInterface,
RebasableToOtherWorkspaceInterface
Expand Down
Loading
Loading