From e51024c2cbd427100e0e4cff7c8becf27e72e925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Thu, 20 Dec 2018 12:03:24 +0100 Subject: [PATCH] Do not try do retrieve object in ItemNormalizer $context['resource_class'] is not defined (#2326) * Do not try do retrieve object in ItemNormalizer $context['resource_class'] is not defined We store some document in Elasticsearch and we hydrate them with the Serializer. But the `ItemNormalizer` raise an error because the `resource_class` is not defined. * Added some log when the $context['resource_class'] is missing in ItemNormalizer --- src/Serializer/ItemNormalizer.php | 28 +++++++++++++++++++- tests/Serializer/ItemNormalizerTest.php | 34 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/Serializer/ItemNormalizer.php b/src/Serializer/ItemNormalizer.php index 36bb1799f1d..f34540440d8 100644 --- a/src/Serializer/ItemNormalizer.php +++ b/src/Serializer/ItemNormalizer.php @@ -13,7 +13,17 @@ namespace ApiPlatform\Core\Serializer; +use ApiPlatform\Core\Api\IriConverterInterface; +use ApiPlatform\Core\Api\ResourceClassResolverInterface; +use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; use ApiPlatform\Core\Exception\InvalidArgumentException; +use ApiPlatform\Core\Metadata\Property\Factory\PropertyMetadataFactoryInterface; +use ApiPlatform\Core\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; +use Symfony\Component\Serializer\NameConverter\NameConverterInterface; /** * Generic item normalizer. @@ -24,6 +34,15 @@ */ class ItemNormalizer extends AbstractItemNormalizer { + private $logger; + + public function __construct(PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, PropertyMetadataFactoryInterface $propertyMetadataFactory, IriConverterInterface $iriConverter, ResourceClassResolverInterface $resourceClassResolver, PropertyAccessorInterface $propertyAccessor = null, NameConverterInterface $nameConverter = null, ClassMetadataFactoryInterface $classMetadataFactory = null, ItemDataProviderInterface $itemDataProvider = null, bool $allowPlainIdentifiers = false, LoggerInterface $logger = null) + { + parent::__construct($propertyNameCollectionFactory, $propertyMetadataFactory, $iriConverter, $resourceClassResolver, $propertyAccessor, $nameConverter, $classMetadataFactory, $itemDataProvider, $allowPlainIdentifiers); + + $this->logger = $logger ?: new NullLogger(); + } + /** * {@inheritdoc} * @@ -37,7 +56,14 @@ public function denormalize($data, $class, $format = null, array $context = []) throw new InvalidArgumentException('Update is not allowed for this operation.'); } - $this->updateObjectToPopulate($data, $context); + if (isset($context['resource_class'])) { + $this->updateObjectToPopulate($data, $context); + } else { + // See https://github.com/api-platform/core/pull/2326 to understand this message. + $this->logger->warning('The "resource_class" key is missing from the context.', [ + 'context' => $context, + ]); + } } return parent::denormalize($data, $class, $format, $context); diff --git a/tests/Serializer/ItemNormalizerTest.php b/tests/Serializer/ItemNormalizerTest.php index 76f830dbc88..d7930b1404c 100644 --- a/tests/Serializer/ItemNormalizerTest.php +++ b/tests/Serializer/ItemNormalizerTest.php @@ -186,4 +186,38 @@ public function testDenormalizeWithIdAndUpdateNotAllowed() $normalizer->setSerializer($serializerProphecy->reveal()); $normalizer->denormalize(['id' => '12', 'name' => 'hello'], Dummy::class, null, $context); } + + public function testDenormalizeWithIdAndNoResourceClass() + { + $context = []; + + $propertyNameCollection = new PropertyNameCollection(['id', 'name']); + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + $propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled(); + + $propertyMetadataFactory = new PropertyMetadata(null, null, true, true); + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + $propertyMetadataFactoryProphecy->create(Dummy::class, 'id', [])->willReturn($propertyMetadataFactory)->shouldBeCalled(); + $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadataFactory)->shouldBeCalled(); + + $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); + + $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); + + $serializerProphecy = $this->prophesize(SerializerInterface::class); + $serializerProphecy->willImplement(DenormalizerInterface::class); + + $normalizer = new ItemNormalizer( + $propertyNameCollectionFactoryProphecy->reveal(), + $propertyMetadataFactoryProphecy->reveal(), + $iriConverterProphecy->reveal(), + $resourceClassResolverProphecy->reveal() + ); + $normalizer->setSerializer($serializerProphecy->reveal()); + + $object = $normalizer->denormalize(['id' => '42', 'name' => 'hello'], Dummy::class, null, $context); + $this->assertInstanceOf(Dummy::class, $object); + $this->assertSame('42', $object->getId()); + $this->assertSame('hello', $object->getName()); + } }