Skip to content

Commit

Permalink
BUGFIX: flow neosBetaMigration:reorderNodeAggregateWasRemoved
Browse files Browse the repository at this point in the history
reparation for upgrading to beta 15

see also neos/neos-development-collection#5364
  • Loading branch information
mhsdesign committed Nov 13, 2024
1 parent 26c16e2 commit 6983322
Showing 1 changed file with 67 additions and 0 deletions.
67 changes: 67 additions & 0 deletions Classes/Command/NeosBetaMigrationCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,25 @@

namespace Neos\ContentRepositoryRegistry\Command;

use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Neos\ContentGraph\DoctrineDbalAdapter\DoctrineDbalContentGraphProjection;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryDependencies;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryInterface;
use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface;
use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName;
use Neos\ContentRepository\Core\Projection\CatchUpOptions;
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\ContentRepositoryRegistry\Factory\EventStore\DoctrineEventStoreFactory;
use Neos\EventStore\Model\Event\EventType;
use Neos\EventStore\Model\Event\EventTypes;
use Neos\EventStore\Model\Event\SequenceNumber;
use Neos\EventStore\Model\EventEnvelope;
use Neos\EventStore\Model\Events;
use Neos\EventStore\Model\EventStream\EventStreamFilter;
use Neos\EventStore\Model\EventStream\ExpectedVersion;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Cli\CommandController;
use Symfony\Component\Console\Helper\ProgressBar;
Expand All @@ -23,6 +35,44 @@ class NeosBetaMigrationCommandController extends CommandController
#[Flow\Inject()]
protected Connection $connection;

public function reorderNodeAggregateWasRemovedCommand(string $contentRepository = 'default', string $workspaceName = 'live'): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$this->backup($contentRepositoryId);

$workspace = $this->contentRepositoryRegistry->get($contentRepositoryId)->findWorkspaceByName(WorkspaceName::fromString($workspaceName));
if (!$workspace) {
$this->outputLine('Workspace not found');
$this->quit(1);
}

$streamName = ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId)->getEventStreamName();

$internals = $this->getInternals($contentRepositoryId);

// get all NodeAggregateWasRemoved from the content stream
$eventsToReorder = iterator_to_array($internals->eventStore->load($streamName, EventStreamFilter::create(EventTypes::create(EventType::fromString('NodeAggregateWasRemoved')))), false);

// remove all the NodeAggregateWasRemoved events at their sequenceNumbers
$eventTableName = DoctrineEventStoreFactory::databaseTableName($contentRepositoryId);
$this->connection->beginTransaction();
$this->connection->executeStatement(
'DELETE FROM ' . $eventTableName . ' WHERE sequencenumber IN (:sequenceNumbers)',
[
'sequenceNumbers' => array_map(fn (EventEnvelope $eventEnvelope) => $eventEnvelope->sequenceNumber->value, $eventsToReorder)
],
[
'sequenceNumbers' => ArrayParameterType::STRING
]
);
$this->connection->commit();

// reapply the NodeAggregateWasRemoved events at the end
$internals->eventStore->commit($streamName, Events::fromArray(array_map(fn (EventEnvelope $eventEnvelope) => $eventEnvelope->event, $eventsToReorder)), ExpectedVersion::ANY());

$this->outputLine('Reordered %d removals. Please replay and rebase your other workspaces.', [count($eventsToReorder)]);
}

public function fixReplayCommand(string $contentRepository = 'default', bool $resetProjection = true): void
{
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
Expand Down Expand Up @@ -149,4 +199,21 @@ private function copyEventTable(string $backupEventTableName, ContentRepositoryI
FROM ' . $eventTableName
);
}

private function getInternals(ContentRepositoryId $contentRepositoryId): ContentRepositoryServiceFactoryDependencies
{
// NOT API!!!
$accessor = new class implements ContentRepositoryServiceFactoryInterface {
public ContentRepositoryServiceFactoryDependencies|null $dependencies;
public function build(ContentRepositoryServiceFactoryDependencies $serviceFactoryDependencies): ContentRepositoryServiceInterface
{
$this->dependencies = $serviceFactoryDependencies;
return new class implements ContentRepositoryServiceInterface
{
};
}
};
$this->contentRepositoryRegistry->buildService($contentRepositoryId, $accessor);
return $accessor->dependencies;
}
}

0 comments on commit 6983322

Please sign in to comment.