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

FEATURE: Cleanup change projection after publish and discard #5384

Merged
Merged
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
23 changes: 23 additions & 0 deletions Neos.Neos/Classes/PendingChangesProjection/ChangeProjection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
use Neos\ContentRepository\Core\EventStore\EventInterface;
use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Event\ContentStreamWasRemoved;
use Neos\ContentRepository\Core\Feature\DimensionSpaceAdjustment\Event\DimensionSpacePointWasMoved;
use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated;
use Neos\ContentRepository\Core\Feature\NodeModification\Event\NodePropertiesWereSet;
Expand Down Expand Up @@ -169,6 +170,7 @@ public function canHandle(EventInterface $event): bool
NodePeerVariantWasCreated::class,
NodeAggregateTypeWasChanged::class,
NodeAggregateNameWasChanged::class,
ContentStreamWasRemoved::class,
]);
}

Expand All @@ -188,6 +190,7 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void
NodePeerVariantWasCreated::class => $this->whenNodePeerVariantWasCreated($event),
NodeAggregateTypeWasChanged::class => $this->whenNodeAggregateTypeWasChanged($event),
NodeAggregateNameWasChanged::class => $this->whenNodeAggregateNameWasChanged($event),
ContentStreamWasRemoved::class => $this->whenContentStreamWasRemoved($event),
default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))),
};
}
Expand Down Expand Up @@ -429,6 +432,11 @@ private function whenNodeAggregateNameWasChanged(NodeAggregateNameWasChanged $ev
);
}

private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event): void
{
$this->removeChangesForContentStreamId($event->contentStreamId);
}

private function markAsChanged(
ContentStreamId $contentStreamId,
NodeAggregateId $nodeAggregateId,
Expand Down Expand Up @@ -562,4 +570,19 @@ private function getChangeForAggregate(

return $changeRow ? Change::fromDatabaseRow($changeRow) : null;
}

private function removeChangesForContentStreamId(ContentStreamId $contentStreamId): void
{
$statement = <<<SQL
DELETE FROM {$this->tableNamePrefix}
WHERE
contentStreamId = :contentStreamId
SQL;
$this->dbal->executeStatement(
$statement,
[
'contentStreamId' => $contentStreamId->value,
]
);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Discard workspace without dimensions
Feature: Discard workspace without dimensions

Background:
Given using no content dimensions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Publish nodes partially without dimensions
Feature: Discard nodes partially without dimensions

Background:
Given using no content dimensions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Publish nodes partially with dimensions
Feature: Discard nodes partially with dimensions

Background:
Given using the following content dimensions:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);

/*
* This file is part of the Neos.Neos package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/


use Behat\Gherkin\Node\TableNode;
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\Neos\PendingChangesProjection\ChangeFinder;
use PHPUnit\Framework\Assert;

/**
* Step implementations for tests inside Neos.Neos
*
* @internal only for behat tests within the Neos.Neos package
*/
trait ChangeProjectionTrait
{
/**
* @template T of object
* @param class-string<T> $className
*
* @return T
*/
abstract private function getObject(string $className): object;

/**
* @Then I expect the ChangeProjection to have the following changes in :contentStreamId:
*/
public function iExpectTheChangeProjectionToHaveTheFollowingChangesInContentStream(TableNode $table, string $contentStreamId)
{
$changeFinder = $this->currentContentRepository->projectionState(ChangeFinder::class);
$changes = $changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId));

$tableRows = $table->getHash();
foreach ($changes as $change) {
foreach ($tableRows as $tableRowIndex => $tableRow) {
if (!$change->nodeAggregateId->equals(NodeAggregateId::fromString($tableRow['nodeAggregateId']))
|| $change->created !== (bool)$tableRow['created']
|| $change->deleted !== (bool)$tableRow['deleted']
|| $change->changed !== (bool)$tableRow['changed']
|| $change->moved !== (bool)$tableRow['moved']
|| (
($change->originDimensionSpacePoint === null && strtolower($tableRow['originDimensionSpacePoint']) !== "null")
&&
($change->originDimensionSpacePoint !== null && strtolower($tableRow['originDimensionSpacePoint']) !== "null" && !$change->originDimensionSpacePoint->equals(DimensionSpacePoint::fromJsonString($tableRow['originDimensionSpacePoint'])))
)
) {
continue;
}
unset($tableRows[$tableRowIndex]);
continue 2;
}
}

if (count($tableRows) !== 0) {
$tableHeader = array_combine(array_values($table->getRow(0)), array_values($table->getRow(0)));
$tableRemain = $tableRows;
array_unshift($tableRemain, $tableHeader);

Assert::assertEmpty($tableRows, "Not all given changes where found." . PHP_EOL . (new TableNode($tableRemain))->getTableAsString());
}
Assert::assertSame(count($table->getHash()), $changes->count(), "More changes found as given.");
dlubitz marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @Then I expect the ChangeProjection to have no changes in :contentStreamId
*/
public function iExpectTheChangeProjectionToHaveNoChangesInContentStream(string $contentStreamId)
{
$changeFinder = $this->currentContentRepository->projectionState(ChangeFinder::class);
$changes = $changeFinder->findByContentStreamId(ContentStreamId::fromString($contentStreamId));

Assert::assertSame(0, $changes->count(), "No changes expected.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class FeatureContext implements BehatContext
use ContentCacheTrait;
use AssetUsageTrait;
use AssetTrait;
use ChangeProjectionTrait;

use WorkspaceServiceTrait;
use ContentRepositorySecurityTrait;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Create node aggregate with node without dimensions

Background:
Given using no content dimensions
And using the following node types:
"""yaml
'Neos.ContentRepository.Testing:Node':
properties:
text:
type: string
"""
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 I am in dimension space point {}
And I am user identified by "initiating-user-identifier"
And the command CreateRootNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

When the command CreateWorkspace is executed with payload:
| Key | Value |
| workspaceName | "user-workspace" |
| baseWorkspaceName | "live" |
| newContentStreamId | "user-cs-id" |

Scenario: Nodes on live workspace have been created
Given I am in workspace "live"

And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} |
| nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} |
| sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington III"} |

Then I expect the ChangeProjection to have no changes in "cs-identifier"


Scenario: Nodes on user workspace have been created
Given I am in workspace "user-workspace"

And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} |
| nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} |
| sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington III"} |

