Skip to content

Commit

Permalink
Merge pull request neos#5256 from dlubitz/90/feature/update-root-node…
Browse files Browse the repository at this point in the history
…-aggregation-dimensions-transformation

FEATURE: Add UpdateRootNodeAggregateDimensions as transformation
  • Loading branch information
skurfuerst authored Sep 19, 2024
2 parents 6058670 + 4a31b0a commit a6b3fb6
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
@contentrepository @adapters=DoctrineDBAL
Feature: Update root node aggregate dimensions

Creates empty root node aggregate dimensions for each allowed dimension combination and removes them for all non-configured ones.

Background:
########################
# SETUP
########################
Given using the following content dimensions:
| Identifier | Values | Generalizations |
| language | mul, de, en, ch | ch->de->mul, en->mul |
And using the following node types:
"""yaml
'Neos.ContentRepository:Root':
constraints:
nodeTypes:
'Neos.ContentRepository.Testing:Document': true
'Neos.ContentRepository.Testing:OtherDocument': true
'Neos.ContentRepository.Testing:Document': []
'Neos.ContentRepository.Testing:OtherDocument': []
"""
And using identifier "default", I define a content repository
And I am in content repository "default"
And the command CreateRootWorkspace is executed with payload:
| Key | Value |
| workspaceName | "live" |
| workspaceTitle | "Live" |
| workspaceDescription | "The live workspace" |
| newContentStreamId | "cs-identifier" |
And I am in workspace "live"
And the command CreateRootNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

Scenario: Run migration after adding a new dimension value
# we change the dimension configuration
Given I change the content dimensions in content repository "default" to:
| Identifier | Values | Generalizations |
| language | mul, de, en, ch, fr | ch->de->mul, en->mul |

When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs", without publishing on success:
"""yaml
migration:
-
transformations:
-
type: 'UpdateRootNodeAggregateDimensions'
settings:
nodeType: 'Neos.ContentRepository:Root'
"""

When I am in workspace "live"
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de"},{"language":"en"},{"language":"ch"}]

When I am in workspace "migration-workspace" and dimension space point {"language": "fr"}
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de"},{"language":"en"},{"language":"ch"},{"language":"fr"}]

Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node migration-cs;lady-eleonode-rootford;{}

When I run integrity violation detection
Then I expect the integrity violation detection result to contain exactly 0 errors

Scenario: Run migration after removing a new dimension value
# we change the dimension configuration
Given I change the content dimensions in content repository "default" to:
| Identifier | Values | Generalizations |
| language | mul, de, ch | ch->de->mul |

When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs", without publishing on success:
"""yaml
migration:
-
transformations:
-
type: 'UpdateRootNodeAggregateDimensions'
settings:
nodeType: 'Neos.ContentRepository:Root'
"""

When I am in workspace "live"
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de"},{"language":"en"},{"language":"ch"}]

When I am in workspace "migration-workspace" and dimension space point {"language": "en"}
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de"},{"language":"ch"}]

Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to no node

When I run integrity violation detection
Then I expect the integrity violation detection result to contain exactly 0 errors

Scenario: Run migration after renaming a new dimension value
# we change the dimension configuration
Given I change the content dimensions in content repository "default" to:
| Identifier | Values | Generalizations |
| language | mul, de_DE, en, ch | ch->de_DE->mul, en->mul |

When I run the following node migration for workspace "live", creating target workspace "migration-workspace" on contentStreamId "migration-cs", without publishing on success:
"""yaml
migration:
-
transformations:
-
type: 'UpdateRootNodeAggregateDimensions'
settings:
nodeType: 'Neos.ContentRepository:Root'
"""

When I am in workspace "live"
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de"},{"language":"en"},{"language":"ch"}]

When I am in workspace "migration-workspace" and dimension space point {"language": "de_DE"}
Then I expect the node aggregate "lady-eleonode-rootford" to exist
And I expect this node aggregate to occupy dimension space points [{}]
And I expect this node aggregate to cover dimension space points [{"language":"mul"},{"language":"de_DE"},{"language":"en"},{"language":"ch"}]

Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to node migration-cs;lady-eleonode-rootford;{}

When I run integrity violation detection
Then I expect the integrity violation detection result to contain exactly 0 errors

Scenario: Without migration, creating new nodeaggregates in new dimensionspacepoint will fail
# we change the dimension configuration
Given I change the content dimensions in content repository "default" to:
| Identifier | Values | Generalizations |
| language | mul, de, en, ch, fr | ch->de->mul, en->mul |

