Skip to content

Commit 133028e

Browse files
authored
fix(state): object mapper on delete operation (#7447)
fixes #7434
1 parent 70bdf29 commit 133028e

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

src/State/Processor/ObjectMapperProcessor.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
3838
if (
3939
!$this->objectMapper
4040
|| !$operation->canWrite()
41+
|| null === $data
4142
|| !is_a($data, $operation->getClass(), true)
4243
|| !(new \ReflectionClass($operation->getClass()))->getAttributes(Map::class)
4344
) {
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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\State\Tests\Processor;
15+
16+
use ApiPlatform\Metadata\Get;
17+
use ApiPlatform\Metadata\Post;
18+
use ApiPlatform\State\Processor\ObjectMapperProcessor;
19+
use ApiPlatform\State\ProcessorInterface;
20+
use PHPUnit\Framework\TestCase;
21+
use Symfony\Component\ObjectMapper\Attribute\Map;
22+
use Symfony\Component\ObjectMapper\ObjectMapperInterface;
23+
24+
class ObjectMapperProcessorTest extends TestCase
25+
{
26+
public function testProcessBypassesWhenNoObjectMapper(): void
27+
{
28+
$data = new DummyResourceWithoutMap();
29+
$operation = new Post(class: DummyResourceWithoutMap::class);
30+
$decorated = $this->createMock(ProcessorInterface::class);
31+
$decorated->expects($this->once())
32+
->method('process')
33+
->with($data, $operation, [], [])
34+
->willReturn($data);
35+
36+
$processor = new ObjectMapperProcessor(null, $decorated);
37+
$this->assertEquals($data, $processor->process($data, $operation));
38+
}
39+
40+
public function testProcessBypassesOnNonWriteOperation(): void
41+
{
42+
$data = new DummyResourceWithoutMap();
43+
$operation = new Get(class: DummyResourceWithoutMap::class);
44+
$objectMapper = $this->createMock(ObjectMapperInterface::class);
45+
$decorated = $this->createMock(ProcessorInterface::class);
46+
$decorated->expects($this->once())
47+
->method('process')
48+
->with($data, $operation, [], [])
49+
->willReturn($data);
50+
51+
$processor = new ObjectMapperProcessor($objectMapper, $decorated);
52+
$this->assertEquals($data, $processor->process($data, $operation));
53+
}
54+
55+
public function testProcessBypassesWithNullData(): void
56+
{
57+
$operation = new Post(class: DummyResourceWithoutMap::class);
58+
$objectMapper = $this->createMock(ObjectMapperInterface::class);
59+
$decorated = $this->createMock(ProcessorInterface::class);
60+
$decorated->expects($this->once())
61+
->method('process')
62+
->with(null, $operation, [], [])
63+
->willReturn(null);
64+
65+
$processor = new ObjectMapperProcessor($objectMapper, $decorated);
66+
$this->assertNull($processor->process(null, $operation));
67+
}
68+
69+
public function testProcessBypassesWithMismatchedDataType(): void
70+
{
71+
$data = new \stdClass();
72+
$operation = new Post(class: DummyResourceWithMap::class);
73+
$objectMapper = $this->createMock(ObjectMapperInterface::class);
74+
$decorated = $this->createMock(ProcessorInterface::class);
75+
$decorated->expects($this->once())
76+
->method('process')
77+
->with($data, $operation, [], [])
78+
->willReturn($data);
79+
80+
$processor = new ObjectMapperProcessor($objectMapper, $decorated);
81+
$this->assertEquals($data, $processor->process($data, $operation));
82+
}
83+
84+
public function testProcessBypassesWithoutMapAttribute(): void
85+
{
86+
$data = new DummyResourceWithoutMap();
87+
$operation = new Post(class: DummyResourceWithoutMap::class);
88+
$objectMapper = $this->createMock(ObjectMapperInterface::class);
89+
$decorated = $this->createMock(ProcessorInterface::class);
90+
$decorated->expects($this->once())
91+
->method('process')
92+
->with($data, $operation, [], [])
93+
->willReturn($data);
94+
95+
$processor = new ObjectMapperProcessor($objectMapper, $decorated);
96+
$this->assertEquals($data, $processor->process($data, $operation));
97+
}
98+
}
99+
100+
class DummyResourceWithoutMap
101+
{
102+
}
103+
104+
#[Map]
105+
class DummyResourceWithMap
106+
{
107+
}

src/State/composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
"symfony/translation-contracts": "^3.0"
3636
},
3737
"require-dev": {
38-
"api-platform/validator": "^4.1",
3938
"api-platform/serializer": "^4.1",
40-
"symfony/type-info": "^7.3",
39+
"api-platform/validator": "^4.1",
4140
"phpunit/phpunit": "11.5.x-dev",
4241
"symfony/http-foundation": "^6.4 || ^7.0",
42+
"symfony/object-mapper": "^7.3",
43+
"symfony/type-info": "^7.3",
4344
"symfony/web-link": "^6.4 || ^7.1",
4445
"willdurand/negotiation": "^3.1"
4546
},

0 commit comments

Comments
 (0)