Skip to content

Commit 659cf14

Browse files
authored
fix(metadata): defaults with new metadata allows extra keys (#4743)
* fix(metadata): defaults with new metadata allows extra keys * fix older versions * fix older versions * fix older versions * fix deprecation
1 parent 7df0ccd commit 659cf14

File tree

7 files changed

+99
-21
lines changed

7 files changed

+99
-21
lines changed

src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,16 +234,22 @@ private function getOperationWithDefaults(ApiResource $resource, Operation $oper
234234
*/
235235
private function addGlobalDefaults($operation)
236236
{
237+
$extraProperties = $operation->getExtraProperties();
237238
foreach ($this->defaults['attributes'] as $key => $value) {
238-
[$key, $value] = $this->getKeyValue($key, $value);
239-
$upperKey = ucfirst($key);
239+
[$newKey, $value] = $this->getKeyValue($key, $value);
240+
$upperKey = ucfirst($newKey);
240241
$getter = 'get'.$upperKey;
241-
if (method_exists($operation, $getter) && null === $operation->{$getter}()) {
242+
243+
if (!method_exists($operation, $getter)) {
244+
if (!isset($extraProperties[$key])) {
245+
$extraProperties[$key] = $value;
246+
}
247+
} elseif (null === $operation->{$getter}()) {
242248
$operation = $operation->{'with'.$upperKey}($value);
243249
}
244250
}
245251

246-
return $operation;
252+
return $operation->withExtraProperties($extraProperties);
247253
}
248254

249255
private function getResourceWithDefaults(string $resourceClass, string $shortName, ApiResource $resource): ApiResource

src/Metadata/WithResourceTrait.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ protected function copyFrom($resource)
3939
}
4040
}
4141

42-
return $self;
42+
return $self->withExtraProperties(array_merge($resource->getExtraProperties(), $self->getExtraProperties()));
4343
}
4444
}

src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private function registerCommonConfiguration(ContainerBuilder $container, array
253253
$container->setAlias('api_platform.name_converter', $config['name_converter']);
254254
}
255255
$container->setParameter('api_platform.asset_package', $config['asset_package']);
256-
$container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
256+
$container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? [], $config['metadata_backward_compatibility_layer'] ?? false));
257257
}
258258

259259
/**
@@ -274,12 +274,21 @@ private function getPaginationDefaults(array $defaults, array $collectionPaginat
274274
return array_merge($collectionPaginationConfiguration, $paginationOptions);
275275
}
276276

277-
private function normalizeDefaults(array $defaults): array
277+
private function normalizeDefaults(array $defaults, bool $compatibility = false): array
278278
{
279279
$normalizedDefaults = ['attributes' => $defaults['attributes'] ?? []];
280280
unset($defaults['attributes']);
281281

282-
[$publicProperties,] = ApiResource::getConfigMetadata();
282+
$publicProperties = [];
283+
284+
if ($compatibility) {
285+
[$publicProperties,] = ApiResource::getConfigMetadata();
286+
} else {
287+
$rc = new \ReflectionClass(ApiResource::class);
288+
foreach ($rc->getConstructor()->getParameters() as $param) {
289+
$publicProperties[$param->getName()] = true;
290+
}
291+
}
283292

284293
$nameConverter = new CamelCaseToSnakeCaseNameConverter();
285294
foreach ($defaults as $option => $value) {

src/Symfony/Bundle/DependencyInjection/Configuration.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
631631
$defaultsNode = $rootNode->children()->arrayNode('defaults');
632632

633633
$defaultsNode
634-
->ignoreExtraKeys()
634+
->ignoreExtraKeys(false)
635635
->beforeNormalization()
636636
->always(static function (array $defaults) use ($nameConverter) {
637637
$normalizedDefaults = [];

tests/Core/Bridge/Symfony/Bundle/Command/SwaggerCommandUnitTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
use ApiPlatform\Metadata\Resource\Factory\ResourceNameCollectionFactoryInterface;
1919
use ApiPlatform\Metadata\Resource\ResourceNameCollection;
2020
use PHPUnit\Framework\MockObject\MockObject;
21+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
2122
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
2223
use Symfony\Component\Console\Input\ArrayInput;
2324
use Symfony\Component\Console\Output\BufferedOutput;
2425
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2526

2627
class SwaggerCommandUnitTest extends KernelTestCase
2728
{
29+
use ExpectDeprecationTrait;
2830
/** @var MockObject&NormalizerInterface */
2931
private $normalizer;
3032

