Skip to content

Commit 56153b7

Browse files
authored
fix(hydra): error hydra prefix (#6599)
1 parent 48267c9 commit 56153b7

File tree

6 files changed

+70
-4
lines changed

6 files changed

+70
-4
lines changed

features/hydra/error.feature

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,21 @@ Feature: Error handling
120120
And the JSON node "title" should be equal to "An error occurred"
121121
And the JSON node "detail" should exist
122122
And the JSON node "violations" should exist
123+
124+
Scenario: Get an rfc 7807 error
125+
When I add "Content-Type" header equal to "application/ld+json"
126+
And I send a "POST" request to "/exception_problems_without_prefix" with body:
127+
"""
128+
{}
129+
"""
130+
Then the response status code should be 400
131+
And the response should be in JSON
132+
And the header "Content-Type" should be equal to "application/problem+json; charset=utf-8"
133+
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
134+
And the JSON node "type" should exist
135+
And the JSON node "title" should be equal to "An error occurred"
136+
And the JSON node "detail" should exist
137+
And the JSON node "description" should exist
138+
And the JSON node "trace" should exist
139+
And the JSON node "status" should exist
140+
And the JSON node "@context" should not exist

src/Hydra/Serializer/ErrorNormalizer.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
namespace ApiPlatform\Hydra\Serializer;
1515

1616
use ApiPlatform\Api\UrlGeneratorInterface as LegacyUrlGeneratorInterface;
17+
use ApiPlatform\JsonLd\Serializer\HydraPrefixTrait;
1718
use ApiPlatform\Metadata\UrlGeneratorInterface;
1819
use ApiPlatform\Serializer\CacheableSupportsMethodInterface;
1920
use ApiPlatform\State\ApiResource\Error;
@@ -32,6 +33,7 @@
3233
final class ErrorNormalizer implements NormalizerInterface, CacheableSupportsMethodInterface
3334
{
3435
use ErrorNormalizerTrait;
36+
use HydraPrefixTrait;
3537

3638
public const FORMAT = 'jsonld';
3739
public const TITLE = 'title';
@@ -47,11 +49,12 @@ public function __construct(private readonly LegacyUrlGeneratorInterface|UrlGene
4749
*/
4850
public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
4951
{
52+
$hydraPrefix = $this->getHydraPrefix($context);
5053
$data = [
5154
'@context' => $this->urlGenerator->generate('api_jsonld_context', ['shortName' => 'Error']),
52-
'@type' => 'hydra:Error',
53-
'hydra:title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE],
54-
'hydra:description' => $this->getErrorMessage($object, $context, $this->debug),
55+
'@type' => $hydraPrefix.'Error',
56+
$hydraPrefix.'title' => $context[self::TITLE] ?? $this->defaultContext[self::TITLE],
57+
$hydraPrefix.'description' => $this->getErrorMessage($object, $context, $this->debug),
5558
];
5659

5760
if ($this->debug && null !== $trace = $object->getTrace()) {
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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\Hydra\Serializer;
15+
16+
use ApiPlatform\JsonLd\ContextBuilder;
17+
use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
18+
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
19+
20+
final class HydraPrefixNameConverter implements NameConverterInterface, AdvancedNameConverterInterface
21+
{
22+
public function __construct(private readonly NameConverterInterface $nameConverter)
23+
{
24+
}
25+
26+
public function normalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string
27+
{
28+
$name = $this->nameConverter->normalize($propertyName, $class, $format, $context);
29+
30+
if (true === ($context[ContextBuilder::HYDRA_CONTEXT_HAS_PREFIX] ?? true)) {
31+
return $name;
32+
}
33+
34+
return str_starts_with($name, ContextBuilder::HYDRA_PREFIX) ? str_replace(ContextBuilder::HYDRA_PREFIX, '', $name) : $name;
35+
}
36+
37+
public function denormalize(string $propertyName, ?string $class = null, ?string $format = null, array $context = []): string
38+
{
39+
return $this->nameConverter->denormalize($propertyName, $class, $format, $context);
40+
}
41+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@
7676
<argument on-invalid="ignore">%api_platform.serializer.default_context%</argument>
7777
</service>
7878

79+
<service id="api_platform.hydra.name_converter.hydra_prefix" class="ApiPlatform\Hydra\Serializer\HydraPrefixNameConverter" decorates="api_platform.name_converter">
80+
<argument type="service" id="api_platform.hydra.name_converter.hydra_prefix.inner" />
81+
</service>
7982
</services>
8083

8184
</container>

src/Symfony/EventListener/ErrorListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ protected function duplicateRequest(\Throwable $exception, Request $request): Re
110110
$operation = $operation->withProvider('api_platform.state.error_provider');
111111
}
112112

113-
$normalizationContext = $operation->getNormalizationContext() ?? [];
113+
$normalizationContext = ($operation->getNormalizationContext() ?? []) + ($apiOperation?->getNormalizationContext() ?? []);
114114
if (!($normalizationContext['api_error_resource'] ?? false)) {
115115
$normalizationContext += ['api_error_resource' => true];
116116
}

tests/Fixtures/TestBundle/ApiResource/ValidationExceptionProblem.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#[Post(processor: [ValidationExceptionProblem::class, 'provide'])]
2222
#[Post(uriTemplate: '/exception_problems', processor: [ValidationExceptionProblem::class, 'provideException'])]
2323
#[Post(uriTemplate: '/exception_problems_with_compatibility', processor: [ValidationExceptionProblem::class, 'provideException'], extraProperties: ['rfc_7807_compliant_errors' => false])]
24+
#[Post(uriTemplate: '/exception_problems_without_prefix', normalizationContext: ['hydra_prefix' => false], processor: [ValidationExceptionProblem::class, 'provideException'], extraProperties: ['rfc_7807_compliant_errors' => true])]
2425
class ValidationExceptionProblem
2526
{
2627
public static function provide(): void

0 commit comments

Comments
 (0)