When I am in workspace "live" and dimension space point {"language": "fr"}
And the command CreateNodeAggregateWithNode is executed with payload and exceptions are caught:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
| originDimensionSpacePoint | {"language": "fr"} |
| parentNodeAggregateId | "lady-eleonode-rootford" |
Then the last command should have thrown an exception of type "NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint"

Then I expect node aggregate identifier "lady-eleonode-rootford" to lead to no node

When I run integrity violation detection
Then I expect the integrity violation detection result to contain exactly 0 errors
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Neos\ContentRepository\NodeMigration\Transformation\RenamePropertyTransformationFactory;
use Neos\ContentRepository\NodeMigration\Transformation\StripTagsOnPropertyTransformationFactory;
use Neos\ContentRepository\NodeMigration\Transformation\TransformationsFactory;
use Neos\ContentRepository\NodeMigration\Transformation\UpdateRootNodeAggregateDimensionsTransformationFactory;

/**
* @implements ContentRepositoryServiceFactoryInterface<NodeMigrationService>
Expand All @@ -49,6 +50,7 @@ public function build(ContentRepositoryServiceFactoryDependencies $serviceFactor
$transformationsFactory->registerTransformation('RenameNodeAggregate', new RenameNodeAggregateTransformationFactory());
$transformationsFactory->registerTransformation('RenameProperty', new RenamePropertyTransformationFactory());
$transformationsFactory->registerTransformation('StripTagsOnProperty', new StripTagsOnPropertyTransformationFactory());
$transformationsFactory->registerTransformation('UpdateRootNodeAggregateDimensions', new UpdateRootNodeAggregateDimensionsTransformationFactory());

return new NodeMigrationService(
$serviceFactoryDependencies->contentRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types=1);

namespace Neos\ContentRepository\NodeMigration\Transformation;

use Neos\ContentRepository\Core\CommandHandler\CommandResult;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\UpdateRootNodeAggregateDimensions;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepository\NodeMigration\MigrationException;

class UpdateRootNodeAggregateDimensionsTransformationFactory implements TransformationFactoryInterface
{
/**
* @param array<string,string> $settings
*/
public function build(
array $settings,
ContentRepository $contentRepository
): GlobalTransformationInterface|NodeAggregateBasedTransformationInterface|NodeBasedTransformationInterface {
if (!isset($settings['nodeType'])) {
throw new MigrationException(
'The "nodeType" must not be empty.',
1726754800
);

}
try {
$nodeTypeName = NodeTypeName::fromString($settings['nodeType']);
} catch (\InvalidArgumentException $exception) {
throw new MigrationException(
sprintf('The given "nodeType" ("%s") is not valid.', $settings['nodeType']),
1726754273
);
}
return new class (
$nodeTypeName,
$contentRepository
) implements GlobalTransformationInterface {
public function __construct(
private readonly NodeTypeName $nodeTypeName,
private readonly ContentRepository $contentRepository,
) {
}

public function execute(
WorkspaceName $workspaceNameForWriting,
): CommandResult {

$rootNodeAggregate = $this->contentRepository->getContentGraph($workspaceNameForWriting)->findRootNodeAggregateByType($this->nodeTypeName);

if (!$rootNodeAggregate) {
throw new MigrationException(
sprintf('There is no root node with the given "nodeType" ("%s") in the content repository.', $this->nodeTypeName->value),
1726754019
);
}

return $this->contentRepository->handle(
UpdateRootNodeAggregateDimensions::create(
$workspaceNameForWriting,
$rootNodeAggregate->nodeAggregateId
)
);
}
};
}
}
12 changes: 12 additions & 0 deletions Neos.Neos/Documentation/References/NodeMigrations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The Content Repository comes with a number of common transformations:
- ``RenameNodeAggregate``
- ``RenameProperty``
- ``StripTagsOnProperty``
- ``UpdateRootNodeAggregateDimensions``

They all implement the ``Neos\ContentRepository\NodeMigration\Transformation\TransformationFactoryInterface``. Custom transformations
can be developed against that interface as well, just use the fully qualified class name for those when specifying
Expand Down Expand Up @@ -262,6 +263,17 @@ Options Reference:
``property`` (string)
The name of the property to work on.

UpdateRootNodeAggregateDimensions
~~~~~~~~~~~~~

Updates all root node aggregate dimensions regarding the current content repository configuration.

Creates empty root node aggregate dimensions for each allowed dimension combination and removes them for all non-configured ones.

Options Reference:

``nodeType`` (string)
The node type name of the root node. For Neos this is usually "Neos.Neos:Sites"


Filters Reference
Expand Down

0 comments on commit a6b3fb6

Please sign in to comment.