Skip to content

Commit

Permalink
Merge pull request #5384 from dlubitz/feature/90/cleanup-change-proje…
Browse files Browse the repository at this point in the history
…ction-after-publish

FEATURE: Cleanup change projection after publish and discard
  • Loading branch information
kitsunet authored Dec 4, 2024
2 parents 1fa7ae3 + 81242dd commit a897790
Show file tree
Hide file tree
Showing 35 changed files with 3,382 additions and 3 deletions.
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.");
}

/**
* @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

0 comments on commit a897790

Please sign in to comment.