Skip to content

Commit

Permalink
Add base normalizer (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
fre5h authored Dec 3, 2021
1 parent ba772b8 commit 687b2f9
Show file tree
Hide file tree
Showing 6 changed files with 423 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Serializer/CircularReferenceHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php
/*
* This file is part of the StfalconApiBundle.
*
* (c) Stfalcon LLC <stfalcon.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 StfalconStudio\ApiBundle\Serializer;

/**
* CircularReferenceHandler.
*/
class CircularReferenceHandler
{
/**
* @param mixed $object
*
* @return callable
*/
public function __invoke($object): callable
{
return static function () use ($object) {
return $object->getId();
};
}
}
79 changes: 79 additions & 0 deletions Serializer/Normalizer/ConstraintViolationListNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the StfalconApiBundle.
*
* (c) Stfalcon LLC <stfalcon.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 StfalconStudio\ApiBundle\Serializer\Normalizer;

use Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer as SymfonyConstraintViolationListNormalizer;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Validator\ConstraintViolationListInterface;

/**
* ConstraintViolationListNormalizer.
*/
class ConstraintViolationListNormalizer implements NormalizerInterface
{
private SymfonyConstraintViolationListNormalizer $symfonyConstraintViolationListNormalizer;

/**
* @param SymfonyConstraintViolationListNormalizer $symfonyConstraintViolationListNormalizer
*/
public function __construct(SymfonyConstraintViolationListNormalizer $symfonyConstraintViolationListNormalizer)
{
$this->symfonyConstraintViolationListNormalizer = $symfonyConstraintViolationListNormalizer;
}

/**
* @param mixed $data
* @param string|null $format
*
* @return bool
*/
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof ConstraintViolationListInterface;
}

/**
* Clear the "detail" field from prefixed property paths.
*
* From the parent class:
* {
* "detail": "propertyPath1: Error description 1\npropertyPath2: Error description 2",
* }
* After additional processing:
* {
* "detail": "Error description 1\nError description 2",
* }
*
* {@inheritdoc}
*/
public function normalize($object, string $format = null, array $context = [])
{
$result = $this->symfonyConstraintViolationListNormalizer->normalize($object, $format, $context);

if (\is_array($result) && \array_key_exists('detail', $result) && $result['detail']) {
$messages = explode("\n", $result['detail']);

foreach ($messages as &$message) {
$position = mb_strpos($message, ': ');
if (\is_int($position)) {
$message = mb_substr($message, $position + 2);
}
}
unset($message);

$result['detail'] = implode("\n", $messages);
}

return $result;
}
}
55 changes: 55 additions & 0 deletions Serializer/Normalizer/JsonSchemaErrorNormalizer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php
/*
* This file is part of the StfalconApiBundle.
*
* (c) Stfalcon LLC <stfalcon.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 StfalconStudio\ApiBundle\Serializer\Normalizer;

use JsonSchema\Validator as JsonSchemaValidator;
use StfalconStudio\ApiBundle\Exception\RuntimeException;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

/**
* JsonSchemaErrorNormalizer.
*/
class JsonSchemaErrorNormalizer implements NormalizerInterface
{
/**
* {@inheritdoc}
*/
public function supportsNormalization($data, string $format = null): bool
{
return $data instanceof JsonSchemaValidator;
}

/**
* @param JsonSchemaValidator|mixed $object
* @param string|null $format
* @param array $context
*
* @return array
*/
public function normalize($object, string $format = null, array $context = []): array
{
if (!$object instanceof JsonSchemaValidator) {
throw new RuntimeException(sprintf('Object of class %s is not instance of %s', \get_class($object), JsonSchemaValidator::class));
}

$data = [];

foreach ($object->getErrors() as ['constraint' => $constraint, 'property' => $property, 'message' => $message]) {
$data[$constraint][] = [
$property => $message,
];
}

return $data;
}
}
39 changes: 39 additions & 0 deletions Tests/Serializer/CircularReferenceHandlerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the StfalconApiBundle.
*
* (c) Stfalcon LLC <stfalcon.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 StfalconStudio\ApiBundle\Tests\Serializer;

use PHPUnit\Framework\TestCase;
use StfalconStudio\ApiBundle\Model\UUID\UuidInterface;
use StfalconStudio\ApiBundle\Serializer\CircularReferenceHandler;

final class CircularReferenceHandlerTest extends TestCase
{
public function testHandleById(): void
{
$handler = new CircularReferenceHandler();

$object = $this
->getMockBuilder(UuidInterface::class)
->disableOriginalConstructor()
->onlyMethods(['getId'])
->getMock()
;

$object
->expects(self::once())
->method('getId')
;

$handler($object)(); // Execute callback
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php
/*
* This file is part of the StfalconApiBundle.
*
* (c) Stfalcon LLC <stfalcon.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 StfalconStudio\ApiBundle\Tests\Serializer\Normalizer;

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use StfalconStudio\ApiBundle\Serializer\Normalizer\ConstraintViolationListNormalizer;
use Symfony\Component\Serializer\Normalizer\ConstraintViolationListNormalizer as SymfonyConstraintViolationListNormalizer;
use Symfony\Component\Validator\ConstraintViolationListInterface;

final class ConstraintViolationListNormalizerTest extends TestCase
{
/** @var SymfonyConstraintViolationListNormalizer|MockObject */
private $symfonyNormalizer;

private ConstraintViolationListNormalizer $normalizer;

protected function setUp(): void
{
$this->symfonyNormalizer = $this->createMock(SymfonyConstraintViolationListNormalizer::class);
$this->normalizer = new ConstraintViolationListNormalizer($this->symfonyNormalizer);
}

protected function tearDown(): void
{
unset(
$this->symfonyNormalizer,
$this->normalizer,
);
}

/**
* @param string $originDetail
* @param string $resultDetail
*
* @dataProvider dataProviderForTestNormalize
*/
public function testNormalize(string $originDetail, string $resultDetail): void
{
$object = new \stdClass();
$format = 'json';
$context = ['some'];

$this->symfonyNormalizer
->expects(self::once())
->method('normalize')
->with($object, $format, $context)
->willReturn(['detail' => $originDetail])
;

$result = (array) $this->normalizer->normalize($object, $format, $context);

self::assertArrayHasKey('detail', $result);
self::assertSame($resultDetail, $result['detail']);
}

public static function dataProviderForTestNormalize(): iterable
{
yield [
'origin_detail' => 'field1: Error description.',
'result_detail' => 'Error description.',
];
yield [
'origin_detail' => "field1: Error description 1.\nfield2: Error description 2.",
'result_detail' => "Error description 1.\nError description 2.",
];
yield [
'origin_detail' => 'Error description.',
'result_detail' => 'Error description.',
];
yield [
'origin_detail' => "field1: Error :description 1.\nfield2: Error :description 2.",
'result_detail' => "Error :description 1.\nError :description 2.",
];
}

public function testNotSupportsNormalization(): void
{
self::assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
}

public function testSupportsNormalization(): void
{
$error = $this->createMock(ConstraintViolationListInterface::class);

self::assertTrue($this->normalizer->supportsNormalization($error));
}
}
Loading

0 comments on commit 687b2f9

Please sign in to comment.