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: Introduce NodeIdentity #4868

Closed
wants to merge 6 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Schema\AbstractSchemaManager;
use Doctrine\DBAL\Types\Types;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\CurrentWorkspaceContentStreamId;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeMove;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeRemoval;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeVariation;
Expand Down Expand Up @@ -43,6 +44,15 @@
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTags;
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Event\SubtreeWasTagged;
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Event\SubtreeWasUntagged;
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\RootWorkspaceWasCreated;
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\WorkspaceWasCreated;
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged;
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished;
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased;
use Neos\ContentRepository\Core\Infrastructure\DbalCheckpointStorage;
use Neos\ContentRepository\Core\Infrastructure\DbalClientInterface;
use Neos\ContentRepository\Core\Infrastructure\DbalSchemaDiff;
Expand Down Expand Up @@ -72,7 +82,7 @@ final class DoctrineDbalContentGraphProjection implements ProjectionInterface, W
use SubtreeTagging;
use NodeRemoval;
use NodeMove;

use CurrentWorkspaceContentStreamId;

public const RELATION_DEFAULT_OFFSET = 128;

Expand Down Expand Up @@ -177,6 +187,7 @@ private function truncateDatabaseTables(): void
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_hierarchyrelation');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_referencerelation');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_dimensionspacepoints');
$connection->executeQuery('TRUNCATE table ' . $this->tableNamePrefix . '_workspaces');
}

public function canHandle(EventInterface $event): bool
Expand All @@ -200,6 +211,21 @@ public function canHandle(EventInterface $event): bool
NodePeerVariantWasCreated::class,
SubtreeWasTagged::class,
SubtreeWasUntagged::class,

/**
* Workspace related commands, see {@see CurrentWorkspaceContentStreamId}
* We are not interested in the events WorkspaceWasRenamed, WorkspaceRebaseFailed and WorkspaceOwnerWasChanged
* As they do not change the current content stream id
*/
WorkspaceWasCreated::class,
RootWorkspaceWasCreated::class,
WorkspaceWasDiscarded::class,
WorkspaceWasPartiallyDiscarded::class,
WorkspaceWasPartiallyPublished::class,
WorkspaceWasPublished::class,
WorkspaceWasRebased::class,
WorkspaceWasRemoved::class,
WorkspaceBaseWorkspaceWasChanged::class,
]);
}

Expand All @@ -224,6 +250,15 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void
NodePeerVariantWasCreated::class => $this->whenNodePeerVariantWasCreated($event, $eventEnvelope),
SubtreeWasTagged::class => $this->whenSubtreeWasTagged($event),
SubtreeWasUntagged::class => $this->whenSubtreeWasUntagged($event),
WorkspaceWasCreated::class => $this->whenWorkspaceWasCreated($event),
RootWorkspaceWasCreated::class => $this->whenRootWorkspaceWasCreated($event),
WorkspaceWasDiscarded::class => $this->whenWorkspaceWasDiscarded($event),
WorkspaceWasPartiallyDiscarded::class => $this->whenWorkspaceWasPartiallyDiscarded($event),
WorkspaceWasPartiallyPublished::class => $this->whenWorkspaceWasPartiallyPublished($event),
WorkspaceWasPublished::class => $this->whenWorkspaceWasPublished($event),
WorkspaceWasRebased::class => $this->whenWorkspaceWasRebased($event),
WorkspaceWasRemoved::class => $this->whenWorkspaceWasRemoved($event),
WorkspaceBaseWorkspaceWasChanged::class => $this->whenWorkspaceBaseWorkspaceWasChanged($event),
default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ public function buildSchema(AbstractSchemaManager $schemaManager): Schema
$this->createNodeTable(),
$this->createHierarchyRelationTable(),
$this->createReferenceRelationTable(),
$this->createDimensionSpacePointsTable()
$this->createDimensionSpacePointsTable(),
$this->createWorkspacesTable()
]);
}

Expand Down Expand Up @@ -94,4 +95,14 @@ private function createReferenceRelationTable(): Table
return $table
->setPrimaryKey(['name', 'position', 'nodeanchorpoint']);
}

