From 85c2ba19d6316f8cc292e7160828854a58ac3663 Mon Sep 17 00:00:00 2001 From: Kayue Yeung Date: Sat, 6 Feb 2021 22:11:28 +0800 Subject: [PATCH 1/4] Attributes need to be allowed by Symfony's serializer at the same time --- src/Serializer/AbstractItemNormalizer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index b8c5eb8551c..af3dfcdd55a 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -371,7 +371,8 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu } } - return $allowedAttributes; + // Attributes need to be allowed by Symfony's serializer at the same time + return array_intersect($allowedAttributes, parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)); } /** From da820fa2cc615fb5f3eeda5d81a29d9d2fbc980c Mon Sep 17 00:00:00 2001 From: Kayue Yeung Date: Sat, 6 Feb 2021 23:07:26 +0800 Subject: [PATCH 2/4] Handle class not supported by Symfony Serializer Some resources are only registered in ApiPlatform --- src/Serializer/AbstractItemNormalizer.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index af3dfcdd55a..d3c4d0e5484 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -371,8 +371,15 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu } } - // Attributes need to be allowed by Symfony's serializer at the same time - return array_intersect($allowedAttributes, parent::getAllowedAttributes($classOrObject, $context, $attributesAsString)); + // Gets allowed attributes using serialization groups + $groupAllowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString); + + if (is_array($groupAllowedAttributes)) { + // Attributes need to be allowed by both API Resource and serialization groups at the same time + return array_intersect($allowedAttributes, $groupAllowedAttributes); + } + + return $allowedAttributes; } /** From dc80470e51ce49083f5d159a684ebb358dd35425 Mon Sep 17 00:00:00 2001 From: Kayue Yeung Date: Tue, 16 Feb 2021 00:43:28 +0800 Subject: [PATCH 3/4] Add unit test cover serialization group --- .../Serializer/AbstractItemNormalizerTest.php | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/Serializer/AbstractItemNormalizerTest.php b/tests/Serializer/AbstractItemNormalizerTest.php index bee96b1e203..8916e584b1c 100644 --- a/tests/Serializer/AbstractItemNormalizerTest.php +++ b/tests/Serializer/AbstractItemNormalizerTest.php @@ -44,6 +44,9 @@ use Symfony\Component\PropertyAccess\PropertyAccessorInterface; use Symfony\Component\PropertyInfo\Type; use Symfony\Component\Serializer\Exception\UnexpectedValueException; +use Symfony\Component\Serializer\Mapping\AttributeMetadata; +use Symfony\Component\Serializer\Mapping\ClassMetadata; +use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface; use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface; use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; @@ -266,6 +269,79 @@ public function testNormalizeWithSecuredProperty() ])); } + public function testNormalizeWithSerializationGroup() + { + $dummy = new Dummy(); + $dummy->setName('myName'); // Available only in group "Default" (Symfony's default group name) + $dummy->setDescription('myDescription'); // Available only in group "dummy:write" + + $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); + $propertyNameCollectionFactoryProphecy->create(Dummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['name', 'description'])); + + $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); + $propertyMetadataFactoryProphecy->create(Dummy::class, 'name', Argument::type('array'))->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)); + $propertyMetadataFactoryProphecy->create(Dummy::class, 'description', Argument::type('array'))->willReturn(new PropertyMetadata(new Type(Type::BUILTIN_TYPE_STRING), '', true)); + + $iriConverterProphecy = $this->prophesize(IriConverterInterface::class); + $iriConverterProphecy->getIriFromItem($dummy)->willReturn('/secured_dummies/1'); + + $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class); + $propertyAccessorProphecy->getValue($dummy, 'name')->willReturn($dummy->getName()); + $propertyAccessorProphecy->getValue($dummy, 'description')->willReturn($dummy->getDescription()); + + // Class metadata + $titleAttributeMetadata = new AttributeMetadata('name'); + $titleAttributeMetadata->addGroup('Default'); + $descriptionAttributeMetadata = new AttributeMetadata('description'); + $descriptionAttributeMetadata->addGroup('dummy:write'); + $classMetadata = new ClassMetadata(Dummy::class); + $classMetadata->addAttributeMetadata($titleAttributeMetadata); + $classMetadata->addAttributeMetadata($descriptionAttributeMetadata); + + $classMetadataFactoryProphecy = $this->prophesize(ClassMetadataFactoryInterface::class); + $classMetadataFactoryProphecy->getMetadataFor($dummy)->willReturn($classMetadata); + $classMetadataFactoryProphecy->getMetadataFor(Dummy::class)->willReturn($classMetadata); + + $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); + $resourceClassResolverProphecy->getResourceClass($dummy, null)->willReturn(Dummy::class); + + $resourceAccessChecker = $this->prophesize(ResourceAccessCheckerInterface::class); + + $serializerProphecy = $this->prophesize(SerializerInterface::class); + $serializerProphecy->willImplement(NormalizerInterface::class); + $serializerProphecy->normalize('myName', null, Argument::type('array'))->willReturn('myName'); + $serializerProphecy->normalize('myDescription', null, Argument::type('array'))->willReturn('myDescription'); + + $normalizer = $this->getMockForAbstractClass(AbstractItemNormalizer::class, [ + $propertyNameCollectionFactoryProphecy->reveal(), + $propertyMetadataFactoryProphecy->reveal(), + $iriConverterProphecy->reveal(), + $resourceClassResolverProphecy->reveal(), + $propertyAccessorProphecy->reveal(), + null, + $classMetadataFactoryProphecy->reveal(), + null, + false, + [], + [], + null, + $resourceAccessChecker->reveal(), + ]); + $normalizer->setSerializer($serializerProphecy->reveal()); + + if (!interface_exists(AdvancedNameConverterInterface::class) && method_exists($normalizer, 'setIgnoredAttributes')) { + $normalizer->setIgnoredAttributes(['alias']); + } + + $expected = [ + 'name' => 'myName', + ]; + $this->assertEquals($expected, $normalizer->normalize($dummy, null, [ + 'resources' => [], + 'groups' => ['Default'], + ])); + } + public function testNormalizeReadableLinks() { $relatedDummy = new RelatedDummy(); From 0c163332ecbe52639ec6e09fcda992aeabf5d24f Mon Sep 17 00:00:00 2001 From: Kayue Yeung Date: Tue, 16 Feb 2021 02:26:01 +0800 Subject: [PATCH 4/4] Fix CS --- src/Serializer/AbstractItemNormalizer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index d3c4d0e5484..1e81673f390 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -374,7 +374,7 @@ protected function getAllowedAttributes($classOrObject, array $context, $attribu // Gets allowed attributes using serialization groups $groupAllowedAttributes = parent::getAllowedAttributes($classOrObject, $context, $attributesAsString); - if (is_array($groupAllowedAttributes)) { + if (\is_array($groupAllowedAttributes)) { // Attributes need to be allowed by both API Resource and serialization groups at the same time return array_intersect($allowedAttributes, $groupAllowedAttributes); }