Skip to content

Commit

Permalink
fix: prevents recomputing normalizationcontext
Browse files Browse the repository at this point in the history
  • Loading branch information
maxhelias committed Oct 7, 2024
1 parent f7f605d commit 413dd77
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 5 deletions.
10 changes: 6 additions & 4 deletions src/State/Processor/SerializeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
trigger_deprecation('api-platform/core', '3.3', 'The resource class on the router is not the same as the operation\'s class which leads to wrong behaviors. Prefer using "stateOptions" if you need to change the entity class.');
}

$serializerContext = $this->serializerContextBuilder->createFromRequest($request, true, [
'resource_class' => $class,
'operation' => $operation,
]);
if (null === ($serializerContext = $request->attributes->get('_api_normalization_context'))) {
$serializerContext = $this->serializerContextBuilder->createFromRequest($request, true, [
'resource_class' => $class,
'operation' => $operation,
]);
}

$serializerContext['uri_variables'] = $uriVariables;

Expand Down
48 changes: 48 additions & 0 deletions src/State/Tests/Processor/SerializeProcessorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the API Platform project.
*
* (c) Kévin Dunglas <dunglas@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace ApiPlatform\State\Tests\Processor;

use ApiPlatform\Metadata\Get;
use ApiPlatform\State\Processor\SerializeProcessor;
use ApiPlatform\State\ProcessorInterface;
use ApiPlatform\State\SerializerContextBuilderInterface;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\SerializerInterface;

class SerializeProcessorTest extends TestCase
{
public function testSerializeItemOperationWithComputedNormalizationContext(): void
{
$data = new \stdClass();
$operation = new Get(class: \stdClass::class, serialize: true);
$exeptedData = '{}';

$request = new Request([], [], ['_api_resource_class' => \stdClass::class, '_api_normalization_context' => ['resource_class' => \stdClass::class, 'operation' => $operation, 'groups' => ['a']]]);
$request->setRequestFormat('json');

$decorated = $this->createStub(ProcessorInterface::class);
$decorated->method('process')->willReturn($exeptedData);

$serializer = $this->createMock(SerializerInterface::class);
$serializer->expects($this->once())->method('serialize')->with($data, 'json')->willReturn($exeptedData);

$serializerContextBuilder = $this->createMock(SerializerContextBuilderInterface::class);
$serializerContextBuilder->expects($this->never())->method('createFromRequest');

$processor = new SerializeProcessor($decorated, $serializer, $serializerContextBuilder);

$this->assertEquals($exeptedData, $processor->process($data, $operation, [], ['request' => $request]));
}
}
5 changes: 4 additions & 1 deletion src/Symfony/EventListener/SerializeListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ public function onKernelView(ViewEvent $event): void
return;
}

$context = $this->serializerContextBuilder->createFromRequest($request, true, $attributes);
if (null === ($context = $request->attributes->get('_api_normalization_context'))) {
$context = $this->serializerContextBuilder->createFromRequest($request, true, $attributes);
}

if (isset($context['output']) && \array_key_exists('class', $context['output']) && null === $context['output']['class']) {
$event->setControllerResult(null);

Expand Down
41 changes: 41 additions & 0 deletions tests/Symfony/EventListener/SerializeListenerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,47 @@ public function testSerializeItemOperation(): void
$this->assertSame('bar', $event->getControllerResult());
}

public function testSerializeItemOperationWithComputedNormalizationContext(): void
{
$serializerProphecy = $this->prophesize(SerializerInterface::class);
$serializerProphecy
->serialize(
Argument::any(),
'xml',
Argument::allOf(
Argument::that(fn (array $context) => $context['resources'] instanceof ResourceList && $context['resources_to_push'] instanceof ResourceList),
Argument::withEntry('request_uri', ''),
Argument::withEntry('resource_class', 'Foo'),
Argument::withEntry('operation_name', 'get')
)
)
->willReturn('bar')
->shouldBeCalled();

$serializerContextBuilderProphecy = $this->prophesize(SerializerContextBuilderInterface::class);
$serializerContextBuilderProphecy
->createFromRequest(
Argument::type(Request::class),
true,
Argument::type('array')
)
->shouldNotBeCalled();

$request = new Request([], [], ['_api_resource_class' => 'Foo', '_api_operation_name' => 'get', '_api_normalization_context' => ['request_uri' => '', 'resource_class' => 'Foo', 'operation_name' => 'get']]);
$request->setRequestFormat('xml');
$event = new ViewEvent(
$this->prophesize(HttpKernelInterface::class)->reveal(),
$request,
HttpKernelInterface::MAIN_REQUEST,
new \stdClass()
);

$listener = new SerializeListener($serializerProphecy->reveal(), $serializerContextBuilderProphecy->reveal());
$listener->onKernelView($event);

$this->assertSame('bar', $event->getControllerResult());
}

public function testEncode(): void
{
$serializerProphecy = $this->prophesize(SerializerInterface::class);
Expand Down

0 comments on commit 413dd77

Please sign in to comment.