Skip to content

Commit

Permalink
refactor #800 Init serialize processor (loic425)
Browse files Browse the repository at this point in the history
This PR was merged into the 1.11 branch.

Discussion
----------

| Q               | A
| --------------- | -----
| Bug fix?        | no
| New feature?    | yes
| BC breaks?      | no
| Deprecations?   | no
| Related tickets | 
| License         | MIT

Based on #799 


Commits
-------

98378b6 PHPUnit tests for Symfony serialize listener
6e60965 Init serialize processor
7d0d151 Apply suggestions from code review
  • Loading branch information
lchrusciel authored Dec 6, 2023
2 parents 2ea35c7 + 7d0d151 commit 3092d5d
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Component\Resource\Tests\Symfony\Serializer\State;

use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Sylius\Component\Resource\src\Symfony\Serializer\State\SerializeProcessor;
use Sylius\Resource\Context\Context;
use Sylius\Resource\Context\Option\RequestOption;
use Sylius\Resource\Metadata\HttpOperation;
use Sylius\Resource\State\ProcessorInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Serializer\SerializerInterface;
use Webmozart\Assert\Assert;

final class SerializeProcessorTest extends TestCase
{
use ProphecyTrait;

private ProcessorInterface|ObjectProphecy $decorated;

private SerializerInterface|ObjectProphecy $serializer;

private SerializeProcessor $serializeProcessor;

protected function setUp(): void
{
$this->decorated = $this->prophesize(ProcessorInterface::class);
$this->serializer = $this->prophesize(SerializerInterface::class);

$this->serializeProcessor = new SerializeProcessor(
$this->decorated->reveal(),
$this->serializer->reveal(),
);
}

/** @test */
public function it_serializes_data_to_the_requested_format(): void
{
$request = $this->prophesize(Request::class);
$operation = $this->prophesize(HttpOperation::class);
$data = $this->prophesize(\stdClass::class);

$context = new Context(new RequestOption($request->reveal()));

$this->decorated->process($data, $operation, $context)->willReturn($data);

$request->getRequestFormat()->willReturn('json');

$operation->canSerialize()->willReturn(null)->shouldBeCalled();
$operation->getNormalizationContext()->willReturn([]);

$this->serializer->serialize($data, 'json', [])->willReturn('serialized_data')->shouldBeCalled();

$result = $this->serializeProcessor->process($data, $operation->reveal(), $context);

Assert::eq($result, 'serialized_data');
}

/** @test */
public function it_serializes_data_to_the_requested_format_with_normalization_context(): void
{
$request = $this->prophesize(Request::class);
$operation = $this->prophesize(HttpOperation::class);
$data = $this->prophesize(\stdClass::class);

$context = new Context(new RequestOption($request->reveal()));

$this->decorated->process($data, $operation, $context)->willReturn($data);

$request->getRequestFormat()->willReturn('json');

$operation->canSerialize()->willReturn(null)->shouldBeCalled();
$operation->getNormalizationContext()->willReturn(['groups' => ['dummy:read']]);

$this->serializer->serialize($data, 'json', ['groups' => ['dummy:read']])->willReturn('serialized_data')->shouldBeCalled();

$result = $this->serializeProcessor->process($data, $operation->reveal(), $context);

Assert::eq($result, 'serialized_data');
}

/** @test */
public function it_does_nothing_when_format_is_html(): void
{
$request = $this->prophesize(Request::class);
$operation = $this->prophesize(HttpOperation::class);
$data = $this->prophesize(\stdClass::class);

$context = new Context(new RequestOption($request->reveal()));

$this->decorated->process($data, $operation, $context)->willReturn($data);

$request->getRequestFormat()->willReturn('html');

$this->serializer->serialize($data, 'json', [])->willReturn('serialized_data')->shouldNotBeCalled();

$result = $this->serializeProcessor->process($data, $operation->reveal(), $context);

Assert::eq($result, $data->reveal());
}

/** @test */
public function it_throws_an_exception_when_serializer_is_not_available(): void
{
$request = $this->prophesize(Request::class);
$operation = $this->prophesize(HttpOperation::class);
$data = $this->prophesize(\stdClass::class);

$serializeProcessor = new SerializeProcessor($this->decorated->reveal(), null);

$context = new Context(new RequestOption($request->reveal()));

$this->decorated->process($data, $operation, $context)->willReturn($data);

$request->getRequestFormat()->willReturn('json', []);

$this->serializer->serialize($data, 'json')->willReturn('serialized_data')->shouldNotBeCalled();

$this->expectException(\LogicException::class);
$this->expectExceptionMessage('You can not use the "json" format if the Serializer is not available. Try running "composer require symfony/serializer".');

$serializeProcessor->process($data, $operation->reveal(), $context);
}

/** @test */
public function it_does_nothing_if_operation_cannot_be_serialized(): void
{
$request = $this->prophesize(Request::class);
$operation = $this->prophesize(HttpOperation::class);
$data = $this->prophesize(\stdClass::class);

$context = new Context(new RequestOption($request->reveal()));

$this->decorated->process($data, $operation, $context)->willReturn($data);

$request->getRequestFormat()->willReturn('json');

$operation->canSerialize()->willReturn(false)->shouldBeCalled();

$this->serializer->serialize($data, 'json', [])->willReturn('serialized_data')->shouldNotBeCalled();

$result = $this->serializeProcessor->process($data, $operation->reveal(), $context);

Assert::eq($result, $data->reveal());
}
}
59 changes: 59 additions & 0 deletions src/Component/src/Symfony/Serializer/State/SerializeProcessor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\Component\Resource\src\Symfony\Serializer\State;

use Sylius\Resource\Context\Context;
use Sylius\Resource\Context\Option\RequestOption;
use Sylius\Resource\Metadata\Operation;
use Sylius\Resource\State\ProcessorInterface;
use Symfony\Component\Serializer\SerializerInterface;

/**
* Serializes the data to the requested format.
*/
final class SerializeProcessor implements ProcessorInterface
{
public function __construct(
private ProcessorInterface $decorated,
private ?SerializerInterface $serializer,
) {
}

public function process(mixed $data, Operation $operation, Context $context): mixed
{
$data = $this->decorated->process($data, $operation, $context);

$request = $context->get(RequestOption::class)?->request();

if (null === $request) {
return $data;
}

/** @var string $format */
$format = $request->getRequestFormat();

if (
'html' === $format ||
!($operation->canSerialize() ?? true)
) {
return $data;
}

if (null === $this->serializer) {
throw new \LogicException(sprintf('You can not use the "%s" format if the Serializer is not available. Try running "composer require symfony/serializer".', $format));
}

return $this->serializer->serialize($data, $format, $operation->getNormalizationContext() ?? []);
}
}

0 comments on commit 3092d5d

Please sign in to comment.