Skip to content

Commit 9660a19

Browse files
authored
fix(serializer): concat context on wrong id (#6050)
1 parent aae0dbf commit 9660a19

File tree

2 files changed

+90
-5
lines changed

2 files changed

+90
-5
lines changed

src/Serializer/ItemNormalizer.php

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,20 @@ private function updateObjectToPopulate(array $data, array &$context): void
7676
try {
7777
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri((string) $data['id'], $context + ['fetch_data' => true]);
7878
} catch (InvalidArgumentException) {
79-
$operation = $this->resourceMetadataCollectionFactory->create($context['resource_class'])->getOperation();
79+
$operation = $this->resourceMetadataCollectionFactory?->create($context['resource_class'])->getOperation();
8080
if (
81-
null !== ($context['uri_variables'] ?? null)
82-
&& $operation instanceof HttpOperation
83-
&& \count($operation->getUriVariables() ?? []) > 1
81+
!$operation || (
82+
null !== ($context['uri_variables'] ?? null)
83+
&& $operation instanceof HttpOperation
84+
&& \count($operation->getUriVariables() ?? []) > 1
85+
)
8486
) {
8587
throw new InvalidArgumentException('Cannot find object to populate, use JSON-LD or specify an IRI at path "id".');
8688
}
8789
$uriVariables = $this->getContextUriVariables($data, $operation, $context);
8890
$iri = $this->iriConverter->getIriFromResource($context['resource_class'], UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => $uriVariables]);
8991

90-
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri($iri, ['fetch_data' => true]);
92+
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getResourceFromIri($iri, $context + ['fetch_data' => true]);
9193
}
9294
}
9395

tests/Serializer/ItemNormalizerTest.php

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,17 @@
1515

1616
use ApiPlatform\Api\IriConverterInterface;
1717
use ApiPlatform\Api\ResourceClassResolverInterface;
18+
use ApiPlatform\Api\UrlGeneratorInterface;
19+
use ApiPlatform\Exception\InvalidArgumentException;
1820
use ApiPlatform\Metadata\ApiProperty;
21+
use ApiPlatform\Metadata\ApiResource;
22+
use ApiPlatform\Metadata\Get;
23+
use ApiPlatform\Metadata\Link;
1924
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
2025
use ApiPlatform\Metadata\Property\Factory\PropertyNameCollectionFactoryInterface;
2126
use ApiPlatform\Metadata\Property\PropertyNameCollection;
27+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
28+
use ApiPlatform\Metadata\Resource\ResourceMetadataCollection;
2229
use ApiPlatform\Serializer\ItemNormalizer;
2330
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Dummy;
2431
use PHPUnit\Framework\TestCase;
@@ -283,4 +290,80 @@ public function testDenormalizeWithIdAndNoResourceClass(): void
283290
$this->assertSame('42', $object->getId());
284291
$this->assertSame('hello', $object->getName());
285292
}
293+
294+
public function testDenormalizeWithWrongIdAndNoResourceMetadataFactory(): void
295+
{
296+
$this->expectException(InvalidArgumentException::class);
297+
$context = ['resource_class' => Dummy::class, 'api_allow_update' => true];
298+
299+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
300+
301+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
302+
303+
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
304+
$iriConverterProphecy->getResourceFromIri('fail', $context + ['fetch_data' => true])->willThrow(new InvalidArgumentException());
305+
306+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
307+
$resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
308+
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
309+
310+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
311+
$serializerProphecy->willImplement(DenormalizerInterface::class);
312+
$normalizer = new ItemNormalizer(
313+
$propertyNameCollectionFactoryProphecy->reveal(),
314+
$propertyMetadataFactoryProphecy->reveal(),
315+
$iriConverterProphecy->reveal(),
316+
$resourceClassResolverProphecy->reveal()
317+
);
318+
$normalizer->setSerializer($serializerProphecy->reveal());
319+
320+
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['name' => 'hello', 'id' => 'fail'], Dummy::class, null, $context));
321+
}
322+
323+
public function testDenormalizeWithWrongId(): void
324+
{
325+
$context = ['resource_class' => Dummy::class, 'api_allow_update' => true];
326+
$operation = new Get(uriVariables: ['id' => new Link(identifiers: ['id'], parameterName: 'id')]);
327+
$obj = new Dummy();
328+
329+
$propertyNameCollection = new PropertyNameCollection(['name']);
330+
$propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
331+
$propertyNameCollectionFactoryProphecy->create(Dummy::class, [])->willReturn($propertyNameCollection)->shouldBeCalled();
332+
333+
$propertyMetadata = (new ApiProperty())->withReadable(true)->withWritable(true);
334+
$propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class);
335+
$propertyMetadataFactoryProphecy->create(Dummy::class, 'name', [])->willReturn($propertyMetadata)->shouldBeCalled();
336+
337+
$iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
338+
$iriConverterProphecy->getResourceFromIri('fail', $context + ['fetch_data' => true])->willThrow(new InvalidArgumentException());
339+
$iriConverterProphecy->getIriFromResource(Dummy::class, UrlGeneratorInterface::ABS_PATH, $operation, ['uri_variables' => ['id' => 'fail']])->willReturn('/dummies/fail');
340+
$iriConverterProphecy->getResourceFromIri('/dummies/fail', $context + ['fetch_data' => true])->willReturn($obj);
341+
342+
$resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
343+
$resourceClassResolverProphecy->getResourceClass(null, Dummy::class)->willReturn(Dummy::class);
344+
$resourceClassResolverProphecy->getResourceClass($obj, Dummy::class)->willReturn(Dummy::class);
345+
$resourceClassResolverProphecy->isResourceClass(Dummy::class)->willReturn(true);
346+
347+
$resourceMetadataCollectionFactory = $this->prophesize(ResourceMetadataCollectionFactoryInterface::class);
348+
$resourceMetadataCollectionFactory->create(Dummy::class)->willReturn(new ResourceMetadataCollection(Dummy::class, [
349+
new ApiResource(operations: [$operation]),
350+
]));
351+
352+
$serializerProphecy = $this->prophesize(SerializerInterface::class);
353+
$serializerProphecy->willImplement(DenormalizerInterface::class);
354+
$normalizer = new ItemNormalizer(
355+
$propertyNameCollectionFactoryProphecy->reveal(),
356+
$propertyMetadataFactoryProphecy->reveal(),
357+
$iriConverterProphecy->reveal(),
358+
$resourceClassResolverProphecy->reveal(),
359+
null,
360+
null,
361+
null,
362+
null,
363+
$resourceMetadataCollectionFactory->reveal()
364+
);
365+
$normalizer->setSerializer($serializerProphecy->reveal());
366+
367+
$this->assertInstanceOf(Dummy::class, $normalizer->denormalize(['name' => 'hello', 'id' => 'fail'], Dummy::class, null, $context));
368+
}
286369
}

0 commit comments

Comments
 (0)