Skip to content

Commit a5b6c6a

Browse files
committed
Merge 3.4
2 parents ec6e645 + fbb53e5 commit a5b6c6a

File tree

10 files changed

+103
-12
lines changed

10 files changed

+103
-12
lines changed

CHANGELOG.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,6 @@ Notes:
142142
* [afe7d47d7](https://github.com/api-platform/core/commit/afe7d47d7b7ba6c8591bfb60137a65d1fa1fe38f) fix(metadata): passing class as parameter in XML ApiResource's definition (#6659)
143143
* [b93ee467c](https://github.com/api-platform/core/commit/b93ee467c69253e0cfe60e75b48a5c7aa683474a) fix(metadata): overwriting XML ApiResource definition by YAML ApiResource definition (#6660)
144144

145-
> [!WARNING]
146-
> Hydra prefix on errors is breaking, read `title` not `hydra:title`. The `hydra_prefix` flag doesn't apply to errors as it provided redundant information (both `hydra:title` and `title` were available)
147-
148145
## v3.4.1
149146

150147
### Bug fixes

features/hydra/error.feature

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ Feature: Error handling
1717
And the header "Link" should contain '<http://www.w3.org/ns/hydra/error>; rel="http://www.w3.org/ns/json-ld#error"'
1818
And the JSON node "type" should exist
1919
And the JSON node "title" should be equal to "An error occurred"
20+
And the JSON node "hydra:title" should be equal to "An error occurred"
2021
And the JSON node "detail" should exist
2122
And the JSON node "description" should exist
23+
And the JSON node "hydra:description" should exist
2224
And the JSON node "trace" should exist
2325
And the JSON node "status" should exist
2426
And the JSON node "@context" should exist
@@ -48,6 +50,8 @@ Feature: Error handling
4850
],
4951
"detail": "name: This value should not be blank.",
5052
"title": "An error occurred",
53+
"hydra:title": "An error occurred",
54+
"hydra:description": "name: This value should not be blank.",
5155
"description": "name: This value should not be blank.",
5256
"type": "/validation_errors/c1051bb4-d103-4f74-8988-acbcafc7fdc3"
5357
}

src/JsonLd/ContextBuilder.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace ApiPlatform\JsonLd;
1515

1616
use ApiPlatform\JsonLd\Serializer\HydraPrefixTrait;
17-
use ApiPlatform\Metadata\Error;
1817
use ApiPlatform\Metadata\Get;
1918
use ApiPlatform\Metadata\HttpOperation;
2019
use ApiPlatform\Metadata\IriConverterInterface;
@@ -185,7 +184,7 @@ private function getResourceContextWithShortname(string $resourceClass, int $ref
185184
}
186185
}
187186