@@ -50,8 +52,12 @@ protected function setUp(): void
5052
->willReturn(new ResourceNameCollection());
5153
}
5254

55+
/**
56+
* @group legacy
57+
*/
5358
public function testDocumentationJsonDoesNotUseEscapedSlashes(): void
5459
{
60+
$this->expectDeprecation('The command "api:swagger:export" is using pre-2.7 metadata, new metadata will not appear, use "api:openapi:export" instead.');
5561
$this->normalizer->method('normalize')
5662
->with(self::isInstanceOf(Documentation::class))
5763
->willReturn(['a-jsonable-documentation' => 'containing/some/slashes']);

tests/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactoryTest.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ public function testCreateWithDefaults(): void
139139
{
140140
$attributeResourceMetadataCollectionFactory = new AttributesResourceMetadataCollectionFactory(null, null, ['attributes' => ['cache_headers' => ['max_age' => 60], 'non_existing_attribute' => 'foo']]);
141141

142-
$operation = new HttpOperation(shortName: 'AttributeDefaultOperations', class: AttributeDefaultOperations::class, cacheHeaders: ['max_age' => 60], paginationItemsPerPage: 10);
142+
$operation = new HttpOperation(shortName: 'AttributeDefaultOperations', class: AttributeDefaultOperations::class, cacheHeaders: ['max_age' => 60], paginationItemsPerPage: 10, extraProperties: ['non_existing_attribute' => 'foo']);
143+
143144
$this->assertEquals(new ResourceMetadataCollection(AttributeDefaultOperations::class, [
144145
new ApiResource(
145146
shortName: 'AttributeDefaultOperations',
@@ -154,7 +155,8 @@ class: AttributeDefaultOperations::class,
154155
'_api_AttributeDefaultOperations_delete' => (new Delete())->withOperation($operation),
155156
],
156157
cacheHeaders: ['max_age' => 60],
157-
paginationItemsPerPage: 10
158+
paginationItemsPerPage: 10,
159+
extraProperties: ['non_existing_attribute' => 'foo']
158160
),
159161
]), $attributeResourceMetadataCollectionFactory->create(AttributeDefaultOperations::class));
160162
}

tests/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
use Doctrine\ORM\OptimisticLockException;
5454
use phpDocumentor\Reflection\DocBlockFactoryInterface;
5555
use PHPUnit\Framework\TestCase;
56+
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
5657
use Symfony\Bundle\SecurityBundle\SecurityBundle;
5758
use Symfony\Component\DependencyInjection\ContainerBuilder;
5859
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
@@ -63,6 +64,7 @@
6364

