Skip to content

Commit 84df467

Browse files
authored
fix(jsonld): remove request uri variables when denormalizing output (#6467)
1 parent 1197e92 commit 84df467

File tree

7 files changed

+225
-1
lines changed

7 files changed

+225
-1
lines changed

src/Serializer/AbstractItemNormalizer.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public function denormalize(mixed $data, string $class, ?string $format = null,
220220
throw new LogicException('Cannot denormalize the input because the injected serializer is not a denormalizer');
221221
}
222222

223-
unset($context['input'], $context['operation'], $context['operation_name']);
223+
unset($context['input'], $context['operation'], $context['operation_name'], $context['uri_variables']);
224224
$context['resource_class'] = $inputClass;
225225

226226
try {

src/Symfony/Routing/IriConverter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public function getResourceFromIri(string $iri, array $context = [], ?Operation
7878
throw new InvalidArgumentException(sprintf('No resource associated to "%s".', $iri));
7979
}
8080

81+
// uri_variables come from the Request context and may not be available
8182
foreach ($context['uri_variables'] ?? [] as $key => $value) {
8283
if (!isset($parameters[$key]) || $parameters[$key] !== (string) $value) {
8384
throw new InvalidArgumentException(sprintf('The iri "%s" does not reference the correct resource.', $iri));
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use Doctrine\ORM\Mapping as ORM;
18+
19+
#[ORM\Entity()]
20+
#[ApiResource(shortName: 'Bar6465')]
21+
#[ORM\Table(name: 'bar6465')]
22+
class Bar
23+
{
24+
#[ORM\Id]
25+
#[ORM\GeneratedValue]
26+
#[ORM\Column]
27+
public ?int $id = null;
28+
29+
#[ORM\Column]
30+
public string $title = '';
31+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465;
15+
16+
use Symfony\Component\Validator\Constraints\NotBlank;
17+
18+
class CustomInput
19+
{
20+
#[NotBlank]
21+
public Bar $bar;
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465;
15+
16+
class CustomOutput
17+
{
18+
public function __construct(public string $title)
19+
{
20+
}
21+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\Get;
18+
use ApiPlatform\Metadata\GetCollection;
19+
use ApiPlatform\Metadata\Link;
20+
use ApiPlatform\Metadata\Post;
21+
use Doctrine\ORM\Mapping as ORM;
22+
23+
#[ORM\Entity()]
24+
#[ORM\Table(name: 'foo6465')]
25+
#[ApiResource(
26+
shortName: 'Foo6465',
27+
operations: [
28+
new Get(),
29+
new GetCollection(),
30+
new Post(),
31+
new Post(
32+
uriTemplate: '/foo/{id}/validate',
33+
uriVariables: ['id' => new Link(fromClass: Foo::class)],
34+
status: 200,
35+
input: CustomInput::class,
36+
output: CustomOutput::class,
37+
processor: [self::class, 'process'],
38+
),
39+
]
40+
)]
41+
class Foo
42+
{
43+
#[ORM\Id]
44+
#[ORM\GeneratedValue]
45+
#[ORM\Column]
46+
public ?int $id = null;
47+
48+
#[ORM\Column(length: 255)]
49+
public ?string $title = null;
50+
51+
/**
52+
* @param CustomInput $data
53+
*/
54+
public static function process($data): CustomOutput
55+
{
56+
return new CustomOutput($data->bar->title);
57+
}
58+
}

tests/Functional/JsonLd.php

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Functional;
15+
16+
use ApiPlatform\Symfony\Bundle\Test\ApiTestCase;
17+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Bar;
18+
use ApiPlatform\Tests\Fixtures\TestBundle\Entity\Issue6465\Foo;
19+
use Doctrine\ORM\EntityManagerInterface;
20+
use Doctrine\ORM\Tools\SchemaTool;
21+
22+
class JsonLd extends ApiTestCase
23+
{
24+
/**
25+
* The input DTO denormalizes an existing Doctrine entity.
26+
*/
27+
public function testIssue6465(): void
28+
{
29+
$container = static::getContainer();
30+
if ('mongodb' === $container->getParameter('kernel.environment')) {
31+
$this->markTestSkipped();
32+
}
33+
34+
$response = self::createClient()->request('POST', '/foo/1/validate', [
35+
'json' => ['bar' => '/bar6465s/2'],
36+
]);
37+
38+
$res = $response->toArray();
39+
$this->assertEquals('Bar two', $res['title']);
40+
}
41+
42+
protected function setUp(): void
43+
{
44+
self::bootKernel();
45+
46+
$container = static::getContainer();
47+
$registry = $container->get('doctrine');
48+
$manager = $registry->getManager();
49+
if (!$manager instanceof EntityManagerInterface) {
50+
return;
51+
}
52+
53+
$classes = [];
54+
foreach ([Foo::class, Bar::class] as $entityClass) {
55+
$classes[] = $manager->getClassMetadata($entityClass);
56+
}
57+
58+
$schemaTool = new SchemaTool($manager);
59+
@$schemaTool->createSchema($classes);
60+
61+
$foo = new Foo();
62+
$foo->title = 'Foo';
63+
$manager->persist($foo);
64+
$bar = new Bar();
65+
$bar->title = 'Bar one';
66+
$manager->persist($bar);
67+
$bar2 = new Bar();
68+
$bar2->title = 'Bar two';
69+
$manager->persist($bar2);
70+
$manager->flush();
71+
}
72+
73+
protected function tearDown(): void
74+
{
75+
$container = static::getContainer();
76+
$registry = $container->get('doctrine');
77+
$manager = $registry->getManager();
78+
if (!$manager instanceof EntityManagerInterface) {
79+
return;
80+
}
81+
82+
$classes = [];
83+
foreach ([Foo::class, Bar::class] as $entityClass) {
84+
$classes[] = $manager->getClassMetadata($entityClass);
85+
}
86+
87+
$schemaTool = new SchemaTool($manager);
88+
@$schemaTool->dropSchema($classes);
89+
parent::tearDown();
90+
}
91+
}

0 commit comments

Comments
 (0)