188-
if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true) || $operation instanceof Error) {
187+
if (false === ($this->defaultContext[self::HYDRA_CONTEXT_HAS_PREFIX] ?? true)) {
189188
return ['http://www.w3.org/ns/hydra/context.jsonld', $context];
190189
}
191190

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\JsonLd\Serializer;
15+
16+
use ApiPlatform\State\ApiResource\Error;
17+
use ApiPlatform\Symfony\Validator\Exception\ValidationException as SymfonyValidationException;
18+
use ApiPlatform\Validator\Exception\ValidationException;
19+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
20+
21+
final class ErrorNormalizer implements NormalizerInterface
22+
{
23+
use HydraPrefixTrait;
24+
25+
public function __construct(private readonly NormalizerInterface $inner, private readonly array $defaultContext = [])
26+
{
27+
}
28+
29+
public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null
30+
{
31+
$normalized = $this->inner->normalize($object, $format, $context);
32+
$hydraPrefix = $this->getHydraPrefix($context + $this->defaultContext);
33+
if (!$hydraPrefix) {
34+
return $normalized;
35+
}
36+
37+
if (isset($normalized['description'])) {
38+
$normalized['hydra:description'] = $normalized['description'];
39+
}
40+
41+
if (isset($normalized['title'])) {
42+
$normalized['hydra:title'] = $normalized['title'];
43+
}
44+
45+
return $normalized;
46+
}
47+
48+
public function supportsNormalization(mixed $data, ?string $format = null, array $context = []): bool
49+
{
50+
return $this->inner->supportsNormalization($data, $format, $context)
51+
&& (is_a($data, Error::class) || is_a($data, ValidationException::class) || is_a($data, SymfonyValidationException::class));
52+
}
53+
54+
public function getSupportedTypes(?string $format): array
55+
{
56+
return $this->inner->getSupportedTypes($format);
57+
}
58+
}

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use ApiPlatform\Metadata\FilterInterface;
3535
use ApiPlatform\Metadata\UriVariableTransformerInterface;
3636
use ApiPlatform\Metadata\UrlGeneratorInterface;
37+
use ApiPlatform\RamseyUuid\Serializer\UuidDenormalizer;
3738
use ApiPlatform\State\ApiResource\Error;
3839
use ApiPlatform\State\ParameterProviderInterface;
3940
use ApiPlatform\State\ProcessorInterface;
@@ -180,7 +181,7 @@ private function registerCommonConfiguration(ContainerBuilder $container, array
180181
$loader->load('api.xml');
181182
$loader->load('filter.xml');
182183

183-
if (class_exists(Uuid::class)) {
184+
if (class_exists(UuidDenormalizer::class) && class_exists(Uuid::class)) {
184185
$loader->load('ramsey_uuid.xml');
185186
}
186187

src/Symfony/Bundle/DependencyInjection/Compiler/MetadataAwareNameConverterPass.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,20 @@ public function process(ContainerBuilder $container): void
3939
}
4040

4141
$definition = $container->getDefinition('serializer.name_converter.metadata_aware');
42-
$num = \count($definition->getArguments());
42+
$key = '$fallbackNameConverter';
43+
$arguments = $definition->getArguments();
44+
if (false === \array_key_exists($key, $arguments)) {
45+
$key = 1;
46+
}
4347

4448
if ($container->hasAlias('api_platform.name_converter')) {
4549
$nameConverter = new Reference((string) $container->getAlias('api_platform.name_converter'));
46-
if (1 === $num) {
50+
51+
// old symfony versions
52+
if (false === \array_key_exists($key, $arguments)) {
4753
$definition->addArgument($nameConverter);
48-
} elseif (1 < $num && null === $definition->getArgument(1)) {
49-
$definition->setArgument(1, $nameConverter);
54+
} elseif (null === $definition->getArgument($key)) {
55+
$definition->setArgument($key, $nameConverter);
5056
}
5157
}
5258

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
<service id="api_platform.hydra.normalizer.constraint_violation_list" class="ApiPlatform\Hydra\Serializer\ConstraintViolationListNormalizer" public="false">
2626
<argument>%api_platform.validator.serialize_payload_fields%</argument>
2727
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
28+
<argument>%api_platform.serializer.default_context%</argument>
2829

2930
<tag name="serializer.normalizer" priority="-780" />
3031
</service>

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@
3636
<tag name="serializer.normalizer" priority="-890" />
3737
</service>
3838

39+
<service id="api_platform.jsonld.normalizer.error" class="ApiPlatform\JsonLd\Serializer\ErrorNormalizer" public="false">
40+
<argument type="service" id="api_platform.jsonld.normalizer.item" />
41+
<argument>%api_platform.serializer.default_context%</argument>
42+
<tag name="serializer.normalizer" priority="-880" />
43+
</service>
44+
3945
<service id="api_platform.jsonld.normalizer.object" class="ApiPlatform\JsonLd\Serializer\ObjectNormalizer" public="false">
4046
<argument type="service" id="serializer.normalizer.object" />
4147
<argument type="service" id="api_platform.iri_converter" />
@@ -46,7 +52,7 @@
4652
</service>
4753

4854
<service id="api_platform.jsonld.normalizer.validation_exception" class="ApiPlatform\Symfony\Validator\Serializer\ValidationExceptionNormalizer" public="false">
49-
<argument type="service" id="api_platform.jsonld.normalizer.item" />
55+
<argument type="service" id="api_platform.jsonld.normalizer.error" />
5056
<argument type="service" id="api_platform.name_converter" on-invalid="ignore" />
5157

5258
<tag name="serializer.normalizer" priority="-800" />

src/Symfony/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@
6565
"api-platform/doctrine-odm": "To support MongoDB. Only versions 4.0 and later are supported.",
6666
"api-platform/elasticsearch": "To support Elasticsearch.",
6767
"api-platform/graphql": "To support GraphQL.",
68+
"api-platform/ramsey-uuid": "To support Ramsey's UUID identifiers.",
6869
"ocramius/package-versions": "To display the API Platform's version in the debug bar.",
6970
"phpstan/phpdoc-parser": "To support extracting metadata from PHPDoc.",
7071
"psr/cache-implementation": "To use metadata caching.",
71-
"ramsey/uuid": "To support Ramsey's UUID identifiers.",
7272
"symfony/cache": "To have metadata caching when using Symfony integration.",
7373
"symfony/config": "To load XML configuration files.",
7474
"symfony/expression-language": "To use authorization and mercure advanced features.",

tests/Symfony/Bundle/DependencyInjection/Compiler/MetadataAwareNameConverterPassTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,23 @@ public function testProcessOnlyOneArg(): void
101101

102102
$pass->process($containerBuilderProphecy->reveal());
103103
}
104+
105+
public function testProcessWithAbstractMetadataAware(): void
106+
{
107+
$pass = new MetadataAwareNameConverterPass();
108+
109+
$definition = $this->prophesize(Definition::class);
110+
$definition->getArguments()->willReturn(['$metadataFactory' => [], '$fallbackNameConverter' => null])->shouldBeCalled();
111+
$definition->getArgument('$fallbackNameConverter')->willReturn(null)->shouldBeCalled();
112+
$definition->setArgument('$fallbackNameConverter', new Reference('app.name_converter'))->willReturn($definition)->shouldBeCalled();
113+
114+
$containerBuilderProphecy = $this->prophesize(ContainerBuilder::class);
115+
$containerBuilderProphecy->hasDefinition('serializer.name_converter.metadata_aware')->willReturn(true)->shouldBeCalled();
116+
$containerBuilderProphecy->hasAlias('api_platform.name_converter')->shouldBeCalled()->willReturn(true);
117+
$containerBuilderProphecy->getAlias('api_platform.name_converter')->shouldBeCalled()->willReturn(new Alias('app.name_converter'));
118+
$containerBuilderProphecy->setAlias('api_platform.name_converter', 'serializer.name_converter.metadata_aware')->shouldBeCalled();
119+
$containerBuilderProphecy->getDefinition('serializer.name_converter.metadata_aware')->shouldBeCalled()->willReturn($definition);
120+
121+
$pass->process($containerBuilderProphecy->reveal());
122+
}
104123
}

0 commit comments

Comments
 (0)