Skip to content

Commit 6fad258

Browse files
committed
Fix HTTP status code when output class is set to null
Also fix Behat test for when input class is set to null
1 parent 4df163a commit 6fad258

File tree

12 files changed

+235
-20
lines changed

12 files changed

+235
-20
lines changed

features/jsonld/input_output.feature

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Feature: JSON-LD DTO input and output
9292
"ipsum": "1"
9393
}
9494
"""
95-
Then the response status code should be 201
95+
Then the response status code should be 204
9696
And the response should be empty
9797

9898
@createSchema
@@ -187,15 +187,50 @@ Feature: JSON-LD DTO input and output
187187

188188
@createSchema
189189
Scenario: Create a resource with no input
190-
When I send a "POST" request to "/dummy_dto_no_inputs" with body:
190+
When I send a "POST" request to "/dummy_dto_no_inputs"
191+
Then the response status code should be 201
192+
And the response should be in JSON
193+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
194+
And the JSON should be equal to:
191195
"""
192196
{
193-
"foo": "test",
194-
"bar": 1
197+
"@context": {
198+
"@vocab": "http://example.com/docs.jsonld#",
199+
"hydra": "http://www.w3.org/ns/hydra/core#",
200+
"id": "OutputDto/id",
201+
"baz": "OutputDto/baz",
202+
"bat": "OutputDto/bat"
203+
},
204+
"@type": "DummyDtoNoInput",
205+
"@id": "/dummy_dto_no_inputs/1",
206+
"id": 1,
207+
"baz": 1,
208+
"bat": "test"
209+
}
210+
"""
211+
212+
Scenario: Update a resource with no input
213+
When I send a "POST" request to "/dummy_dto_no_inputs/1/double_bat"
214+
Then the response status code should be 200
215+
And the response should be in JSON
216+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
217+
And the JSON should be equal to:
218+
"""
219+
{
220+
"@context": {
221+
"@vocab": "http://example.com/docs.jsonld#",
222+
"hydra": "http://www.w3.org/ns/hydra/core#",
223+
"id": "OutputDto/id",
224+
"baz": "OutputDto/baz",
225+
"bat": "OutputDto/bat"
226+
},
227+
"@type": "DummyDtoNoInput",
228+
"@id": "/dummy_dto_no_inputs/1",
229+
"id": 1,
230+
"baz": 1,
231+
"bat": "testtest"
195232
}
196233
"""
197-
Then the response status code should be 201
198-
And the response should be empty
199234

200235
@!mongodb
201236
Scenario: Use messenger with an input where the handler gives a synchronous result

phpstan.neon.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ parameters:
7070
-
7171
message: '#Method ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Util\\QueryBuilderHelper::mapJoinAliases() should return array<string, array<string>\|string> but returns array<int|string, mixed>\.#'
7272
path: %currentWorkingDirectory%/src/Bridge/Doctrine/Orm/Util/QueryBuilderHelper.php
73+
-
74+
message: "#Call to function method_exists\\(\\) with 'Symfony\\\\\\\\Component.+' and 'addRemovedBindingIds?' will always evaluate to false\\.#"
75+
path: %currentWorkingDirectory%/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php
7376
- "#Call to method PHPUnit\\\\Framework\\\\Assert::assertSame\\(\\) with array\\('(collection_context|item_context|subresource_context)'\\) and array<Symfony\\\\Component\\\\VarDumper\\\\Cloner\\\\Data>\\|bool\\|float\\|int\\|string\\|null will always evaluate to false\\.#"
7477
# https://github.com/doctrine/doctrine2/pull/7298/files
7578
- '#Strict comparison using === between null and int will always evaluate to false\.#'

src/EventListener/SerializeListener.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,8 @@ public function onKernelView(GetResponseForControllerResultEvent $event): void
6161

6262
$context = $this->serializerContextBuilder->createFromRequest($request, true, $attributes);
6363

64-
if (
65-
(isset($context['output']) && \array_key_exists('class', $context['output']) && null === $context['output']['class'])
66-
||
67-
(
68-
null === $controllerResult && isset($context['input']) && \array_key_exists('class', $context['input']) &&
69-
null === $context['input']['class']
70-
)
71-
) {
72-
$event->setControllerResult('');
64+
if (isset($context['output']) && \array_key_exists('class', $context['output']) && null === $context['output']['class']) {
65+
$event->setControllerResult(null);
7366

7467
return;
7568
}

src/Metadata/Resource/Factory/InputOutputResourceMetadataFactory.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ private function getTransformedOperations(array $operations, array $resourceAttr
6464

6565
$operation['input'] = isset($operation['input']) ? $this->transformInputOutput($operation['input']) : $resourceAttributes['input'];
6666
$operation['output'] = isset($operation['output']) ? $this->transformInputOutput($operation['output']) : $resourceAttributes['output'];
67+
68+
if (
69+
!isset($operation['status'])
70+
&& isset($operation['output'])
71+
&& \array_key_exists('class', $operation['output'])
72+
&& null === $operation['output']['class']
73+
) {
74+
$operation['status'] = 204;
75+
}
6776
}
6877

6978
return $operations;

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,15 @@ private function getPartialContainerBuilderProphecy()
843843

844844
$containerBuilderProphecy->getDefinition('api_platform.http_cache.purger.varnish')->willReturn(new Definition());
845845

846+
// irrelevant, but to prevent errors
847+
// https://github.com/symfony/symfony/pull/29944
848+
if (method_exists(ContainerBuilder::class, 'addRemovedBindingId')) {
849+
$containerBuilderProphecy->addRemovedBindingId(Argument::type('string'))->will(function () {});
850+
} elseif (method_exists(ContainerBuilder::class, 'addRemovedBindingIds')) {
851+
// https://github.com/symfony/symfony/pull/31173
852+
$containerBuilderProphecy->addRemovedBindingIds(Argument::type('string'))->will(function () {});
853+
}
854+
846855
return $containerBuilderProphecy;
847856
}
848857

tests/EventListener/SerializeListenerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,9 +111,9 @@ public function testSerializeCollectionOperationWithOutputClassDisabled()
111111
$request->setRequestFormat('xml');
112112

113113
$eventProphecy = $this->prophesize(GetResponseForControllerResultEvent::class);
114-
$eventProphecy->getControllerResult()->willReturn(new \stdClass())->shouldBeCalled();
115-
$eventProphecy->getRequest()->willReturn($request)->shouldBeCalled();
116-
$eventProphecy->setControllerResult('')->shouldBeCalled();
114+
$eventProphecy->getControllerResult()->willReturn(new \stdClass());
115+
$eventProphecy->getRequest()->willReturn($request);
116+
$eventProphecy->setControllerResult(null)->shouldBeCalled();
117117

118118
$serializerContextBuilderProphecy = $this->prophesize(SerializerContextBuilderInterface::class);
119119
$serializerContextBuilderProphecy->createFromRequest(Argument::type(Request::class), true, Argument::type('array'))->willReturn($expectedContext)->shouldBeCalled();
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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\Core\Tests\Fixtures\TestBundle\Controller\DummyDtoNoInput;
15+
16+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoNoInput as DummyDtoNoInputDocument;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoNoInput;
18+
use Symfony\Component\HttpFoundation\Request;
19+
20+
final class CreateItemAction
21+
{
22+
public function __invoke(Request $request)
23+
{
24+
$resourceClass = $request->attributes->get('_api_resource_class');
25+
if (!\in_array($resourceClass, [DummyDtoNoInput::class, DummyDtoNoInputDocument::class], true)) {
26+
throw new \InvalidArgumentException();
27+
}
28+
29+
$data = new $resourceClass();
30+
31+
$data->lorem = 'test';
32+
$data->ipsum = 1;
33+
34+
return $data;
35+
}
36+
}
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\Core\Tests\Fixtures\TestBundle\Controller\DummyDtoNoInput;
15+
16+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoNoInput as DummyDtoNoInputDocument;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoNoInput;
18+
19+
final class DoubleBatAction
20+
{
21+
public function __invoke($data)
22+
{
23+
if (!$data instanceof DummyDtoNoInput && !$data instanceof DummyDtoNoInputDocument) {
24+
throw new \InvalidArgumentException();
25+
}
26+
27+
$data->lorem .= $data->lorem;
28+
29+
return $data;
30+
}
31+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\Core\Tests\Fixtures\TestBundle\DataTransformer;
15+
16+
use ApiPlatform\Core\DataTransformer\DataTransformerInterface;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\DummyDtoNoInput as DummyDtoNoInputDocument;
18+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\OutputDto;
19+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\DummyDtoNoInput;
20+
21+
final class DummyDtoNoInputToOutputDtoDataTransformer implements DataTransformerInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function transform($object, string $to, array $context = [])
27+
{
28+
if (!$object instanceof DummyDtoNoInput && !$object instanceof DummyDtoNoInputDocument) {
29+
throw new \InvalidArgumentException();
30+
}
31+
32+
$output = new OutputDto();
33+
$output->id = $object->getId();
34+
$output->bat = (string) $object->lorem;
35+
$output->baz = (float) $object->ipsum;
36+
37+
return $output;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
public function supportsTransformation($data, string $to, array $context = []): bool
44+
{
45+
return ($data instanceof DummyDtoNoInput || $data instanceof DummyDtoNoInputDocument) && OutputDto::class === $to;
46+
}
47+
}

tests/Fixtures/TestBundle/Document/DummyDtoNoInput.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace ApiPlatform\Core\Tests\Fixtures\TestBundle\Document;
1515

1616
use ApiPlatform\Core\Annotation\ApiResource;
17+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Controller\DummyDtoNoInput\CreateItemAction;
18+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Controller\DummyDtoNoInput\DoubleBatAction;
1719
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\OutputDto;
1820
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
1921

@@ -28,7 +30,25 @@
2830
* attributes={
2931
* "input"=false,
3032
* "output"=OutputDto::class
31-
* }
33+
* },
34+
* collectionOperations={
35+
* "post"={
36+
* "method"="POST",
37+
* "path"="/dummy_dto_no_inputs",
38+
* "controller"=CreateItemAction::class,
39+
* },
40+
* "get",
41+
* },
42+
* itemOperations={
43+
* "get",
44+
* "delete",
45+
* "post_double_bat"={
46+
* "method"="POST",
47+
* "path"="/dummy_dto_no_inputs/{id}/double_bat",
48+
* "controller"=DoubleBatAction::class,
49+
* "status"=200,
50+
* },
51+
* },
3252
* )
3353
*/
3454
class DummyDtoNoInput

0 commit comments

Comments
 (0)