Skip to content

Commit 113c197

Browse files
committed
prepare guide
1 parent 867fd50 commit 113c197

File tree

14 files changed

+58
-10
lines changed

14 files changed

+58
-10
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ jobs:
204204
- name: Setup PHP
205205
uses: shivammathur/setup-php@v2
206206
with:
207-
php-version: latest
207+
php-version: ${{ matrix.php }}
208208
tools: pecl, composer
209209
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite, mongodb
210210
ini-values: memory_limit=-1

src/Action/ExceptionAction.php

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ public function __invoke(FlattenException $exception, Request $request): Respons
8181
$error = $error->getConstraintViolationList();
8282
} elseif (method_exists($error, 'getViolations') && $error->getViolations() instanceof ConstraintViolationListInterface) {
8383
$error = $error->getViolations();
84+
} else {
85+
$error = $exception;
8486
}
8587

8688
$serializerFormat = $format['key'];

src/Hydra/Serializer/ErrorNormalizer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public function normalize(mixed $object, string $format = null, array $context =
6464
*/
6565
public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
6666
{
67-
if ($data instanceof Error) {
67+
if ($context['api_error_resource'] ?? false) {
6868
return false;
6969
}
7070

src/Metadata/ErrorResource.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public function __construct(
9292
shortName: $shortName,
9393
description: $description,
9494
types: $types,
95-
operations: $operations ?? [new Get()],
95+
operations: $operations ?? [new Error()],
9696
formats: $formats,
9797
inputFormats: $inputFormats,
9898
outputFormats: $outputFormats,

src/Metadata/Exception/HttpExceptionInterface.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
namespace ApiPlatform\Metadata\Exception;
1515

16-
interface HttpExceptionInterface extends \Throwable
16+
interface HttpExceptionInterface
1717
{
1818
/**
1919
* Returns the status code.

src/Metadata/Exception/ProblemExceptionInterface.php

-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ public function getTitle(): ?string;
2828

2929
public function getStatus(): ?int;
3030

31-
public function setStatus(int $status): void;
32-
3331
public function getDetail(): ?string;
3432

3533
public function getInstance(): ?string;

src/Problem/Serializer/ErrorNormalizer.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public function normalize(mixed $object, string $format = null, array $context =
6565
*/
6666
public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
6767
{
68-
if ($data instanceof Error) {
68+
if ($context['api_error_resource'] ?? false) {
6969
return false;
7070
}
7171

src/State/ErrorProvider.php

+7-2
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,28 @@
1515

1616
use ApiPlatform\Metadata\HttpOperation;
1717
use ApiPlatform\Metadata\Operation;
18+
use ApiPlatform\Metadata\ResourceClassResolverInterface;
1819
use ApiPlatform\State\ApiResource\Error;
1920

2021
/**
2122
* @internal
2223
*/
2324
final class ErrorProvider implements ProviderInterface
2425
{
25-
public function __construct(private readonly bool $debug = false)
26+
public function __construct(private readonly bool $debug = false, private ?ResourceClassResolverInterface $resourceClassResolver = null)
2627
{
2728
}
2829

29-
public function provide(Operation $operation, array $uriVariables = [], array $context = []): Error
30+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): object
3031
{
3132
if (!($request = $context['request'] ?? null) || !$operation instanceof HttpOperation || null === ($exception = $request->attributes->get('exception'))) {
3233
throw new \RuntimeException('Not an HTTP request');
3334
}
3435

36+
if ($this->resourceClassResolver?->isResourceClass($exception::class)) {
37+
return $exception;
38+
}
39+
3540
$status = $operation->getStatus() ?? 500;
3641
$error = Error::createFromException($exception, $status);
3742
if (!$this->debug && $status >= 500) {

src/Symfony/Bundle/DependencyInjection/Configuration.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ public function getConfigTreeBuilder(): TreeBuilder
169169
'html' => ['mime_types' => ['text/html']],
170170
]);
171171
$this->addFormatSection($rootNode, 'error_formats', [
172-
'jsonproblem' => ['mime_types' => ['application/problem+json']],
173172
'jsonld' => ['mime_types' => ['application/ld+json']],
173+
'jsonproblem' => ['mime_types' => ['application/problem+json']],
174174
'json' => ['mime_types' => ['application/problem+json', 'application/json']],
175175
]);
176176

src/Symfony/Bundle/Resources/config/api.xml

+1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@
203203

204204
<service id="api_platform.state.error_provider" class="ApiPlatform\State\ErrorProvider">
205205
<argument key="$debug">%kernel.debug%</argument>
206+
<argument type="service" key="$resourceClassResolver" id="api_platform.resource_class_resolver" />
206207
<tag name="api_platform.state_provider" key="api_platform.state.error_provider" />
207208
</service>
208209

src/Symfony/EventListener/ErrorListener.php

+26
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ApiPlatform\Api\IdentifiersExtractorInterface as LegacyIdentifiersExtractorInterface;
1717
use ApiPlatform\Api\ResourceClassResolverInterface as LegacyResourceClassResolverInterface;
1818
use ApiPlatform\Metadata\Error as ErrorOperation;
19+
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
1920
use ApiPlatform\Metadata\Exception\ProblemExceptionInterface;
2021
use ApiPlatform\Metadata\HttpOperation;
2122
use ApiPlatform\Metadata\IdentifiersExtractorInterface;
@@ -32,6 +33,7 @@
3233
use Symfony\Component\HttpFoundation\Request;
3334
use Symfony\Component\HttpKernel\EventListener\ErrorListener as SymfonyErrorListener;
3435
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
36+
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
3537

3638
/**
3739
* This error listener extends the Symfony one in order to add
@@ -71,6 +73,11 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re
7173
$request->setRequestFormat($format);
7274
$apiOperation = $this->initializeOperation($request);
7375

76+
// TODO: add configuration flag to:
77+
// - always use symfony error handler (skips this listener)
78+
// - use symfony error handler if it's not an api error, ie apiOperation is null
79+
// - use api platform to handle errors (the default behavior we handle firewall errors for example but they're out of our scope)
80+
7481
// Let the error handler take this we don't handle HTML nor non-api platform requests
7582
if ('html' === $format) {
7683
$this->controller = 'error_controller';
@@ -139,6 +146,17 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re
139146
$operation = $operation->withProvider('api_platform.state.error_provider');
140147
}
141148

149+
$normalizationContext = $operation->getNormalizationContext() ?? [];
150+
if (!($normalizationContext['_api_error_resource'] ?? false)) {
151+
$normalizationContext = $normalizationContext + ['api_error_resource' => true];
152+
}
153+
154+
if (!isset($normalizationContext[AbstractObjectNormalizer::IGNORED_ATTRIBUTES])) {
155+
$normalizationContext[AbstractObjectNormalizer::IGNORED_ATTRIBUTES] = ['trace', 'file', 'line', 'code', 'message', 'traceAsString'];
156+
}
157+
158+
$operation = $operation->withNormalizationContext($normalizationContext);
159+
142160
$dup->attributes->set('_api_resource_class', $operation->getClass());
143161
$dup->attributes->set('_api_previous_operation', $apiOperation);
144162
$dup->attributes->set('_api_operation', $operation);
@@ -192,6 +210,14 @@ private function getStatusCode(?HttpOperation $apiOperation, Request $request, ?
192210
return $exception->getStatusCode();
193211
}
194212

213+
if ($exception instanceof ProblemExceptionInterface && $status = $exception->getStatus()) {
214+
return $status;
215+
}
216+
217+
if ($exception instanceof HttpExceptionInterface) {
218+
return $exception->getStatusCode();
219+
}
220+
195221
if ($exception instanceof RequestExceptionInterface) {
196222
return 400;
197223
}

src/Symfony/EventListener/SerializeListener.php

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public function __construct(
4848
private readonly SerializerContextBuilderInterface $serializerContextBuilder,
4949
ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null,
5050
private readonly array $errorFormats = [],
51+
// @phpstan-ignore-next-line we don't need this anymore
5152
private readonly bool $debug = false,
5253
) {
5354
$this->resourceMetadataCollectionFactory = $resourceMetadataFactory;

tests/Symfony/Bundle/DependencyInjection/ConfigurationTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm
9696
'error_formats' => [
9797
'jsonproblem' => ['mime_types' => ['application/problem+json']],
9898
'jsonld' => ['mime_types' => ['application/ld+json']],
99+
'json' => ['mime_types' => ['application/problem+json', 'application/json']],
99100
],
100101
'exception_to_status' => [
101102
ExceptionInterface::class => Response::HTTP_BAD_REQUEST,

tests/Symfony/EventListener/ErrorListenerTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,20 @@ public function testDuplicateExceptionWithErrorResource(): void
103103
$this->assertTrue($request->attributes->has('_api_previous_operation'));
104104
$this->assertEquals('_api_errors_hydra', $request->attributes->get('_api_operation_name'));
105105

106+
$operation = $request->attributes->get('_api_operation');
107+
$this->assertEquals($operation->getNormalizationContext(), [
108+
// this flag is for bc layer on error normalizers
109+
'api_error_resource' => true,
110+
'ignored_attributes' => [
111+
'trace',
112+
'file',
113+
'line',
114+
'code',
115+
'message',
116+
'traceAsString',
117+
],
118+
]);
119+
106120
return true;
107121
}), HttpKernelInterface::SUB_REQUEST, false)->willReturn(new Response());
108122
$request = Request::create('/');

0 commit comments

Comments
 (0)