Skip to content

Commit

Permalink
TASK: Add workspace content stream mapping to content graph projection
Browse files Browse the repository at this point in the history
Previously the `DoctrineDbalContentGraphProjection` accessed the `workspace` table of a different projection in order to resolve the workspace<->content stream mapping.

This change adds the `workspace` table to the content graph projection and uses that instead for the resolution.

**Note:** This is not a breaking change because it comes with a migration and does not require a replay, but a
    ./flow cr:setup
is needed in order to apply that!

Related: #5038
  • Loading branch information
bwaidelich committed May 25, 2024
1 parent 60753a8 commit 0b0ca5d
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,11 @@ public function __construct(

public function buildForWorkspace(WorkspaceName $workspaceName): ContentGraph
{
// FIXME: Should be part of this projection, this is forbidden
$tableName = strtolower(sprintf(
'cr_%s_p_%s',
$this->contentRepositoryId->value,
'workspace'
));

$currentContentStreamIdStatement = <<<SQL
SELECT
currentcontentstreamid
FROM
{$tableName}
{$this->tableNames->workspace()}
WHERE
workspaceName = :workspaceName
LIMIT 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public function referenceRelation(): string
return $this->tableNamePrefix . '_referencerelation';
}

public function workspace(): string
{
return $this->tableNamePrefix . '_workspace';
}

public function checkpoint(): string
{
return $this->tableNamePrefix . '_checkpoint';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeRemoval;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\NodeVariation;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\SubtreeTagging;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature\Workspace;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\HierarchyRelation;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRecord;
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRelationAnchorPoint;
Expand Down Expand Up @@ -43,6 +44,16 @@
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\WorkspaceRebaseFailed;
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased;
use Neos\ContentRepository\Core\Infrastructure\DbalCheckpointStorage;
use Neos\ContentRepository\Core\Infrastructure\DbalSchemaDiff;
use Neos\ContentRepository\Core\NodeType\NodeTypeName;
Expand All @@ -65,10 +76,11 @@
*/
final class DoctrineDbalContentGraphProjection implements ProjectionInterface, WithMarkStaleInterface
{
use NodeMove;
use NodeRemoval;
use NodeVariation;
use SubtreeTagging;
use NodeRemoval;
use NodeMove;
use Workspace;


public const RELATION_DEFAULT_OFFSET = 128;
Expand All @@ -91,7 +103,19 @@ public function __construct(

public function setUp(): void
{
foreach ($this->determineRequiredSqlStatements() as $statement) {
$statements = $this->determineRequiredSqlStatements();

// MIGRATION from 2024-05-23: copy data from "cr_<crid>_p_workspace" to "cr_<crid>_p_graph_workspace" table
$legacyWorkspaceTableName = str_replace('_p_graph_workspace', '_p_workspace', $this->tableNames->workspace());
if (
$this->dbal->getSchemaManager()->tablesExist([$legacyWorkspaceTableName])
&& !$this->dbal->getSchemaManager()->tablesExist([$this->tableNames->workspace()])
) {
$statements[] = 'INSERT INTO ' . $this->tableNames->workspace() . ' (workspacename, baseworkspacename, currentcontentstreamid, status) SELECT workspacename, baseworkspacename, currentcontentstreamid, status FROM ' . $legacyWorkspaceTableName;
}
// /MIGRATION

foreach ($statements as $statement) {
try {
$this->dbal->executeStatement($statement);
} catch (DbalException $e) {
Expand Down Expand Up @@ -169,8 +193,18 @@ public function canHandle(EventInterface $event): bool
NodeSpecializationVariantWasCreated::class,
RootNodeAggregateDimensionsWereUpdated::class,
RootNodeAggregateWithNodeWasCreated::class,
RootWorkspaceWasCreated::class,
SubtreeWasTagged::class,
SubtreeWasUntagged::class,
WorkspaceBaseWorkspaceWasChanged::class,
WorkspaceRebaseFailed::class,
WorkspaceWasCreated::class,
WorkspaceWasDiscarded::class,
WorkspaceWasPartiallyDiscarded::class,
WorkspaceWasPartiallyPublished::class,
WorkspaceWasPublished::class,
WorkspaceWasRebased::class,
WorkspaceWasRemoved::class,
]);
}

Expand All @@ -193,8 +227,18 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void
NodeSpecializationVariantWasCreated::class => $this->whenNodeSpecializationVariantWasCreated($event, $eventEnvelope),
RootNodeAggregateDimensionsWereUpdated::class => $this->whenRootNodeAggregateDimensionsWereUpdated($event),
RootNodeAggregateWithNodeWasCreated::class => $this->whenRootNodeAggregateWithNodeWasCreated($event, $eventEnvelope),
RootWorkspaceWasCreated::class => $this->whenRootWorkspaceWasCreated($event),
SubtreeWasTagged::class => $this->whenSubtreeWasTagged($event),
SubtreeWasUntagged::class => $this->whenSubtreeWasUntagged($event),
WorkspaceBaseWorkspaceWasChanged::class => $this->whenWorkspaceBaseWorkspaceWasChanged($event),
WorkspaceRebaseFailed::class => $this->whenWorkspaceRebaseFailed($event),
WorkspaceWasCreated::class => $this->whenWorkspaceWasCreated($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),
default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))),
};
}
Expand Down Expand Up @@ -628,6 +672,11 @@ private function whenRootNodeAggregateWithNodeWasCreated(RootNodeAggregateWithNo
);
}

