From fce885107789c9247f64861a4f818b0daf57fba2 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 3 Feb 2024 18:17:40 +0100 Subject: [PATCH] TASK: Introduce `NeosFusionContextSerializer` to replace node property converters --- .../Classes/Core/Cache/ContentCache.php | 4 +- .../Core/Cache/FusionContextSerializer.php | 10 ++ .../Core/Cache/RuntimeContentCache.php | 6 +- .../Unit/Core/Cache/ContentCacheTest.php | 13 +- .../Cache/NeosFusionContextSerializer.php | 128 ++++++++++++++++++ .../JsonStringToNodeConverter.php | 96 ------------- .../NodeToJsonStringSerializer.php | 87 ------------ Neos.Neos/Configuration/Objects.yaml | 5 + 8 files changed, 150 insertions(+), 199 deletions(-) create mode 100644 Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php delete mode 100644 Neos.Neos/Classes/TypeConverter/JsonStringToNodeConverter.php delete mode 100644 Neos.Neos/Classes/TypeConverter/NodeToJsonStringSerializer.php diff --git a/Neos.Fusion/Classes/Core/Cache/ContentCache.php b/Neos.Fusion/Classes/Core/Cache/ContentCache.php index 1558ea2a4b4..687bb041658 100644 --- a/Neos.Fusion/Classes/Core/Cache/ContentCache.php +++ b/Neos.Fusion/Classes/Core/Cache/ContentCache.php @@ -125,7 +125,7 @@ public function createCacheSegment($content, $fusionPath, array $cacheIdentifier * * @param string $content The content rendered by the Fusion Runtime * @param string $fusionPath The Fusion path that rendered the content, for example "page/body/parts/breadcrumbMenu" - * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object + * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object * @return string The original content, but with additional markers added */ public function createUncachedSegment($content, $fusionPath, array $serializedContext) @@ -141,7 +141,7 @@ public function createUncachedSegment($content, $fusionPath, array $serializedCo * * @param string $content The content rendered by the Fusion Runtime * @param string $fusionPath The Fusion path that rendered the content, for example "page/body/parts/breadcrumbMenu" - * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object + * @param array $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object * @param array $cacheIdentifierValues * @param array $tags Tags to add to the cache entry * @param integer $lifetime Lifetime of the cache segment in seconds. NULL for the default lifetime and 0 for unlimited lifetime. diff --git a/Neos.Fusion/Classes/Core/Cache/FusionContextSerializer.php b/Neos.Fusion/Classes/Core/Cache/FusionContextSerializer.php index f3897166fea..ab22006fb87 100644 --- a/Neos.Fusion/Classes/Core/Cache/FusionContextSerializer.php +++ b/Neos.Fusion/Classes/Core/Cache/FusionContextSerializer.php @@ -26,11 +26,18 @@ public function __construct( ) { } + /** + * @param array $context + */ public function denormalize(mixed $data, string $type, string $format = null, array $context = []) { return $this->propertyMapper->convert($data, $type); } + /** + * @param array $context + * @return array + */ public function normalize(mixed $object, string $format = null, array $context = []) { return $this->propertyMapper->convert($object, 'string'); @@ -46,6 +53,9 @@ public function supportsNormalization(mixed $data, string $format = null) throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978913); } + /** + * @return array + */ public function getSupportedTypes(?string $format): array { throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978914); diff --git a/Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php b/Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php index 123d172081a..d673724605c 100644 --- a/Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php +++ b/Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php @@ -74,7 +74,7 @@ public function __construct(Runtime $runtime) $this->runtime = $runtime; } - public function injectSerializer(NormalizerInterface&DenormalizerInterface $serializer) + public function injectSerializer(NormalizerInterface&DenormalizerInterface $serializer): void { $this->serializer = $serializer; } @@ -375,7 +375,7 @@ protected function buildCacheTags(array $configuration, $fusionPath, $fusionObje * {@see self::unserializeContext()} * * @param array $contextVariables - * @return array + * @return array */ protected function serializeContext(array $contextVariables): array { @@ -394,7 +394,7 @@ protected function serializeContext(array $contextVariables): array * Decodes and serialized array of context variables to its original values * {@see self::serializeContext()} * - * @param array $contextArray + * @param array $contextArray * @return array */ protected function unserializeContext(array $contextArray): array diff --git a/Neos.Fusion/Tests/Unit/Core/Cache/ContentCacheTest.php b/Neos.Fusion/Tests/Unit/Core/Cache/ContentCacheTest.php index 451cf75a57f..1995481e55b 100644 --- a/Neos.Fusion/Tests/Unit/Core/Cache/ContentCacheTest.php +++ b/Neos.Fusion/Tests/Unit/Core/Cache/ContentCacheTest.php @@ -16,7 +16,6 @@ use Neos\Cache\EnvironmentConfiguration; use Neos\Cache\Frontend\FrontendInterface; use Neos\Cache\Frontend\StringFrontend; -use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Security\Context; use Neos\Flow\Tests\UnitTestCase; use Neos\Fusion\Core\Cache\ContentCache; @@ -193,10 +192,6 @@ public function createUncachedSegmentAndProcessCacheSegmentsDoesWorkWithCacheSeg { $contentCache = new ContentCache(); - $mockPropertyMapper = $this->createMock(PropertyMapper::class); - $mockPropertyMapper->expects(self::any())->method('convert')->will($this->returnArgument(0)); - $this->inject($contentCache, 'propertyMapper', $mockPropertyMapper); - $mockCache = $this->createMock(FrontendInterface::class); $this->inject($contentCache, 'cache', $mockCache); @@ -205,7 +200,7 @@ public function createUncachedSegmentAndProcessCacheSegmentsDoesWorkWithCacheSeg $content = $contentCache->createUncachedSegment( $invalidContent, 'uncached.fusion.path', - ['node' => 'A node identifier'] + [] ); $output = $contentCache->processCacheSegments($content); @@ -223,10 +218,6 @@ public function getCachedSegmentWithExistingCacheEntryReplacesNestedCachedSegmen $mockSecurityContext = $this->createMock(Context::class); $this->inject($contentCache, 'securityContext', $mockSecurityContext); - $mockPropertyMapper = $this->createMock(PropertyMapper::class); - $mockPropertyMapper->expects(self::any())->method('convert')->will($this->returnArgument(0)); - $this->inject($contentCache, 'propertyMapper', $mockPropertyMapper); - $mockContext = $this->getMockBuilder(EnvironmentConfiguration::class)->disableOriginalConstructor()->getMock(); $cacheBackend = new TransientMemoryBackend($mockContext); $cacheFrontend = new StringFrontend('foo', $cacheBackend); @@ -246,7 +237,7 @@ public function getCachedSegmentWithExistingCacheEntryReplacesNestedCachedSegmen $innerUncachedContent = $contentCache->createUncachedSegment( $uncachedCommandOutput, 'some.fusionh.path.innerUncached', - ['node' => 'A node identifier'] + [] ); $outerContentStart = 'You can nest cached segments like <'; diff --git a/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php b/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php new file mode 100644 index 00000000000..b3f79121830 --- /dev/null +++ b/Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php @@ -0,0 +1,128 @@ + $context + */ + public function denormalize(mixed $data, string $type, string $format = null, array $context = []) + { + if ($type === Node::class) { + return $this->deserializeNode($data); + } + return $this->fusionContextSerializer->denormalize($data, $type, $format, $context); + } + + /** + * @param array $context + * @return array + */ + public function normalize(mixed $object, string $format = null, array $context = []) + { + if ($object instanceof Node) { + return $this->serializeNode($object); + } + return $this->fusionContextSerializer->normalize($object, $format, $context); + } + + /** + * @param array $serializedNode + */ + private function deserializeNode(array $serializedNode): Node + { + $contentRepositoryId = ContentRepositoryId::fromString($serializedNode['contentRepositoryId']); + + $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); + + $workspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::fromString($serializedNode['workspaceName'])); + if (!$workspace) { + throw new \RuntimeException(sprintf('Could not find workspace while trying to convert node from array %s.', json_encode($serializedNode)), 1699782153); + } + + $subgraph = $contentRepository->getContentGraph()->getSubgraph( + $workspace->currentContentStreamId, + DimensionSpacePoint::fromArray($serializedNode['dimensionSpacePoint']), + $workspace->isPublicWorkspace() + ? VisibilityConstraints::frontend() + : VisibilityConstraints::withoutRestrictions() + ); + + $node = $subgraph->findNodeById(NodeAggregateId::fromString($serializedNode['nodeAggregateId'])); + if (!$node) { + throw new \RuntimeException(sprintf('Could not find serialized node %s.', json_encode($serializedNode)), 1699782153); + } + return $node; + } + + /** + * @return array + */ + private function serializeNode(Node $source): array + { + $contentRepository = $this->contentRepositoryRegistry->get( + $source->subgraphIdentity->contentRepositoryId + ); + + $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($source->subgraphIdentity->contentStreamId); + + if (!$workspace) { + throw new \RuntimeException(sprintf('Could not fetch workspace for node (%s) in content stream (%s).', $source->nodeAggregateId->value, $source->subgraphIdentity->contentStreamId->value), 1699780153); + } + + return [ + 'contentRepositoryId' => $source->subgraphIdentity->contentRepositoryId->value, + 'workspaceName' => $workspace->workspaceName->value, + 'dimensionSpacePoint' => $source->subgraphIdentity->dimensionSpacePoint->jsonSerialize(), + 'nodeAggregateId' => $source->nodeAggregateId->value + ]; + } + + public function supportsDenormalization(mixed $data, string $type, string $format = null) + { + throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978912); + } + + public function supportsNormalization(mixed $data, string $format = null) + { + throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978913); + } + + /** + * @return array + */ + public function getSupportedTypes(?string $format): array + { + throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978914); + } +} diff --git a/Neos.Neos/Classes/TypeConverter/JsonStringToNodeConverter.php b/Neos.Neos/Classes/TypeConverter/JsonStringToNodeConverter.php deleted file mode 100644 index e652bf64808..00000000000 --- a/Neos.Neos/Classes/TypeConverter/JsonStringToNodeConverter.php +++ /dev/null @@ -1,96 +0,0 @@ - - */ - protected $sourceTypes = ['string']; - - /** - * @var string - */ - protected $targetType = Node::class; - - /** - * @var integer - */ - protected $priority = 2; - - #[Flow\Inject] - protected ContentRepositoryRegistry $contentRepositoryRegistry; - - /** - * @param string $source - * @param string $targetType - * @param array $subProperties - * @return Node|null|\Neos\Error\Messages\Error - */ - public function convertFrom( - $source, - $targetType = null, - array $subProperties = [], - PropertyMappingConfigurationInterface $configuration = null - ) { - assert(is_string($source)); - - try { - $serializedNode = json_decode($source, true, 512, JSON_THROW_ON_ERROR); - } catch (\JsonException $e) { - return new \Neos\Error\Messages\Error(sprintf('Cannot convert assumed json string %s to node. %s', $source, $e->getMessage())); - } - - $contentRepositoryId = ContentRepositoryId::fromString($serializedNode['contentRepositoryId']); - - $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - - $workspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::fromString($serializedNode['workspaceName'])); - if (!$workspace) { - return new \Neos\Error\Messages\Error('Could not find workspace while trying to convert node from json string %s.', 1699782153, [$source]); - } - - $subgraph = $contentRepository->getContentGraph()->getSubgraph( - $workspace->currentContentStreamId, - DimensionSpacePoint::fromArray($serializedNode['dimensionSpacePoint']), - $workspace->isPublicWorkspace() - ? VisibilityConstraints::frontend() - : VisibilityConstraints::withoutRestrictions() - ); - - return $subgraph->findNodeById(NodeAggregateId::fromString($serializedNode['nodeAggregateId'])); - } -} diff --git a/Neos.Neos/Classes/TypeConverter/NodeToJsonStringSerializer.php b/Neos.Neos/Classes/TypeConverter/NodeToJsonStringSerializer.php deleted file mode 100644 index 56dbb147f70..00000000000 --- a/Neos.Neos/Classes/TypeConverter/NodeToJsonStringSerializer.php +++ /dev/null @@ -1,87 +0,0 @@ - - */ - protected $sourceTypes = [Node::class]; - - /** - * @var string - */ - protected $targetType = 'string'; - - /** - * @var integer - */ - protected $priority = 1; - - /** - * @param Node $source - * @param string $targetType - * @param array $subProperties - * @return mixed|\Neos\Error\Messages\Error|string|null - */ - public function convertFrom( - $source, - $targetType = null, - array $subProperties = [], - PropertyMappingConfigurationInterface $configuration = null - ) { - assert($source instanceof Node); - - $contentRepository = $this->contentRepositoryRegistry->get( - $source->subgraphIdentity->contentRepositoryId - ); - - $workspace = $contentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId($source->subgraphIdentity->contentStreamId); - - if (!$workspace) { - return new \Neos\Error\Messages\Error('Could not fetch workspace for node (%s) in content stream (%s).', 1699780153, [$source->nodeAggregateId->value, $source->subgraphIdentity->contentStreamId->value]); - } - - return json_encode([ - 'contentRepositoryId' => $source->subgraphIdentity->contentRepositoryId->value, - 'workspaceName' => $workspace->workspaceName->value, - 'dimensionSpacePoint' => $source->subgraphIdentity->dimensionSpacePoint->jsonSerialize(), - 'nodeAggregateId' => $source->nodeAggregateId->value - ], JSON_THROW_ON_ERROR); - } -} diff --git a/Neos.Neos/Configuration/Objects.yaml b/Neos.Neos/Configuration/Objects.yaml index fdc548b70fe..56b7ac6e23e 100644 --- a/Neos.Neos/Configuration/Objects.yaml +++ b/Neos.Neos/Configuration/Objects.yaml @@ -20,6 +20,11 @@ Neos\Neos\Domain\Service\FusionConfigurationCache: 2: setting: "Neos.Neos.fusion.enableObjectTreeCache" +Neos\Fusion\Core\Cache\RuntimeContentCache: + properties: + serializer: + object: Neos\Neos\Fusion\Cache\NeosFusionContextSerializer + Neos\Neos\FrontendRouting\FrontendNodeRoutePartHandlerInterface: className: Neos\Neos\FrontendRouting\EventSourcedFrontendNodeRoutePartHandler