6465
class ApiPlatformExtensionTest extends TestCase
6566
{
67+
use ExpectDeprecationTrait;
6668
use ProphecyTrait;
6769

6870
public const DEFAULT_CONFIG = ['api_platform' => [
@@ -111,16 +113,9 @@ class ApiPlatformExtensionTest extends TestCase
111113
'order_parameter_name' => 'order',
112114
'order_nulls_comparison' => null,
113115
'pagination' => [
114-
'client_enabled' => false,
115-
'client_items_per_page' => false,
116-
'enabled' => true,
116+
'page_parameter_name' => 'page',
117117
'enabled_parameter_name' => 'pagination',
118-
'items_per_page' => 30,
119118
'items_per_page_parameter_name' => 'itemsPerPage',
120-
'maximum_items_per_page' => 30,
121-
'page_parameter_name' => 'page',
122-
'partial' => false,
123-
'client_partial' => false,
124119
'partial_parameter_name' => 'partial',
125120
],
126121
],
@@ -136,8 +131,6 @@ class ApiPlatformExtensionTest extends TestCase
136131
OptimisticLockException::class => Response::HTTP_CONFLICT,
137132
],
138133
'show_webby' => true,
139-
// TODO: to remove in 3.0
140-
'allow_plain_identifiers' => false,
141134
'eager_loading' => [
142135
'enabled' => true,
143136
'max_joins' => 30,
@@ -1036,6 +1029,55 @@ public function testGraphQlConfigurationMetadataBackwardCompatibilityLayer(): vo
10361029
$this->assertServiceHasTags('api_platform.graphql.command.export_command', ['console.command']);
10371030
}
10381031

1032+
/**
1033+
* @group legacy
1034+
*/
1035+
public function testLegacyPlainIdentifier()
1036+
{
1037+
// There's an issue with deprecations being different on lower versions
1038+
if (\PHP_VERSION_ID < 80000) {
1039+
$this->markTestSkipped();
1040+
}
1041+
1042+
$config = self::DEFAULT_CONFIG;
1043+
$config['api_platform']['allow_plain_identifiers'] = false;
1044+
$this->expectDeprecation('Since api-platform/core 2.7: The use of `allow_plain_identifiers` has been deprecated in 2.7 and will be removed in 3.0.');
1045+
(new ApiPlatformExtension())->load($config, $this->container);
1046+
}
1047+
1048+
/**
1049+
* @group legacy
1050+
*/
1051+
public function testLegacyPaginationCollectionOptions()
1052+
{
1053+
// There's an issue with deprecations being different on lower versions
1054+
if (\PHP_VERSION_ID < 80000) {
1055+
$this->markTestSkipped();
1056+
}
1057+
1058+
$config = self::DEFAULT_CONFIG;
1059+
1060+
$config['api_platform']['collection']['pagination'] = [
1061+
'client_enabled' => false,
1062+
'client_items_per_page' => false,
1063+
'enabled' => true,
1064+
'items_per_page' => 30,
1065+
'maximum_items_per_page' => 30,
1066+
'partial' => false,
1067+
'client_partial' => false,
1068+
];
1069+
1070+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.enabled` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_enabled` instead.');
1071+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.partial` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_partial` instead.');
1072+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.client_enabled` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_enabled` instead.');
1073+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.client_items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_items_per_page` instead.');
1074+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.client_partial` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_client_partial` instead.');
1075+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_items_per_page` instead.');
1076+
$this->expectDeprecation('Since api-platform/core 2.6: The use of the `collection.pagination.maximum_items_per_page` has been deprecated in 2.6 and will be removed in 3.0. Use `defaults.pagination_maximum_items_per_page` instead.');
1077+
1078+
(new ApiPlatformExtension())->load($config, $this->container);
1079+
}
1080+
10391081
public function testLegacyBundlesConfigurationFosUserBundle(): void
10401082
{
10411083
$config = self::DEFAULT_CONFIG;
@@ -1824,4 +1866,17 @@ public function testAutoConfigurableInterfaces(): void
18241866

18251867
$this->assertEmpty(array_diff(array_keys($interfaces), $has), 'Not all expected interfaces are autoconfigurable.');
18261868
}
1869+
1870+
public function testDefaults()
1871+
{
1872+
$config = self::DEFAULT_CONFIG;
1873+
$config['api_platform']['defaults'] = [
1874+
'something' => 'test',
1875+
'attributes' => ['else' => 'foo'],
1876+
];
1877+
1878+
(new ApiPlatformExtension())->load($config, $this->container);
1879+
1880+
$this->assertEquals($this->container->getParameter('api_platform.defaults'), ['attributes' => ['else' => 'foo', 'something' => 'test']]);
1881+
}
18271882
}

0 commit comments

Comments
 (0)