private function whenRootWorkspaceWasCreated(RootWorkspaceWasCreated $event): void
{
$this->createWorkspace($event->workspaceName, null, $event->newContentStreamId);
}

private function whenSubtreeWasTagged(SubtreeWasTagged $event): void
{
$this->addSubtreeTag($event->contentStreamId, $event->nodeAggregateId, $event->affectedDimensionSpacePoints, $event->tag);
Expand All @@ -638,6 +687,71 @@ private function whenSubtreeWasUntagged(SubtreeWasUntagged $event): void
$this->removeSubtreeTag($event->contentStreamId, $event->nodeAggregateId, $event->affectedDimensionSpacePoints, $event->tag);
}

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

private function whenWorkspaceRebaseFailed(WorkspaceRebaseFailed $event): void
{
$this->markWorkspaceAsOutdatedConflict($event->workspaceName);
}

private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void
{
$this->createWorkspace($event->workspaceName, $event->baseWorkspaceName, $event->newContentStreamId);
}

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

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

private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void
{
// TODO: How do we test this method? – It's hard to design a BDD testcase that fails if this method is commented out...
$this->updateWorkspaceContentStreamId($event->sourceWorkspaceName, $event->newSourceContentStreamId);
$this->markDependentWorkspacesAsOutdated($event->targetWorkspaceName);

// NASTY: we need to set the source workspace name as non-outdated; as it has been made up-to-date again.
$this->markWorkspaceAsUpToDate($event->sourceWorkspaceName);

$this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName);
}

private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void
{
// TODO: How do we test this method? – It's hard to design a BDD testcase that fails if this method is commented out...
$this->updateWorkspaceContentStreamId($event->sourceWorkspaceName, $event->newSourceContentStreamId);
$this->markDependentWorkspacesAsOutdated($event->targetWorkspaceName);

// NASTY: we need to set the source workspace name as non-outdated; as it has been made up-to-date again.
$this->markWorkspaceAsUpToDate($event->sourceWorkspaceName);

$this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName);
}

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

// When the rebase is successful, we can set the status of the workspace back to UP_TO_DATE.
$this->markWorkspaceAsUpToDate($event->workspaceName);
}

private function whenWorkspaceWasRemoved(WorkspaceWasRemoved $event): void
{
$this->removeWorkspace($event->workspaceName);
}

/** --------------------------------- */

