Skip to content

Commit c2be409

Browse files
authored
fix(symfony): error in provider without uri variables (#6005)
fixes #6002
1 parent e7bc2ab commit c2be409

File tree

2 files changed

+171
-7
lines changed

2 files changed

+171
-7
lines changed

src/Symfony/Controller/MainController.php

+14-7
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ApiPlatform\Exception\InvalidIdentifierException;
1818
use ApiPlatform\Exception\InvalidUriVariableException;
1919
use ApiPlatform\Metadata\Error;
20+
use ApiPlatform\Metadata\Exception\RuntimeException;
2021
use ApiPlatform\Metadata\HttpOperation;
2122
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
2223
use ApiPlatform\State\ProcessorInterface;
@@ -47,8 +48,11 @@ public function __construct(
4748
public function __invoke(Request $request): Response
4849
{
4950
$operation = $this->initializeOperation($request);
50-
$uriVariables = [];
51+
if (!$operation) {
52+
throw new RuntimeException('Not an API operation.');
53+
}
5154

55+
$uriVariables = [];
5256
if (!$operation instanceof Error) {
5357
try {
5458
$uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass());
@@ -80,12 +84,15 @@ public function __invoke(Request $request): Response
8084
// The provider can change the Operation, extract it again from the Request attributes
8185
if ($request->attributes->get('_api_operation') !== $operation) {
8286
$operation = $this->initializeOperation($request);
83-
try {
84-
$uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass());
85-
} catch (InvalidIdentifierException|InvalidUriVariableException $e) {
86-
// if this occurs with our base operation we throw above so log instead of throw here
87-
if ($this->logger) {
88-
$this->logger->error($e->getMessage(), ['operation' => $operation]);
87+
88+
if (!$operation instanceof Error) {
89+
try {
90+
$uriVariables = $this->getOperationUriVariables($operation, $request->attributes->all(), $operation->getClass());
91+
} catch (InvalidIdentifierException|InvalidUriVariableException $e) {
92+
// if this occurs with our base operation we throw above so log instead of throw here
93+
if ($this->logger) {
94+
$this->logger->error($e->getMessage(), ['operation' => $operation]);
95+
}
8996
}
9097
}
9198
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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\Symfony\Controller;
15+
16+
use ApiPlatform\Metadata\Error;
17+
use ApiPlatform\Metadata\Exception\RuntimeException;
18+
use ApiPlatform\Metadata\Get;
19+
use ApiPlatform\Metadata\Link;
20+
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
21+
use ApiPlatform\State\ProcessorInterface;
22+
use ApiPlatform\State\ProviderInterface;
23+
use ApiPlatform\Symfony\Controller\MainController;
24+
use PHPUnit\Framework\TestCase;
25+
use Psr\Log\LoggerInterface;
26+
use Symfony\Component\HttpFoundation\Request;
27+
use Symfony\Component\HttpFoundation\Response;
28+
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
29+
30+
class MainControllerTest extends TestCase
31+
{
32+
public function testControllerNotSupported(): void
33+
{
34+
$this->expectException(RuntimeException::class);
35+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
36+
$provider = $this->createMock(ProviderInterface::class);
37+
$processor = $this->createMock(ProcessorInterface::class);
38+
$controller = new MainController($resourceMetadataFactory, $provider, $processor);
39+
$controller->__invoke(new Request());
40+
}
41+
42+
public function testController(): void
43+
{
44+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
45+
$provider = $this->createMock(ProviderInterface::class);
46+
$processor = $this->createMock(ProcessorInterface::class);
47+
$controller = new MainController($resourceMetadataFactory, $provider, $processor);
48+
49+
$body = new \stdClass();
50+
$response = new Response();
51+
$request = new Request();
52+
$request->attributes->set('_api_operation', new Get());
53+
54+
$provider->expects($this->once())
55+
->method('provide')
56+
->willReturn($body);
57+
58+
$processor->expects($this->once())
59+
->method('process')
60+
->willReturn($response);
61+
62+
$this->assertEquals($response, $controller->__invoke($request));
63+
}
64+
65+
public function testControllerWithNonExistentUriVariables(): void
66+
{
67+
$this->expectException(NotFoundHttpException::class);
68+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
69+
$provider = $this->createMock(ProviderInterface::class);
70+
$processor = $this->createMock(ProcessorInterface::class);
71+
$controller = new MainController($resourceMetadataFactory, $provider, $processor);
72+
73+
$body = new \stdClass();
74+
$response = new Response();
75+
$request = new Request();
76+
$request->attributes->set('_api_operation', new Get(uriVariables: ['id' => new Link()]));
77+
78+
$controller->__invoke($request);
79+
}
80+
81+
public function testControllerWithUriVariables(): void
82+
{
83+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
84+
$provider = $this->createMock(ProviderInterface::class);
85+
$processor = $this->createMock(ProcessorInterface::class);
86+
$controller = new MainController($resourceMetadataFactory, $provider, $processor);
87+
88+
$body = new \stdClass();
89+
$response = new Response();
90+
$request = new Request();
91+
$request->attributes->set('_api_operation', new Get(uriVariables: ['id' => new Link()]));
92+
$request->attributes->set('id', 0);
93+
94+
$provider->expects($this->once())
95+
->method('provide')
96+
->willReturn($body);
97+
98+
$processor->expects($this->once())
99+
->method('process')
100+
->willReturn($response);
101+
102+
$this->assertEquals($response, $controller->__invoke($request));
103+
}
104+
105+
public function testControllerErrorWithUriVariables(): void
106+
{
107+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
108+
$provider = $this->createMock(ProviderInterface::class);
109+
$processor = $this->createMock(ProcessorInterface::class);
110+
$controller = new MainController($resourceMetadataFactory, $provider, $processor);
111+
112+
$body = new \stdClass();
113+
$response = new Response();
114+
$request = new Request();
115+
$request->attributes->set('_api_operation', new Error(uriVariables: ['id' => new Link()]));
116+
117+
$provider->expects($this->once())
118+
->method('provide')
119+
->willReturn($body);
120+
121+
$processor->expects($this->once())
122+
->method('process')
123+
->willReturn($response);
124+
125+
$this->assertEquals($response, $controller->__invoke($request));
126+
}
127+
128+
public function testControllerErrorWithUriVariablesDuringProvider(): void
129+
{
130+
$resourceMetadataFactory = $this->createMock(ResourceMetadataCollectionFactoryInterface::class);
131+
$provider = $this->createMock(ProviderInterface::class);
132+
$logger = $this->createMock(LoggerInterface::class);
133+
$processor = $this->createMock(ProcessorInterface::class);
134+
$controller = new MainController($resourceMetadataFactory, $provider, $processor, logger: $logger);
135+
136+
$response = new Response();
137+
$request = new Request();
138+
$request->attributes->set('_api_operation', new Get(uriVariables: ['id' => new Link()]));
139+
$request->attributes->set('id', '1');
140+
141+
$provider->expects($this->once())
142+
->method('provide')
143+
->willReturnCallback(function () use ($request) {
144+
$request->attributes->set('_api_operation', new Error(uriVariables: ['status' => new Link()]));
145+
$request->attributes->remove('id');
146+
147+
return new \stdClass();
148+
});
149+
150+
$logger->expects($this->never())->method('error');
151+
$processor->expects($this->once())
152+
->method('process')
153+
->willReturn($response);
154+
155+
$this->assertEquals($response, $controller->__invoke($request));
156+
}
157+
}

0 commit comments

Comments
 (0)