Then I expect the ChangeProjection to have the following changes in "user-cs-id":
| nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint |
| sir-david-nodenborough | 1 | 1 | 0 | 0 | {} |
| nody-mc-nodeface | 1 | 1 | 0 | 0 | {} |
| sir-nodeward-nodington-iii | 1 | 1 | 0 | 0 | {} |

And I expect the ChangeProjection to have no changes in "cs-identifier"
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Create node aggregate with node with dimensions

Background:
Given using the following content dimensions:
| Identifier | Values | Generalizations |
| language | de,gsw,fr | gsw->de, fr |
And using the following node types:
"""yaml
'Neos.ContentRepository.Testing:Node':
properties:
text:
type: string
"""
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 I am in dimension space point {"language": "de"}
And I am user identified by "initiating-user-identifier"
And the command CreateRootNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

When the command CreateWorkspace is executed with payload:
| Key | Value |
| workspaceName | "user-workspace" |
| baseWorkspaceName | "live" |
| newContentStreamId | "user-cs-id" |

Scenario: Nodes on live workspace have been created
Given I am in workspace "live"

And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} |
| nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} |

Then I am in dimension space point {"language": "fr"}
And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a french text about Sir Nodeward Nodington III"} |

Then I expect the ChangeProjection to have no changes in "cs-identifier"


Scenario: Nodes on user workspace have been created
Given I am in workspace "user-workspace"

And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} |
| nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} |
| sir-nodeward-nodington-iv | bakura | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington IV"} |

Then I am in dimension space point {"language": "fr"}
And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a extended text about Sir Nodeward Nodington III"} |

Then I expect the ChangeProjection to have the following changes in "user-cs-id":
| nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint |
| sir-david-nodenborough | 1 | 1 | 0 | 0 | {"language":"de"} |
| nody-mc-nodeface | 1 | 1 | 0 | 0 | {"language":"de"} |
| sir-nodeward-nodington-iv | 1 | 1 | 0 | 0 | {"language":"de"} |
| sir-nodeward-nodington-iii | 1 | 1 | 0 | 0 | {"language":"fr"} |
And I expect the ChangeProjection to have no changes in "cs-identifier"
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
@contentrepository @adapters=DoctrineDBAL
@flowEntities
Feature: Create node generalization variant

Background:
Given using the following content dimensions:
| Identifier | Values | Generalizations |
| language | de,gsw,fr,en | gsw->de->en, fr |
And using the following node types:
"""yaml
'Neos.ContentRepository.Testing:Node':
properties:
text:
type: string

"""
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 I am in dimension space point {"language": "de"}
And I am user identified by "initiating-user-identifier"
And the command CreateRootNodeAggregateWithNode is executed with payload:
| Key | Value |
| nodeAggregateId | "lady-eleonode-rootford" |
| nodeTypeName | "Neos.ContentRepository:Root" |

And the following CreateNodeAggregateWithNode commands are executed:
| nodeAggregateId | nodeName | parentNodeAggregateId | nodeTypeName | initialPropertyValues |
| sir-david-nodenborough | node | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {} |
| nody-mc-nodeface | child-node | sir-david-nodenborough | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Nody Mc Nodeface"} |
| sir-nodeward-nodington-iii | esquire | lady-eleonode-rootford | Neos.ContentRepository.Testing:Node | {"text": "This is a text about Sir Nodeward Nodington III"} |

And the command CreateWorkspace is executed with payload:
| Key | Value |
| workspaceName | "user-workspace" |
| baseWorkspaceName | "live" |
| newContentStreamId | "user-cs-id" |

Scenario: Create node generalization variant of node with
When I am in workspace "user-workspace" and dimension space point {"language":"de"}
And the command CreateNodeVariant is executed with payload:
| Key | Value |
| nodeAggregateId | "sir-david-nodenborough" |
| sourceOrigin | {"language":"de"} |
| targetOrigin | {"language":"en"} |

Then I expect the ChangeProjection to have no changes in "cs-identifier"
Then I expect the ChangeProjection to have the following changes in "user-cs-id":
| nodeAggregateId | created | changed | moved | deleted | originDimensionSpacePoint |
| sir-david-nodenborough | 1 | 1 | 0 | 0 | {"language":"en"} |

And the command PublishWorkspace is executed with payload:
| Key | Value |
| workspaceName | "user-workspace" |
| newContentStreamId | "new-user-workspace-cs-id" |

Then I expect the ChangeProjection to have no changes in "cs-identifier"
Then I expect the ChangeProjection to have no changes in "user-cs-id"
Then I expect the ChangeProjection to have no changes in "new-user-workspace-cs-id"
Loading
Loading