private function createWorkspacesTable(): Table
{
$workspaceTable = new Table($this->tableNamePrefix . '_workspaces', [
(new Column('workspacename', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setCustomSchemaOption('collation', self::DEFAULT_TEXT_COLLATION),
DbalSchemaFactory::columnForContentStreamId('currentcontentstreamid')->setNotNull(true),
]);

return $workspaceTable->setPrimaryKey(['workspacename']);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

declare(strict_types=1);

namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature;

use Doctrine\DBAL\Connection;
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\RootWorkspaceWasCreated;
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\WorkspaceWasCreated;
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged;
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasDiscarded;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyDiscarded;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPartiallyPublished;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Event\WorkspaceWasPublished;
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
* The CurrentWorkspaceContentStreamId projection feature trait
*
* Duplicated from the projection {@see \Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjection}
* But we only require the workspace name to content stream id mapping.
*
* @internal
*/
trait CurrentWorkspaceContentStreamId
{
abstract protected function getTableNamePrefix(): string;

abstract protected function getDatabaseConnection(): Connection;

private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void
{
$this->getDatabaseConnection()->insert($this->getTableNamePrefix() . '_workspaces', [
'workspaceName' => $event->workspaceName->value,
'currentContentStreamId' => $event->newContentStreamId->value
]);
}

private function whenRootWorkspaceWasCreated(RootWorkspaceWasCreated $event): void
{
$this->getDatabaseConnection()->insert($this->getTableNamePrefix() . '_workspaces', [
'workspaceName' => $event->workspaceName->value,
'currentContentStreamId' => $event->newContentStreamId->value
]);
}

private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void
{
$this->updateContentStreamId($event->newContentStreamId, $event->workspaceName);
}

private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscarded $event): void
{
$this->updateContentStreamId($event->newContentStreamId, $event->workspaceName);
}

private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void
{
$this->updateContentStreamId($event->newSourceContentStreamId, $event->sourceWorkspaceName);
}

private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void
{
$this->updateContentStreamId($event->newSourceContentStreamId, $event->sourceWorkspaceName);
}

private function whenWorkspaceWasRebased(WorkspaceWasRebased $event): void
{
$this->updateContentStreamId($event->newContentStreamId, $event->workspaceName);
}

private function whenWorkspaceWasRemoved(WorkspaceWasRemoved $event): void
{
$this->getDatabaseConnection()->delete(
$this->getTableNamePrefix() . '_workspaces',
['workspaceName' => $event->workspaceName->value]
);
}

private function whenWorkspaceBaseWorkspaceWasChanged(WorkspaceBaseWorkspaceWasChanged $event): void
{
$this->updateContentStreamId($event->newContentStreamId, $event->workspaceName);
}

private function updateContentStreamId(
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
): void {
$this->getDatabaseConnection()->update($this->getTableNamePrefix() . '_workspaces', [
'currentContentStreamId' => $contentStreamId->value,
], [
'workspaceName' => $workspaceName->value
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Exception\RootNodeAggregateDoesNotExist;
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
* The Doctrine DBAL adapter content graph
Expand Down Expand Up @@ -79,15 +81,20 @@ public function __construct(
}

final public function getSubgraph(
ContentStreamId $contentStreamId,
WorkspaceName $workspaceName,
DimensionSpacePoint $dimensionSpacePoint,
VisibilityConstraints $visibilityConstraints
): ContentSubgraphInterface {
$index = $contentStreamId->value . '-' . $dimensionSpacePoint->hash . '-' . $visibilityConstraints->getHash();
$index = $workspaceName->value . '-' . $dimensionSpacePoint->hash . '-' . $visibilityConstraints->getHash();
if (!isset($this->subgraphs[$index])) {
$contentStreamId = $this->findCurrentContentStreamIdForWorkspaceName($workspaceName);
if (!$contentStreamId) {
throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName);
}
$this->subgraphs[$index] = new ContentSubgraphWithRuntimeCaches(
new ContentSubgraph(
$this->contentRepositoryId,
$workspaceName,
$contentStreamId,
$dimensionSpacePoint,
$visibilityConstraints,
Expand Down Expand Up @@ -353,7 +360,7 @@ public function countNodes(): int
try {
return (int)$result->fetchOne();
} catch (DriverException | DBALException $e) {
throw new \RuntimeException(sprintf('Failed to fetch rows from database: %s', $e->getMessage()), 1701444590, $e);
throw new \RuntimeException(sprintf('Failed to count rows in database: %s', $e->getMessage()), 1701444590, $e);
}
}

Expand Down Expand Up @@ -426,4 +433,27 @@ private function fetchRows(QueryBuilder $queryBuilder): array
throw new \RuntimeException(sprintf('Failed to fetch rows from database: %s', $e->getMessage()), 1701444358, $e);
}
}

private function findCurrentContentStreamIdForWorkspaceName(WorkspaceName $workspaceName): ?ContentStreamId
{
$query = $this->createQueryBuilder()
->select('workspaces.currentContentStreamId')
->from($this->tableNamePrefix . '_workspaces', 'workspaces')
->where('workspaces.workspaceName = :workspaceName')
->setParameter(':workspaceName', $workspaceName->value);

$result = $query->execute();
if (!$result instanceof Result) {
throw new \RuntimeException(sprintf('Failed to fetch contents stream for workspace. Expected result to be of type %s, got: %s', Result::class, get_debug_type($result)), 1710883555);
}
try {
$contentStreamId = $result->fetchOne();
if ($contentStreamId === false) {
return null;
}
return ContentStreamId::fromString((string)$contentStreamId);
} catch (DriverException | DBALException $e) {
throw new \RuntimeException(sprintf('Failed to fetch row in database: %s', $e->getMessage()), 1710883554, $e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
use Neos\ContentRepository\Core\SharedModel\Node\NodeName;
use Neos\ContentRepository\Core\SharedModel\Node\PropertyName;
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
* The content subgraph application repository
Expand Down Expand Up @@ -105,6 +106,7 @@ final class ContentSubgraph implements ContentSubgraphInterface

public function __construct(
private readonly ContentRepositoryId $contentRepositoryId,
private readonly WorkspaceName $workspaceName,
private readonly ContentStreamId $contentStreamId,
private readonly DimensionSpacePoint $dimensionSpacePoint,
private readonly VisibilityConstraints $visibilityConstraints,
Expand Down Expand Up @@ -328,7 +330,13 @@ public function findSubtree(NodeAggregateId $entryNodeAggregateId, FindSubtreeFi
foreach (array_reverse($result) as $nodeData) {
$nodeAggregateId = $nodeData['nodeaggregateid'];
$parentNodeAggregateId = $nodeData['parentNodeAggregateId'];
$node = $this->nodeFactory->mapNodeRowToNode($nodeData, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints);
$node = $this->nodeFactory->mapNodeRowToNode(
$nodeData,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
);
$subtree = new Subtree((int)$nodeData['level'], $node, array_key_exists($nodeAggregateId, $subtreesByParentNodeId) ? array_reverse($subtreesByParentNodeId[$nodeAggregateId]) : []);
if ($subtree->level === 0) {
return $subtree;
Expand Down Expand Up @@ -357,6 +365,7 @@ public function findAncestorNodes(NodeAggregateId $entryNodeAggregateId, FindAnc

return $this->nodeFactory->mapNodeRowsToNodes(
$nodeRows,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
Expand Down Expand Up @@ -418,6 +427,7 @@ public function findClosestNode(NodeAggregateId $entryNodeAggregateId, FindClose
);
return $this->nodeFactory->mapNodeRowsToNodes(
$nodeRows,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
Expand All @@ -435,7 +445,13 @@ public function findDescendantNodes(NodeAggregateId $entryNodeAggregateId, FindD
}
$queryBuilderCte->addOrderBy('level')->addOrderBy('position');
$nodeRows = $this->fetchCteResults($queryBuilderInitial, $queryBuilderRecursive, $queryBuilderCte, 'tree');
return $this->nodeFactory->mapNodeRowsToNodes($nodeRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints);
return $this->nodeFactory->mapNodeRowsToNodes(
$nodeRows,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
);
}

public function countDescendantNodes(NodeAggregateId $entryNodeAggregateId, CountDescendantNodesFilter $filter): int
Expand Down Expand Up @@ -863,6 +879,7 @@ private function fetchNode(QueryBuilder $queryBuilder): ?Node
}
return $this->nodeFactory->mapNodeRowToNode(
$nodeRow,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
Expand All @@ -876,7 +893,13 @@ private function fetchNodes(QueryBuilder $queryBuilder): Nodes
} catch (DbalDriverException | DbalException $e) {
throw new \RuntimeException(sprintf('Failed to fetch nodes: %s', $e->getMessage()), 1678292896, $e);
}
return $this->nodeFactory->mapNodeRowsToNodes($nodeRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints);
return $this->nodeFactory->mapNodeRowsToNodes(
$nodeRows,
$this->workspaceName,
$this->contentStreamId,
$this->dimensionSpacePoint,
$this->visibilityConstraints
);
}

private function fetchCount(QueryBuilder $queryBuilder): int
Expand All @@ -895,7 +918,7 @@ private function fetchReferences(QueryBuilder $queryBuilder): References
} catch (DbalDriverException | DbalException $e) {
throw new \RuntimeException(sprintf('Failed to fetch references: %s', $e->getMessage()), 1678364944, $e);
}
return $this->nodeFactory->mapReferenceRowsToReferences($referenceRows, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints);
return $this->nodeFactory->mapReferenceRowsToReferences($referenceRows, $this->workspaceName, $this->contentStreamId, $this->dimensionSpacePoint, $this->visibilityConstraints);
}

/**
Expand Down
Loading
Loading