Skip to content

Commit

Permalink
tweaks and more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
bwaidelich committed Mar 14, 2024
1 parent 6a8bc30 commit b33a774
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function iRemoveTheFollowingSubtreeTag(TableNode $payloadTable): void
$dataset = $this->transformPayloadTableToDataset($payloadTable);
$subtreeTagToRemove = SubtreeTag::fromString($dataset['subtreeTag']);
$record = $this->transformDatasetToHierarchyRelationRecord($dataset);
$subtreeTags = NodeFactory::extractSubtreeTagsWithInheritedFromJson($record['subtreetags']);
$subtreeTags = NodeFactory::extractNodeTagsFromJson($record['subtreetags']);
if (!$subtreeTags->contain($subtreeTagToRemove)) {
throw new \RuntimeException(sprintf('Failed to remove subtree tag "%s" because that tag is not set', $subtreeTagToRemove->value), 1708618267);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private function nodeTagsForNode(NodeAggregateId $nodeAggregateId, ContentStream
if (!is_string($subtreeTagsJson)) {
throw new \RuntimeException(sprintf('Failed to fetch SubtreeTags for node "%s" in content subgraph "%s@%s"', $nodeAggregateId->value, $dimensionSpacePoint->toJson(), $contentStreamId->value), 1698838865);
}
return NodeFactory::extractSubtreeTagsWithInheritedFromJson($subtreeTagsJson);
return NodeFactory::extractNodeTagsFromJson($subtreeTagsJson);
}

private function subtreeTagsForHierarchyRelation(ContentStreamId $contentStreamId, NodeRelationAnchorPoint $parentNodeAnchorPoint, DimensionSpacePoint $dimensionSpacePoint): NodeTags
Expand All @@ -231,7 +231,7 @@ private function subtreeTagsForHierarchyRelation(ContentStreamId $contentStreamI
if (!is_string($subtreeTagsJson)) {
throw new \RuntimeException(sprintf('Failed to fetch SubtreeTags for hierarchy parent anchor point "%s" in content subgraph "%s@%s"', $parentNodeAnchorPoint->value, $dimensionSpacePoint->toJson(), $contentStreamId->value), 1704199847);
}
return NodeFactory::extractSubtreeTagsWithInheritedFromJson($subtreeTagsJson);
return NodeFactory::extractNodeTagsFromJson($subtreeTagsJson);
}

abstract protected function getDatabaseConnection(): Connection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag;
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphIdentity;
use Neos\ContentRepository\Core\Projection\ContentGraph\DimensionSpacePointsBySubtreeTags;
use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes;
use Neos\ContentRepository\Core\Projection\ContentGraph\Reference;
use Neos\ContentRepository\Core\Projection\ContentGraph\References;
Expand Down Expand Up @@ -84,7 +85,7 @@ public function mapNodeRowToNode(
$nodeType,
$this->createPropertyCollectionFromJsonString($nodeRow['properties']),
isset($nodeRow['name']) ? NodeName::fromString($nodeRow['name']) : null,
self::extractSubtreeTagsWithInheritedFromJson($nodeRow['subtreetags']),
self::extractNodeTagsFromJson($nodeRow['subtreetags']),
Timestamps::create(
self::parseDateTimeString($nodeRow['created']),
self::parseDateTimeString($nodeRow['originalcreated']),
Expand Down Expand Up @@ -161,7 +162,7 @@ public function mapNodeRowsToNodeAggregate(
$nodesByCoveredDimensionSpacePoints = [];
$coverageByOccupants = [];
$occupationByCovering = [];
$disabledDimensionSpacePoints = [];
$dimensionSpacePointsBySubtreeTags = DimensionSpacePointsBySubtreeTags::create();

foreach ($nodeRows as $nodeRow) {
// A node can occupy exactly one DSP and cover multiple ones...
Expand Down Expand Up @@ -192,15 +193,13 @@ public function mapNodeRowsToNodeAggregate(
$occupationByCovering[$coveredDimensionSpacePoint->hash] = $occupiedDimensionSpacePoint;
$nodesByCoveredDimensionSpacePoints[$coveredDimensionSpacePoint->hash]
= $nodesByOccupiedDimensionSpacePoints[$occupiedDimensionSpacePoint->hash];
// ... as we do for disabling
$subTreeTagsWithInherited = self::extractSubtreeTagsWithInheritedFromJson($nodeRow['subtreetags']);
if ($subTreeTagsWithInherited->withoutInherited()->contain(SubtreeTag::fromString('disabled'))) {
$disabledDimensionSpacePoints[$coveredDimensionSpacePoint->hash] = $coveredDimensionSpacePoint;
// ... as we do for explicit subtree tags
foreach (self::extractNodeTagsFromJson($nodeRow['subtreetags'])->withoutInherited() as $explicitTag) {
$dimensionSpacePointsBySubtreeTags = $dimensionSpacePointsBySubtreeTags->withSubtreeTagAndDimensionSpacePoint($explicitTag, $coveredDimensionSpacePoint);
}
}
ksort($occupiedDimensionSpacePoints);
ksort($coveredDimensionSpacePoints);
ksort($disabledDimensionSpacePoints);

/** @var Node $primaryNode a nodeAggregate only exists if it at least contains one node. */
$primaryNode = current($nodesByOccupiedDimensionSpacePoints);
Expand All @@ -217,7 +216,7 @@ public function mapNodeRowsToNodeAggregate(
new DimensionSpacePointSet($coveredDimensionSpacePoints),
$nodesByCoveredDimensionSpacePoints,
OriginByCoverage::fromArray($occupationByCovering),
new DimensionSpacePointSet($disabledDimensionSpacePoints)
$dimensionSpacePointsBySubtreeTags,
);
}

Expand All @@ -239,7 +238,7 @@ public function mapNodeRowsToNodeAggregates(
$classificationByNodeAggregate = [];
$coverageByOccupantsByNodeAggregate = [];
$occupationByCoveringByNodeAggregate = [];
$disabledDimensionSpacePointsByNodeAggregate = [];
$dimensionSpacePointsBySubtreeTagsByNodeAggregate = [];

foreach ($nodeRows as $nodeRow) {
// A node can occupy exactly one DSP and cover multiple ones...
Expand Down Expand Up @@ -284,10 +283,12 @@ public function mapNodeRowsToNodeAggregates(
= $nodesByOccupiedDimensionSpacePointsByNodeAggregate
[$rawNodeAggregateId][$occupiedDimensionSpacePoint->hash];

// ... as we do for disabling
if (isset($nodeRow['disableddimensionspacepointhash'])) {
$disabledDimensionSpacePointsByNodeAggregate
[$rawNodeAggregateId][$coveredDimensionSpacePoint->hash] = $coveredDimensionSpacePoint;
// ... as we do for explicit subtree tags
if (!array_key_exists($rawNodeAggregateId, $dimensionSpacePointsBySubtreeTagsByNodeAggregate)) {
$dimensionSpacePointsBySubtreeTagsByNodeAggregate[$rawNodeAggregateId] = DimensionSpacePointsBySubtreeTags::create();
}
foreach (self::extractNodeTagsFromJson($nodeRow['subtreetags'])->withoutInherited() as $explicitTag) {
$dimensionSpacePointsBySubtreeTagsByNodeAggregate[$rawNodeAggregateId] = $dimensionSpacePointsBySubtreeTagsByNodeAggregate[$rawNodeAggregateId]->withSubtreeTagAndDimensionSpacePoint($explicitTag, $coveredDimensionSpacePoint);
}
}

Expand Down Expand Up @@ -315,14 +316,12 @@ public function mapNodeRowsToNodeAggregates(
OriginByCoverage::fromArray(
$occupationByCoveringByNodeAggregate[$rawNodeAggregateId]
),
new DimensionSpacePointSet(
$disabledDimensionSpacePointsByNodeAggregate[$rawNodeAggregateId] ?? []
)
$dimensionSpacePointsBySubtreeTagsByNodeAggregate[$rawNodeAggregateId],
);
}
}

public static function extractSubtreeTagsWithInheritedFromJson(string $subtreeTagsJson): NodeTags
public static function extractNodeTagsFromJson(string $subtreeTagsJson): NodeTags
{
$explicitTags = [];
$inheritedTags = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ protected function mapRawDataToHierarchyRelation(array $rawData): HierarchyRelat
DimensionSpacePoint::fromJsonString($rawData['dimensionspacepoint']),
$rawData['dimensionspacepointhash'],
(int)$rawData['position'],
NodeFactory::extractSubtreeTagsWithInheritedFromJson($rawData['subtreetags']),
NodeFactory::extractNodeTagsFromJson($rawData['subtreetags']),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphIdentity;
use Neos\ContentRepository\Core\Projection\ContentGraph\DimensionSpacePointsBySubtreeTags;
use Neos\ContentRepository\Core\Projection\ContentGraph\Reference;
use Neos\ContentRepository\Core\Projection\ContentGraph\References;
use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree;
Expand Down Expand Up @@ -253,7 +254,8 @@ public function mapNodeRowsToNodeAggregate(
new DimensionSpacePointSet($coveredDimensionSpacePoints),
$nodesByCoveredDimensionSpacePoint,
OriginByCoverage::fromArray($occupationByCovered),
new DimensionSpacePointSet($disabledDimensionSpacePoints)
// TODO implement (see \Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\NodeFactory::mapNodeRowsToNodeAggregate())
DimensionSpacePointsBySubtreeTags::create(),
);
}

Expand Down Expand Up @@ -349,7 +351,8 @@ public function mapNodeRowsToNodeAggregates(array $nodeRows, VisibilityConstrain
new DimensionSpacePointSet($coveredDimensionSpacePoints[$key]),
$nodesByCoveredDimensionSpacePoint[$key],
OriginByCoverage::fromArray($occupationByCovered[$key]),
new DimensionSpacePointSet($disabledDimensionSpacePoints[$key])
// TODO implement (see \Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\NodeFactory::mapNodeRowsToNodeAggregates())
DimensionSpacePointsBySubtreeTags::create(),
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,57 @@ Feature: Tag subtree without dimensions
| b | Neos.ContentRepository.Testing:Document | root | b |
| b1 | Neos.ContentRepository.Testing:Document | b | b1 |

Scenario: Tagging the same node twice with the same subtree tag is ignored
When the command TagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag1" |
And the graph projection is fully up to date
Then I expect exactly 14 events to be published on stream with prefix "ContentStream:cs-identifier"
And event at index 13 is of type "SubtreeWasTagged" with payload:
| Key | Expected |
| contentStreamId | "cs-identifier" |
| nodeAggregateId | "a1" |
| affectedDimensionSpacePoints | [[]] |
| tag | "tag1" |
When the command TagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag1" |
Then I expect exactly 14 events to be published on stream with prefix "ContentStream:cs-identifier"

Scenario: Untagging a node without tags is ignored
Then I expect exactly 13 events to be published on stream with prefix "ContentStream:cs-identifier"
When the command UntagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag1" |
Then I expect exactly 13 events to be published on stream with prefix "ContentStream:cs-identifier"

Scenario: Untagging a node that is only implicitly tagged (inherited) is ignored
When the command TagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag1" |
And the graph projection is fully up to date
Then I expect exactly 14 events to be published on stream with prefix "ContentStream:cs-identifier"
And event at index 13 is of type "SubtreeWasTagged" with payload:
| Key | Expected |
| contentStreamId | "cs-identifier" |
| nodeAggregateId | "a1" |
| affectedDimensionSpacePoints | [[]] |
| tag | "tag1" |
When the command UntagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1a" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag1" |
Then I expect exactly 14 events to be published on stream with prefix "ContentStream:cs-identifier"

Scenario: Tagging subtree with arbitrary strategy since dimensions are not involved
When the command TagSubtree is executed with payload:
| Key | Value |
Expand Down Expand Up @@ -134,7 +185,7 @@ Feature: Tag subtree without dimensions
| Key | Value |
| nodeAggregateId | "a1a3" |
| nodeTypeName | "Neos.ContentRepository.Testing:Document" |
| parentNodeAggregateId | "a1a" |
| parentNodeAggregateId | "a1a" |
When I execute the findSubtree query for entry node aggregate id "b" I expect the following tree with tags:
"""
b (tag2*)
Expand All @@ -146,3 +197,20 @@ Feature: Tag subtree without dimensions
a1a2 (tag4*,tag3,tag2)
a1a3 (tag4,tag3,tag2)
"""

When the command UntagSubtree is executed with payload:
| Key | Value |
| nodeAggregateId | "a1a" |
| nodeVariantSelectionStrategy | "allVariants" |
| tag | "tag4" |
When I execute the findSubtree query for entry node aggregate id "b" I expect the following tree with tags:
"""
b (tag2*)
b1 (tag3*,tag2)
a1a (tag3,tag2)
a1a1 (tag4*,tag1*,tag3,tag2)
a1a1a (tag4*,tag3,tag2)
a1a1b (tag4*,tag3,tag2)
a1a2 (tag4*,tag3,tag2)
a1a3 (tag3,tag2)
"""
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ public function denormalize(Event $event): EventInterface
$eventInstance = $eventClassName::fromArray($eventDataAsArray);
return match ($eventInstance::class) {
// upcast disabled / enabled events to the corresponding SubtreeTag events
NodeAggregateWasDisabled::class => new SubtreeWasTagged($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, SubtreeTag::fromString('disabled')),
NodeAggregateWasEnabled::class => new SubtreeWasUntagged($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, SubtreeTag::fromString('disabled')),
NodeAggregateWasDisabled::class => new SubtreeWasTagged($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, SubtreeTag::disabled()),
NodeAggregateWasEnabled::class => new SubtreeWasUntagged($eventInstance->contentStreamId, $eventInstance->nodeAggregateId, $eventInstance->affectedDimensionSpacePoints, SubtreeTag::disabled()),
default => $eventInstance,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ private function handleDisableNodeAggregate(
$nodeAggregate,
$command->coveredDimensionSpacePoint
);
$tag = SubtreeTag::disabled();

if ($nodeAggregate->disablesDimensionSpacePoint($command->coveredDimensionSpacePoint)) {
if ($nodeAggregate->isExplicitlyTaggedWithSubtreeTagInDimensionSpacePoint($tag, $command->coveredDimensionSpacePoint)) {
// already disabled, so we can return a no-operation.
return EventsToPublish::empty();
}
Expand All @@ -79,7 +80,7 @@ private function handleDisableNodeAggregate(
$command->contentStreamId,
$command->nodeAggregateId,
$affectedDimensionSpacePoints,
SubtreeTag::fromString('disabled'),
$tag,
),
);

Expand Down Expand Up @@ -116,8 +117,9 @@ public function handleEnableNodeAggregate(
$nodeAggregate,
$command->coveredDimensionSpacePoint
);
$tag = SubtreeTag::disabled();

if (!$nodeAggregate->disablesDimensionSpacePoint($command->coveredDimensionSpacePoint)) {
if (!$nodeAggregate->isExplicitlyTaggedWithSubtreeTagInDimensionSpacePoint($tag, $command->coveredDimensionSpacePoint)) {
// already enabled, so we can return a no-operation.
return EventsToPublish::empty();
}
Expand All @@ -134,7 +136,7 @@ public function handleEnableNodeAggregate(
$command->contentStreamId,
$command->nodeAggregateId,
$affectedDimensionSpacePoints,
SubtreeTag::fromString('disabled'),
$tag,
)
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,28 @@

namespace Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto;

use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;

/**
* A tag that can be added to Node aggregates that is inherited by all its descendants
*
* @api
*/
final readonly class SubtreeTag implements \JsonSerializable
final class SubtreeTag implements \JsonSerializable
{
/**
* @var array<string,self>
*/
private static array $instances = [];

private static function instance(string $value): self
{
if (!array_key_exists($value, self::$instances)) {
self::$instances[$value] = new self($value);
}
return self::$instances[$value];
}

private function __construct(public string $value)
{
$regexPattern = '/^[a-z0-9_.-]{1,36}$/';
Expand All @@ -31,12 +46,17 @@ private function __construct(public string $value)

public static function fromString(string $value): self
{
return new self($value);
return self::instance($value);
}

public static function disabled(): self
{
return self::instance('disabled');
}

public function equals(self $other): bool
{
return $this->value === $other->value;
return $this === $other;
}

public function jsonSerialize(): string
Expand Down
Loading

0 comments on commit b33a774

Please sign in to comment.