From 26fff3a0b737f69df60e32548dfec3afe4764830 Mon Sep 17 00:00:00 2001
From: mhsdesign <85400359+mhsdesign@users.noreply.github.com>
Date: Mon, 19 Feb 2024 15:13:32 +0100
Subject: [PATCH] WIP: `cr:list` command
---
.../ContentStreamCommandController.php | 18 ++++-
.../Classes/Command/CrCommandController.php | 74 +++++++++++++++++++
.../Classes/ContentRepositoryRegistry.php | 13 ++++
.../Classes/Command/SiteCommandController.php | 2 +
4 files changed, 103 insertions(+), 4 deletions(-)
diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/ContentStreamCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/ContentStreamCommandController.php
index a692a22fdd7..5b1a6f4876b 100644
--- a/Neos.ContentRepositoryRegistry/Classes/Command/ContentStreamCommandController.php
+++ b/Neos.ContentRepositoryRegistry/Classes/Command/ContentStreamCommandController.php
@@ -28,9 +28,14 @@ class ContentStreamCommandController extends CommandController
* If you also call with "--removeTemporary", will delete ALL content streams which are currently not assigned
* to a workspace (f.e. dangling ones in FORKED or CREATED.).
*/
- public function pruneCommand(string $contentRepositoryIdentifier = 'default', bool $removeTemporary = false): void
+ public function pruneCommand(string $contentRepository = 'default', bool $removeTemporary = false): void
{
- $contentRepositoryId = ContentRepositoryId::fromString($contentRepositoryIdentifier);
+ if (!$this->output->askConfirmation(sprintf('> This operation in "%s" cannot be reverted. Are you sure to proceed? (y/n) ', $contentRepository), false)) {
+ $this->outputLine('Abort.');
+ return;
+ }
+
+ $contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$contentStreamPruner = $this->contentRepositoryRegistry->buildService($contentRepositoryId, new ContentStreamPrunerFactory());
$unusedContentStreams = $contentStreamPruner->prune($removeTemporary);
@@ -47,9 +52,14 @@ public function pruneCommand(string $contentRepositoryIdentifier = 'default', bo
/**
* Remove unused and deleted content streams from the event stream; effectively REMOVING information completely
*/
- public function pruneRemovedFromEventStreamCommand(string $contentRepositoryIdentifier = 'default'): void
+ public function pruneRemovedFromEventStreamCommand(string $contentRepository = 'default'): void
{
- $contentRepositoryId = ContentRepositoryId::fromString($contentRepositoryIdentifier);
+ if (!$this->output->askConfirmation(sprintf('> This operation in "%s" cannot be reverted. Are you sure to proceed? (y/n) ', $contentRepository), false)) {
+ $this->outputLine('Abort.');
+ return;
+ }
+
+ $contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
$contentStreamPruner = $this->contentRepositoryRegistry->buildService($contentRepositoryId, new ContentStreamPrunerFactory());
$unusedContentStreams = $contentStreamPruner->pruneRemovedFromEventStream();
diff --git a/Neos.ContentRepositoryRegistry/Classes/Command/CrCommandController.php b/Neos.ContentRepositoryRegistry/Classes/Command/CrCommandController.php
index c82eebe9106..f5bfbcdfca1 100644
--- a/Neos.ContentRepositoryRegistry/Classes/Command/CrCommandController.php
+++ b/Neos.ContentRepositoryRegistry/Classes/Command/CrCommandController.php
@@ -7,12 +7,16 @@
use Neos\ContentRepository\Core\Projection\CatchUpOptions;
use Neos\ContentRepository\Core\Projection\ProjectionStatusType;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
+use Neos\ContentRepositoryRegistry\Exception\InvalidConfigurationException;
use Neos\ContentRepositoryRegistry\Service\ProjectionReplayServiceFactory;
use Neos\EventStore\Model\Event\SequenceNumber;
use Neos\EventStore\Model\EventStore\StatusType;
use Neos\Flow\Cli\CommandController;
use Neos\ContentRepository\Core\Service\ContentStreamPrunerFactory;
use Neos\ContentRepository\Core\Service\WorkspaceMaintenanceServiceFactory;
+use Neos\Flow\Persistence\Doctrine\Exception\DatabaseException;
+use Neos\Neos\Domain\Model\Site;
+use Neos\Neos\Domain\Repository\SiteRepository;
use Symfony\Component\Console\Output\Output;
final class CrCommandController extends CommandController
@@ -21,6 +25,7 @@ final class CrCommandController extends CommandController
public function __construct(
private readonly ContentRepositoryRegistry $contentRepositoryRegistry,
private readonly ProjectionReplayServiceFactory $projectionServiceFactory,
+ private readonly SiteRepository $siteRepository,
) {
parent::__construct();
}
@@ -217,4 +222,73 @@ public function pruneCommand(string $contentRepository = 'default', bool $force
$this->outputLine('Done.');
}
+
+ public function listCommand()
+ {
+ $rows = [];
+
+ /** @var list $sites */
+ $sites = [];
+ try {
+ $sites = iterator_to_array($this->siteRepository->findAll());
+ } catch (DatabaseException) {
+ // doctrine might have not been migrated yet or no database is connected.
+ $this->outputLine('Site repository is not accessible.');
+ }
+
+ foreach ($this->contentRepositoryRegistry->getAllContentRepositoryIds() as $contentRepositoryId) {
+ $contentRepository = null;
+ try {
+ $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId);
+ } catch (InvalidConfigurationException $exception) {
+ $this->outputLine('Content repository %s is not well configures: %s.', [$contentRepositoryId->value, $exception->getMessage()]);
+ }
+
+ $configuredSites = [];
+ foreach ($sites as $site) {
+ if (!$site->getConfiguration()->contentRepositoryId->equals($contentRepositoryId)) {
+ continue;
+ }
+ $configuredSites[] = $site->getName();
+ }
+
+ $statusString = '-';
+ $workspacesString = '-';
+ $contentStreamsString = '-';
+ $nodesString = '-';
+
+ if ($contentRepository) {
+ $statusString = $contentRepository->status()->isOk() ? 'okay' : 'not okay';
+
+ try {
+ $workspacesString = sprintf('%d entries', count($contentRepository->getWorkspaceFinder()->findAll()));
+ } catch (\Throwable $e) {
+ $this->outputLine('WorkspaceFinder of %s not functional: %s.', [$contentRepositoryId->value, $e->getMessage()]);
+ }
+
+ try {
+ $contentStreamsString = sprintf('%d entries', iterator_count($contentRepository->getContentStreamFinder()->findAllIds()));
+ } catch (\Throwable $e) {
+ $this->outputLine('ContentStreamFinder of %s not functional: %s.', [$contentRepositoryId->value, $e->getMessage()]);
+ }
+
+ try {
+ $nodesString = sprintf('%d entries', $contentRepository->getContentGraph()->countNodes());
+ } catch (\Throwable $e) {
+ $this->outputLine('ContentGraph of %s not functional: %s.', [$contentRepositoryId->value, $e->getMessage()]);
+ }
+ }
+
+ $rows[] = [
+ $contentRepositoryId->value,
+ $statusString,
+ join(', ', $configuredSites) ?: '-',
+ $workspacesString,
+ $contentStreamsString,
+ $nodesString
+ ];
+ }
+
+ $this->output->outputTable($rows, ['Identifier', 'Status', 'Sites', 'Workspaces', 'Contentstreams', 'Nodes']);
+ }
}
diff --git a/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php b/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php
index 367ba4da484..d986fa88dcd 100644
--- a/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php
+++ b/Neos.ContentRepositoryRegistry/Classes/ContentRepositoryRegistry.php
@@ -64,6 +64,19 @@ public function get(ContentRepositoryId $contentRepositoryId): ContentRepository
return $this->getFactory($contentRepositoryId)->getOrBuild();
}
+ /**
+ * @return iterable
+ */
+ public function getAllContentRepositoryIds(): iterable
+ {
+ if (!is_array($this->settings['contentRepositories'] ?? null)) {
+ throw InvalidConfigurationException::fromMessage('No Content Repositories are configured');
+ }
+ foreach (array_keys($this->settings['contentRepositories']) as $contentRepositoryId) {
+ yield ContentRepositoryId::fromString($contentRepositoryId);
+ }
+ }
+
/**
* @internal for test cases only
*/
diff --git a/Neos.Neos/Classes/Command/SiteCommandController.php b/Neos.Neos/Classes/Command/SiteCommandController.php
index 888c0923ed3..c1629333a11 100644
--- a/Neos.Neos/Classes/Command/SiteCommandController.php
+++ b/Neos.Neos/Classes/Command/SiteCommandController.php
@@ -175,6 +175,8 @@ public function listCommand()
}
}
+ // todo use outputTable
+
$this->outputLine();
$this->outputLine(' ' . str_pad('Name', $longestSiteName + 15)
. str_pad('Node name', $longestNodeName + 15)