From 347322eeeed6c5b7fe30268926cb6c602075a5f2 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sun, 27 Oct 2024 21:06:12 +0100 Subject: [PATCH] TASK: Catchups will only be able to access the state of the projection they are registered for A catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections state is not safe as the other projection might not be behind - the order is undefined. This will make it possible to catchup projections from outside of the cr instance as proposed here: https://github.com/neos/neos-development-collection/pull/5321 --- Classes/ContentRepository.php | 2 +- .../ProjectionsAndCatchUpHooksFactory.php | 6 +++--- Classes/Projection/CatchUpHookFactories.php | 16 ++++++++++++---- .../Projection/CatchUpHookFactoryInterface.php | 13 +++++++++++-- .../ContentGraphReadModelInterface.php | 6 +++++- .../Projection/ProjectionsAndCatchUpHooks.php | 1 + 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Classes/ContentRepository.php b/Classes/ContentRepository.php index d966002f..8587b3fc 100644 --- a/Classes/ContentRepository.php +++ b/Classes/ContentRepository.php @@ -147,7 +147,7 @@ public function catchUpProjection(string $projectionClassName, CatchUpOptions $o $projection = $this->projectionsAndCatchUpHooks->projections->get($projectionClassName); $catchUpHookFactory = $this->projectionsAndCatchUpHooks->getCatchUpHookFactoryForProjection($projection); - $catchUpHook = $catchUpHookFactory?->build($this); + $catchUpHook = $catchUpHookFactory?->build($this->id, $projection->getState()); // TODO allow custom stream name per projection $streamName = VirtualStreamName::all(); diff --git a/Classes/Factory/ProjectionsAndCatchUpHooksFactory.php b/Classes/Factory/ProjectionsAndCatchUpHooksFactory.php index cd369a87..f48ed134 100644 --- a/Classes/Factory/ProjectionsAndCatchUpHooksFactory.php +++ b/Classes/Factory/ProjectionsAndCatchUpHooksFactory.php @@ -6,12 +6,12 @@ use Neos\ContentRepository\Core\Projection\CatchUpHookFactories; use Neos\ContentRepository\Core\Projection\CatchUpHookFactoryInterface; +use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphProjectionInterface; use Neos\ContentRepository\Core\Projection\ProjectionFactoryInterface; use Neos\ContentRepository\Core\Projection\ProjectionInterface; use Neos\ContentRepository\Core\Projection\Projections; use Neos\ContentRepository\Core\Projection\ProjectionsAndCatchUpHooks; use Neos\ContentRepository\Core\Projection\ProjectionStateInterface; -use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphProjectionInterface; /** * @api for custom framework integrations, not for users of the CR @@ -19,7 +19,7 @@ final class ProjectionsAndCatchUpHooksFactory { /** - * @var array>, options: array, catchUpHooksFactories: array}> + * @var array>, options: array, catchUpHooksFactories: array>}> */ private array $factories = []; @@ -40,7 +40,7 @@ public function registerFactory(ProjectionFactoryInterface $factory, array $opti /** * @param ProjectionFactoryInterface> $factory - * @param CatchUpHookFactoryInterface $catchUpHookFactory + * @param CatchUpHookFactoryInterface $catchUpHookFactory * @return void * @api */ diff --git a/Classes/Projection/CatchUpHookFactories.php b/Classes/Projection/CatchUpHookFactories.php index deabf534..f12fb3be 100644 --- a/Classes/Projection/CatchUpHookFactories.php +++ b/Classes/Projection/CatchUpHookFactories.php @@ -4,18 +4,22 @@ namespace Neos\ContentRepository\Core\Projection; -use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; /** + * @implements CatchUpHookFactoryInterface * @internal */ final class CatchUpHookFactories implements CatchUpHookFactoryInterface { /** - * @var array + * @var array> */ private array $catchUpHookFactories; + /** + * @param CatchUpHookFactoryInterface ...$catchUpHookFactories + */ private function __construct(CatchUpHookFactoryInterface ...$catchUpHookFactories) { $this->catchUpHookFactories = $catchUpHookFactories; @@ -26,6 +30,10 @@ public static function create(): self return new self(); } + /** + * @param CatchUpHookFactoryInterface $catchUpHookFactory + * @return self + */ public function with(CatchUpHookFactoryInterface $catchUpHookFactory): self { if ($this->has($catchUpHookFactory::class)) { @@ -44,9 +52,9 @@ private function has(string $catchUpHookFactoryClassName): bool return array_key_exists($catchUpHookFactoryClassName, $this->catchUpHookFactories); } - public function build(ContentRepository $contentRepository): CatchUpHookInterface + public function build(ContentRepositoryId $contentRepositoryId, ProjectionStateInterface $projectionState): CatchUpHookInterface { - $catchUpHooks = array_map(static fn(CatchUpHookFactoryInterface $catchUpHookFactory) => $catchUpHookFactory->build($contentRepository), $this->catchUpHookFactories); + $catchUpHooks = array_map(static fn(CatchUpHookFactoryInterface $catchUpHookFactory) => $catchUpHookFactory->build($contentRepositoryId, $projectionState), $this->catchUpHookFactories); return new DelegatingCatchUpHook(...$catchUpHooks); } } diff --git a/Classes/Projection/CatchUpHookFactoryInterface.php b/Classes/Projection/CatchUpHookFactoryInterface.php index ec84d096..10d918cd 100644 --- a/Classes/Projection/CatchUpHookFactoryInterface.php +++ b/Classes/Projection/CatchUpHookFactoryInterface.php @@ -4,12 +4,21 @@ namespace Neos\ContentRepository\Core\Projection; -use Neos\ContentRepository\Core\ContentRepository; +use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; /** + * @template T of ProjectionStateInterface * @api */ interface CatchUpHookFactoryInterface { - public function build(ContentRepository $contentRepository): CatchUpHookInterface; + /** + * Note that a catchup doesn't have access to the full content repository, as it would allow full recursion via handle and accessing other projections + * state is not safe as the other projection might not be behind - the order is undefined. + * + * @param ContentRepositoryId $contentRepositoryId the content repository the catchup was registered in + * @param ProjectionStateInterface&T $projectionState the state of the projection the catchup was registered to (Its only safe to access this projections state) + * @return CatchUpHookInterface + */ + public function build(ContentRepositoryId $contentRepositoryId, ProjectionStateInterface $projectionState): CatchUpHookInterface; } diff --git a/Classes/Projection/ContentGraph/ContentGraphReadModelInterface.php b/Classes/Projection/ContentGraph/ContentGraphReadModelInterface.php index e1298bc1..5f157f8d 100644 --- a/Classes/Projection/ContentGraph/ContentGraphReadModelInterface.php +++ b/Classes/Projection/ContentGraph/ContentGraphReadModelInterface.php @@ -24,7 +24,11 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; /** - * @api for creating a custom content repository graph projection implementation, **not for users of the CR** + * This low level interface gives access to the content graph and workspaces + * + * Generally this is not accessible for users of the CR, except for registering a catchup-hook on the content graph + * + * @api as dependency in catchup hooks and for creating a custom content repository graph projection implementation */ interface ContentGraphReadModelInterface extends ProjectionStateInterface { diff --git a/Classes/Projection/ProjectionsAndCatchUpHooks.php b/Classes/Projection/ProjectionsAndCatchUpHooks.php index 255e59b7..d7b674ba 100644 --- a/Classes/Projection/ProjectionsAndCatchUpHooks.php +++ b/Classes/Projection/ProjectionsAndCatchUpHooks.php @@ -26,6 +26,7 @@ public function __construct( /** * @param ProjectionInterface $projection + * @return ?CatchUpHookFactoryInterface */ public function getCatchUpHookFactoryForProjection(ProjectionInterface $projection): ?CatchUpHookFactoryInterface {