Skip to content

Commit

Permalink
5054 - WIP: Implement constraint checks for node variation with custo…
Browse files Browse the repository at this point in the history
…m destination
  • Loading branch information
Bernhard Schmitt committed Dec 15, 2024
1 parent ee6245f commit ab338d6
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@ Feature: Create node variant
tethered:
type: 'Neos.ContentRepository.Testing:Tethered'
'Neos.ContentRepository.Testing:Tethered': []
'Neos.ContentRepository.Testing:RestrictiveDocument':
constraints:
nodeTypes:
'Neos.ContentRepository.Testing:Document': false
childNodes:
tethered:
type: 'Neos.ContentRepository.Testing:Tethered'
constraints:
nodeTypes:
'Neos.ContentRepository.Testing:Document': false
"""
And using identifier "default", I define a content repository
And I am in content repository "default"
And I am user identified by "initiating-user-identifier"
And the command CreateRootWorkspace is executed with payload:
| Key | Value |
| workspaceName | "live" |
| newContentStreamId | "cs-identifier" |
| Key | Value |
| workspaceName | "live" |
| newContentStreamId | "cs-identifier" |
And I am in workspace "live" and dimension space point {"market":"DE", "language":"gsw"}
And the command CreateRootNodeAggregateWithNode is executed with payload:
| Key | Value |
Expand All @@ -36,6 +46,15 @@ Feature: Create node variant
# We have to add yet another node since we need test cases with a partially covering parent node
# Node /document/child
| nody-mc-nodeface | child | sir-david-nodenborough | Neos.ContentRepository.Testing:Document | {} |
And I am in workspace "live" and dimension space point {"market":"DE", "language":"de"}
# We have to add yet another node that could be varied but not to a different parent
And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName |
| polyglot-mc-nodeface | polyglot-child | lady-eleonode-rootford | Neos.ContentRepository.Testing:Document |
# ...and we have to add yet another node for node type constraint checks
And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | tetheredDescendantNodeAggregateIds |
| the-governode | governode | lady-eleonode-rootford | Neos.ContentRepository.Testing:RestrictiveDocument | {"tethered": "nodimer-tetherton"} |

Scenario: Try to create a variant in a workspace that does not exist
When the command CreateNodeVariant is executed with payload and exceptions are caught:
Expand Down Expand Up @@ -120,3 +139,82 @@ Feature: Create node variant
| sourceOrigin | {"market":"DE", "language":"gsw"} |
| targetOrigin | {"market":"DE", "language":"de"} |
Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint"

Scenario: Try to create a variant as a child of a different parent aggregate that does not exist
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "i-do-not-exist" |
Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist"

Scenario: Try to create a variant as a sibling of a non-existing succeeding sibling
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "nody-mc-nodeface" |
| succeedingSiblingNodeAggregateId | "i-do-not-exist" |
Then the last command should have thrown an exception of type "NodeAggregateCurrentlyDoesNotExist"

Scenario: Try to create a variant before a sibling which is not a child of the new parent
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "nody-mc-nodeface" |
| succeedingSiblingNodeAggregateId | "sir-david-nodenborough" |
Then the last command should have thrown an exception of type "NodeAggregateIsNoChild"

Scenario: Try to create a variant before a sibling which is none (no new parent case)
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| succeedingSiblingNodeAggregateId | "nody-mc-nodeface" |
Then the last command should have thrown an exception of type "NodeAggregateIsNoSibling"

Scenario: Try to create a variant as a child of a different parent aggregate that does not cover the requested DSP
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"CH", "language":"de"} |
| parentNodeAggregateId | "sir-david-nodenborough" |
Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint"

Scenario: Try to create a variant of a node having a name that is already taken by one of the variant's siblings
Given I am in workspace "live" and dimension space point {"market":"DE", "language":"gsw"}
And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName |
| evil-occupant | polyglot-child | nody-mc-nodeface | Neos.ContentRepository.Testing:Document |

When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "nody-mc-nodeface" |
Then the last command should have thrown an exception of type "NodeNameIsAlreadyCovered"

Scenario: Try to vary a node as a child of another parent whose node type does not allow child nodes of the variant's type
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "the-governode" |
Then the last command should have thrown an exception of type "NodeConstraintException"

Scenario: Try to vary a node as a child of another parent whose parent's node type does not allow grand child nodes of the variant's type
When the command CreateNodeVariant is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "polyglot-mc-nodeface" |
| sourceOrigin | {"market":"DE", "language":"de"} |
| targetOrigin | {"market":"DE", "language":"gsw"} |
| parentNodeAggregateId | "nodimer-tetherton" |
Then the last command should have thrown an exception of type "NodeConstraintException"
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,16 @@
* @param NodeAggregateId $nodeAggregateId The identifier of the affected node aggregate
* @param OriginDimensionSpacePoint $sourceOrigin Dimension Space Point from which the node is to be copied from
* @param OriginDimensionSpacePoint $targetOrigin Dimension Space Point to which the node is to be copied to
* @param ?NodeAggregateId $parentNodeAggregateId The optional id of the node aggregate to be used as the variant's parent
* @param ?NodeAggregateId $succeedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's succeeding sibling
*/
private function __construct(
public WorkspaceName $workspaceName,
public NodeAggregateId $nodeAggregateId,
public OriginDimensionSpacePoint $sourceOrigin,
public OriginDimensionSpacePoint $targetOrigin,
public ?NodeAggregateId $parentNodeAggregateId,
public ?NodeAggregateId $succeedingSiblingNodeAggregateId,
) {
}

Expand All @@ -51,10 +55,12 @@ private function __construct(
* @param NodeAggregateId $nodeAggregateId The identifier of the affected node aggregate
* @param OriginDimensionSpacePoint $sourceOrigin Dimension Space Point from which the node is to be copied from
* @param OriginDimensionSpacePoint $targetOrigin Dimension Space Point to which the node is to be copied to
* @param ?NodeAggregateId $parentNodeAggregateId The id of the node aggregate to be used as the variant's parent
* @param ?NodeAggregateId $succeedingSiblingNodeAggregateId The optional id of the node aggregate to be used as the variant's succeeding sibling
*/
public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin): self
public static function create(WorkspaceName $workspaceName, NodeAggregateId $nodeAggregateId, OriginDimensionSpacePoint $sourceOrigin, OriginDimensionSpacePoint $targetOrigin, ?NodeAggregateId $parentNodeAggregateId = null, ?NodeAggregateId $succeedingSiblingNodeAggregateId = null): self
{
return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin);
return new self($workspaceName, $nodeAggregateId, $sourceOrigin, $targetOrigin, $parentNodeAggregateId, $succeedingSiblingNodeAggregateId);
}

public static function fromArray(array $array): self
Expand All @@ -64,6 +70,12 @@ public static function fromArray(array $array): self
NodeAggregateId::fromString($array['nodeAggregateId']),
OriginDimensionSpacePoint::fromArray($array['sourceOrigin']),
OriginDimensionSpacePoint::fromArray($array['targetOrigin']),
array_key_exists('parentNodeAggregateId', $array)
? NodeAggregateId::fromString($array['parentNodeAggregateId'])
: null,
array_key_exists('succeedingSiblingNodeAggregateId', $array)
? NodeAggregateId::fromString($array['succeedingSiblingNodeAggregateId'])
: null,
);
}

Expand All @@ -83,6 +95,8 @@ public function createCopyForWorkspace(
$this->nodeAggregateId,
$this->sourceOrigin,
$this->targetOrigin,
$this->parentNodeAggregateId,
$this->succeedingSiblingNodeAggregateId,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,45 @@ private function handleCreateNodeVariant(
$this->requireNodeAggregateToBeUntethered($nodeAggregate);
$this->requireNodeAggregateToOccupyDimensionSpacePoint($nodeAggregate, $command->sourceOrigin);
$this->requireNodeAggregateToNotOccupyDimensionSpacePoint($nodeAggregate, $command->targetOrigin);
$parentNodeAggregate = $this->requireProjectedParentNodeAggregate(
$contentGraph,
$command->nodeAggregateId,
$command->sourceOrigin
);

if ($command->succeedingSiblingNodeAggregateId) {
$this->requireProjectedNodeAggregate($contentGraph, $command->succeedingSiblingNodeAggregateId);
}
if ($command->parentNodeAggregateId) {
$parentNodeAggregate = $this->requireProjectedNodeAggregate($contentGraph, $command->parentNodeAggregateId);
if ($command->succeedingSiblingNodeAggregateId) {
$this->requireNodeAggregateToBeChild(
$contentGraph,
$command->succeedingSiblingNodeAggregateId,
$command->parentNodeAggregateId,
$command->targetOrigin->toDimensionSpacePoint(),
);
}
if ($nodeAggregate->nodeName) {
$this->requireNodeNameToBeUncovered($contentGraph, $nodeAggregate->nodeName, $command->parentNodeAggregateId);
$this->requireNodeTypeNotToDeclareTetheredChildNodeName($parentNodeAggregate->nodeTypeName, $nodeAggregate->nodeName);
}
$this->requireConstraintsImposedByAncestorsAreMet(
$contentGraph,
$this->requireNodeType($nodeAggregate->nodeTypeName),
[$command->parentNodeAggregateId],
);
} else {
$parentNodeAggregate = $this->requireProjectedParentNodeAggregate(
$contentGraph,
$command->nodeAggregateId,
$command->sourceOrigin
);
if ($command->succeedingSiblingNodeAggregateId) {
$this->requireNodeAggregateToBeSibling(
$contentGraph,
$command->nodeAggregateId,
$command->succeedingSiblingNodeAggregateId,
$command->targetOrigin->toDimensionSpacePoint(),
);
}
}

$this->requireNodeAggregateToCoverDimensionSpacePoint(
$parentNodeAggregate,
$command->targetOrigin->toDimensionSpacePoint()
Expand Down

0 comments on commit ab338d6

Please sign in to comment.