Expand All @@ -661,6 +775,7 @@ private function truncateDatabaseTables(): void
$this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->hierarchyRelation());
$this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->referenceRelation());
$this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->dimensionSpacePoints());
$this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->workspace());
} catch (DbalException $e) {
throw new \RuntimeException(sprintf('Failed to truncate database tables for projection %s: %s', self::class, $e->getMessage()), 1716478318, $e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DoctrineDbalContentGraphSchemaBuilder
private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci';

public function __construct(
private readonly ContentGraphTableNames $contentGraphTableNames
private readonly ContentGraphTableNames $tableNames
) {
}

Expand All @@ -29,13 +29,14 @@ public function buildSchema(AbstractSchemaManager $schemaManager): Schema
$this->createNodeTable(),
$this->createHierarchyRelationTable(),
$this->createReferenceRelationTable(),
$this->createDimensionSpacePointsTable()
$this->createDimensionSpacePointsTable(),
$this->createWorkspaceTable(),
]);
}

private function createNodeTable(): Table
{
$table = self::createTable($this->contentGraphTableNames->node(), [
$table = self::createTable($this->tableNames->node(), [
DbalSchemaFactory::columnForNodeAnchorPoint('relationanchorpoint')->setAutoincrement(true),
DbalSchemaFactory::columnForNodeAggregateId('nodeaggregateid')->setNotnull(false),
DbalSchemaFactory::columnForDimensionSpacePointHash('origindimensionspacepointhash')->setNotnull(false),
Expand All @@ -57,7 +58,7 @@ private function createNodeTable(): Table

private function createHierarchyRelationTable(): Table
{
$table = self::createTable($this->contentGraphTableNames->hierarchyRelation(), [
$table = self::createTable($this->tableNames->hierarchyRelation(), [
(new Column('position', self::type(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForContentStreamId('contentstreamid')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePointHash('dimensionspacepointhash')->setNotnull(true),
Expand All @@ -76,7 +77,7 @@ private function createHierarchyRelationTable(): Table

private function createDimensionSpacePointsTable(): Table
{
$table = self::createTable($this->contentGraphTableNames->dimensionSpacePoints(), [
$table = self::createTable($this->tableNames->dimensionSpacePoints(), [
DbalSchemaFactory::columnForDimensionSpacePointHash('hash')->setNotnull(true),
DbalSchemaFactory::columnForDimensionSpacePoint('dimensionspacepoint')->setNotnull(true)
]);
Expand All @@ -87,7 +88,7 @@ private function createDimensionSpacePointsTable(): Table

private function createReferenceRelationTable(): Table
{
$table = self::createTable($this->contentGraphTableNames->referenceRelation(), [
$table = self::createTable($this->tableNames->referenceRelation(), [
(new Column('name', self::type(Types::STRING)))->setLength(255)->setNotnull(true)->setCustomSchemaOption('charset', 'ascii')->setCustomSchemaOption('collation', 'ascii_general_ci'),
(new Column('position', self::type(Types::INTEGER)))->setNotnull(true),
DbalSchemaFactory::columnForNodeAnchorPoint('nodeanchorpoint'),
Expand All @@ -99,6 +100,18 @@ private function createReferenceRelationTable(): Table
->setPrimaryKey(['name', 'position', 'nodeanchorpoint']);
}

private function createWorkspaceTable(): Table
{
$workspaceTable = self::createTable($this->tableNames->workspace(), [
DbalSchemaFactory::columnForWorkspaceName('workspacename')->setNotnull(true),
DbalSchemaFactory::columnForWorkspaceName('baseworkspacename')->setNotnull(false),
DbalSchemaFactory::columnForContentStreamId('currentcontentstreamid')->setNotNull(true),
(new Column('status', self::type(Types::BINARY)))->setLength(20)->setNotnull(false),
]);

return $workspaceTable->setPrimaryKey(['workspacename']);
}

/**
* @param array<Column> $columns
*/
Expand Down
Loading

0 comments on commit 0b0ca5d

Please sign in to comment.