Skip to content

Commit

Permalink
TASK: Introduce NeosFusionContextSerializer to replace node propert…
Browse files Browse the repository at this point in the history
…y converters
  • Loading branch information
mhsdesign committed Feb 4, 2024
1 parent 76a72a8 commit fce8851
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 199 deletions.
4 changes: 2 additions & 2 deletions Neos.Fusion/Classes/Core/Cache/ContentCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<Acme.Com:Page>/body<Acme.Demo:DefaultPageTemplate>/parts/breadcrumbMenu"
* @param array<string, array{type: string, value: string}> $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object
* @param array<string, array{type: string, value: mixed}> $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)
Expand All @@ -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<Acme.Com:Page>/body<Acme.Demo:DefaultPageTemplate>/parts/breadcrumbMenu"
* @param array<string, array{type: string, value: string}> $serializedContext Serialized Fusion context variables which are needed to correctly render the specified Fusion object
* @param array<string, array{type: string, value: mixed}> $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.
Expand Down
10 changes: 10 additions & 0 deletions Neos.Fusion/Classes/Core/Cache/FusionContextSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,18 @@ public function __construct(
) {
}

/**
* @param array<int|string,mixed> $context
*/
public function denormalize(mixed $data, string $type, string $format = null, array $context = [])
{
return $this->propertyMapper->convert($data, $type);
}

/**
* @param array<int|string,mixed> $context
* @return array<int|string,mixed>
*/
public function normalize(mixed $object, string $format = null, array $context = [])
{
return $this->propertyMapper->convert($object, 'string');
Expand All @@ -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<int|string,mixed>
*/
public function getSupportedTypes(?string $format): array
{
throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978914);
Expand Down
6 changes: 3 additions & 3 deletions Neos.Fusion/Classes/Core/Cache/RuntimeContentCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -375,7 +375,7 @@ protected function buildCacheTags(array $configuration, $fusionPath, $fusionObje
* {@see self::unserializeContext()}
*
* @param array<string, mixed> $contextVariables
* @return array<string, array{type: string, value: string}>
* @return array<string, array{type: string, value: mixed}>
*/
protected function serializeContext(array $contextVariables): array
{
Expand All @@ -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<string, array{type: string, value: string}> $contextArray
* @param array<string, array{type: string, value: mixed}> $contextArray
* @return array<string, mixed>
*/
protected function unserializeContext(array $contextArray): array
Expand Down
13 changes: 2 additions & 11 deletions Neos.Fusion/Tests/Unit/Core/Cache/ContentCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -205,7 +200,7 @@ public function createUncachedSegmentAndProcessCacheSegmentsDoesWorkWithCacheSeg
$content = $contentCache->createUncachedSegment(
$invalidContent,
'uncached.fusion.path',
['node' => 'A node identifier']
[]
);

$output = $contentCache->processCacheSegments($content);
Expand All @@ -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);
Expand All @@ -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 <';
Expand Down
128 changes: 128 additions & 0 deletions Neos.Neos/Classes/Fusion/Cache/NeosFusionContextSerializer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

declare(strict_types=1);

namespace Neos\Neos\Fusion\Cache;

use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
use Neos\ContentRepository\Core\Factory\ContentRepositoryId;
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
use Neos\Fusion\Core\Cache\FusionContextSerializer;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
* Serializer for Fusion's [at]cache.context values
*
* Uses the flow property mapper as implementation.
* It relies on a converter being available from the context value type to string and reverse.
*
* {@see FusionContextSerializer}
*
* @internal
*/
final class NeosFusionContextSerializer implements NormalizerInterface, DenormalizerInterface
{
public function __construct(
private readonly FusionContextSerializer $fusionContextSerializer,
private readonly ContentRepositoryRegistry $contentRepositoryRegistry
) {
}

/**
* @param array<int|string,mixed> $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<int|string,mixed> $context
* @return array<int|string,mixed>
*/
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<int|string,mixed> $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<int|string,mixed>
*/
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<int|string,mixed>
*/
public function getSupportedTypes(?string $format): array
{
throw new \BadMethodCallException(sprintf('Method %s is not supported.', __METHOD__), 1706978914);
}
}
96 changes: 0 additions & 96 deletions Neos.Neos/Classes/TypeConverter/JsonStringToNodeConverter.php

This file was deleted.

Loading

0 comments on commit fce8851

Please sign in to comment.