From 8b3aacd9e3c557c27e2ec3f58ad7818972845120 Mon Sep 17 00:00:00 2001 From: Beno!t POLASZEK Date: Wed, 2 Oct 2019 11:37:05 +0200 Subject: [PATCH 1/2] Add default configuration --- .../ApiPlatformExtension.php | 6 ++ .../DependencyInjection/Configuration.php | 20 ++++++ .../Resources/config/metadata/metadata.xml | 5 ++ ...ltConfigurationResourceMetadataFactory.php | 65 +++++++++++++++++++ .../ApiPlatformExtensionTest.php | 3 + .../DependencyInjection/ConfigurationTest.php | 20 ++++++ ...nfigurationResourceMetadataFactoryTest.php | 55 ++++++++++++++++ 7 files changed, 174 insertions(+) create mode 100644 src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php create mode 100644 tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index cf39090b833..9dd004d70e8 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -117,6 +117,7 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerElasticsearchConfiguration($container, $config, $loader); $this->registerDataTransformerConfiguration($container); $this->registerSecurityConfiguration($container, $loader); + $this->registerDefaultsConfiguration($container, $config); $container->registerForAutoconfiguration(DataPersisterInterface::class) ->addTag('api_platform.data_persister'); @@ -602,4 +603,9 @@ private function registerSecurityConfiguration(ContainerBuilder $container, XmlF $loader->load('security.xml'); } } + + private function registerDefaultsConfiguration(ContainerBuilder $container, array $config): void + { + $container->setParameter('api_platform.defaults', $config['defaults']); + } } diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php index b49c43b28ed..01a66ee3b80 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php @@ -13,6 +13,7 @@ namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection; +use ApiPlatform\Core\Annotation\ApiResource; use ApiPlatform\Core\Bridge\Elasticsearch\Metadata\Document\DocumentMetadata; use ApiPlatform\Core\Exception\FilterValidationException; use ApiPlatform\Core\Exception\InvalidArgumentException; @@ -179,6 +180,8 @@ public function getConfigTreeBuilder() 'jsonld' => ['mime_types' => ['application/ld+json']], ]); + $this->addDefaultsSection($rootNode); + return $treeBuilder; } @@ -494,4 +497,21 @@ private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, ar ->end() ->end(); } + + private function addDefaultsSection(ArrayNodeDefinition $rootNode): void + { + $defaultsNode = $rootNode->children()->arrayNode('defaults')->addDefaultsIfNotSet(); + $attributesNode = $defaultsNode->children()->arrayNode('attributes')->addDefaultsIfNotSet(); + $properties = (new \ReflectionClass(ApiResource::class))->getProperties(); + + foreach ($properties as $property) { + $optionName = $property->getName(); + if ('attributes' === $optionName) { // "attributes" is a sub-level and not a config option + continue; + } + + $node = $property->isPublic() ? $defaultsNode : $attributesNode; + $node->children()->variableNode($optionName); + } + } } diff --git a/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml b/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml index 1a50a9b0e14..5a071c899a1 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml @@ -33,6 +33,11 @@ %api_platform.patch_formats% + + + %api_platform.defaults% + + diff --git a/src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php b/src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php new file mode 100644 index 00000000000..8a6e2669ce4 --- /dev/null +++ b/src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Metadata\Resource\Factory; + +use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; + +/** + * Creates a resource metadata from the default configuration. + * + * @author Beno!t POLASZEK + */ +final class DefaultConfigurationResourceMetadataFactory implements ResourceMetadataFactoryInterface +{ + private $decorated; + + private $options; + + public function __construct(ResourceMetadataFactoryInterface $decorated, array $options) + { + $this->decorated = $decorated; + $this->options = $options; + } + + /** + * {@inheritdoc} + */ + public function create(string $resourceClass): ResourceMetadata + { + $resourceMetadata = $this->decorated->create($resourceClass); + $attributes = $resourceMetadata->getAttributes(); + + $baseConfig = array_filter($this->options, function (string $key) { + return 'attributes' !== $key; + }, \ARRAY_FILTER_USE_KEY); + $attributesConfig = $this->options['attributes'] ?? []; + + foreach ($baseConfig as $key => $value) { + if (method_exists($resourceMetadata, 'get'.$key) && null === $resourceMetadata->{'get'.$key}() && method_exists($resourceMetadata, 'with'.$key)) { + $resourceMetadata = $resourceMetadata->{'with'.$key}($value); + } + } + + foreach ($attributesConfig as $key => $value) { + if (\array_key_exists($key, $attributes)) { + continue; + } + + $attributes[$key] = $value; + $resourceMetadata = $resourceMetadata->withAttributes($attributes); + } + + return $resourceMetadata; + } +} diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 11f23d41b3f..72e9adca18a 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -694,6 +694,7 @@ public function testEnableElasticsearch() $containerBuilderProphecy->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)->willReturn($this->childDefinitionProphecy)->shouldBeCalled(); $containerBuilderProphecy->setParameter('api_platform.elasticsearch.hosts', ['http://elasticsearch:9200'])->shouldBeCalled(); $containerBuilderProphecy->setParameter('api_platform.elasticsearch.mapping', [])->shouldBeCalled(); + $containerBuilderProphecy->setParameter('api_platform.defaults', ['attributes' => []])->shouldBeCalled(); $config = self::DEFAULT_CONFIG; $config['api_platform']['elasticsearch'] = [ @@ -885,6 +886,7 @@ private function getPartialContainerBuilderProphecy() 'api_platform.metadata.property.name_collection_factory.property_info', 'api_platform.metadata.property.name_collection_factory.xml', 'api_platform.metadata.resource.metadata_factory.cached', + 'api_platform.metadata.resource.metadata_factory.defaults', 'api_platform.metadata.resource.metadata_factory.operation', 'api_platform.metadata.resource.metadata_factory.formats', 'api_platform.metadata.resource.metadata_factory.input_output', @@ -1076,6 +1078,7 @@ private function getBaseContainerBuilderProphecy(array $doctrineIntegrationsToLo 'api_platform.resource_class_directories' => Argument::type('array'), 'api_platform.validator.serialize_payload_fields' => [], 'api_platform.elasticsearch.enabled' => false, + 'api_platform.defaults' => ['attributes' => []], ]; foreach ($parameters as $key => $value) { diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php index a891c8724e2..39e07400a81 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ConfigurationTest.php @@ -204,6 +204,9 @@ private function runDefaultConfigTests(array $doctrineIntegrationsToLoad = ['orm ], 'allow_plain_identifiers' => false, 'resource_class_directories' => [], + 'defaults' => [ + 'attributes' => [], + ], ], $config); } @@ -402,4 +405,21 @@ public function testEnableElasticsearch() $this->assertTrue($config['elasticsearch']['enabled']); } + + public function testDefaultsConfig() + { + $config = $this->processor->processConfiguration($this->configuration, [ + 'api_platform' => [ + 'defaults' => [ + 'description' => 'foo', + 'attributes' => [ + 'mercure' => true, + ], + ], + ], + ]); + + $this->assertEquals('foo', $config['defaults']['description']); + $this->assertTrue($config['defaults']['attributes']['mercure']); + } } diff --git a/tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php b/tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php new file mode 100644 index 00000000000..cb711862a66 --- /dev/null +++ b/tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Metadata\Resource\Factory; + +use ApiPlatform\Core\Metadata\Resource\Factory\DefaultConfigurationResourceMetadataFactory; +use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; +use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; +use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; +use PHPUnit\Framework\TestCase; + +class DefaultConfigurationResourceMetadataFactoryTest extends TestCase +{ + public function testCreate() + { + $decoratedProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); + $decoratedProphecy->create(Dummy::class)->shouldBeCalled()->willReturn(new ResourceMetadata( + 'foo', + null, + null, + null, + null, + [ + 'mercure' => true, + ] + )); + + $defaults = [ + 'shortName' => 'bar', + 'description' => 'A Foo entity', + 'attributes' => [ + 'mercure' => false, + 'messenger' => true, + ], + ]; + + $factory = new DefaultConfigurationResourceMetadataFactory($decoratedProphecy->reveal(), $defaults); + + $resourceMetadata = $factory->create(Dummy::class); + $this->assertEquals('foo', $resourceMetadata->getShortName()); + $this->assertEquals('A Foo entity', $resourceMetadata->getDescription()); + $this->assertTrue($resourceMetadata->getAttribute('mercure')); + $this->assertTrue($resourceMetadata->getAttribute('messenger')); + } +} From df86b86b64d119e0b2173e766b70e131f35e1fa7 Mon Sep 17 00:00:00 2001 From: Beno!t POLASZEK Date: Wed, 2 Oct 2019 15:11:44 +0200 Subject: [PATCH 2/2] Apply changes from code review --- .../ApiPlatformExtension.php | 7 +------ .../Resources/config/metadata/metadata.xml | 2 +- ...php => DefaultsResourceMetadataFactory.php} | 18 +++++++++--------- .../ApiPlatformExtensionTest.php | 4 ++++ ...=> DefaultsResourceMetadataFactoryTest.php} | 8 ++++---- 5 files changed, 19 insertions(+), 20 deletions(-) rename src/Metadata/Resource/Factory/{DefaultConfigurationResourceMetadataFactory.php => DefaultsResourceMetadataFactory.php} (76%) rename tests/Metadata/Resource/Factory/{DefaultConfigurationResourceMetadataFactoryTest.php => DefaultsResourceMetadataFactoryTest.php} (83%) diff --git a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php index 9dd004d70e8..8b5ef60d791 100644 --- a/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php +++ b/src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php @@ -117,7 +117,6 @@ public function load(array $configs, ContainerBuilder $container): void $this->registerElasticsearchConfiguration($container, $config, $loader); $this->registerDataTransformerConfiguration($container); $this->registerSecurityConfiguration($container, $loader); - $this->registerDefaultsConfiguration($container, $config); $container->registerForAutoconfiguration(DataPersisterInterface::class) ->addTag('api_platform.data_persister'); @@ -192,6 +191,7 @@ private function registerCommonConfiguration(ContainerBuilder $container, array if ($config['name_converter']) { $container->setAlias('api_platform.name_converter', $config['name_converter']); } + $container->setParameter('api_platform.defaults', $config['defaults']); } private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void @@ -603,9 +603,4 @@ private function registerSecurityConfiguration(ContainerBuilder $container, XmlF $loader->load('security.xml'); } } - - private function registerDefaultsConfiguration(ContainerBuilder $container, array $config): void - { - $container->setParameter('api_platform.defaults', $config['defaults']); - } } diff --git a/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml b/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml index 5a071c899a1..b9ef44f00c2 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/metadata/metadata.xml @@ -33,7 +33,7 @@ %api_platform.patch_formats% - + %api_platform.defaults% diff --git a/src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php b/src/Metadata/Resource/Factory/DefaultsResourceMetadataFactory.php similarity index 76% rename from src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php rename to src/Metadata/Resource/Factory/DefaultsResourceMetadataFactory.php index 8a6e2669ce4..7d490ee51f4 100644 --- a/src/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactory.php +++ b/src/Metadata/Resource/Factory/DefaultsResourceMetadataFactory.php @@ -20,16 +20,16 @@ * * @author Beno!t POLASZEK */ -final class DefaultConfigurationResourceMetadataFactory implements ResourceMetadataFactoryInterface +final class DefaultsResourceMetadataFactory implements ResourceMetadataFactoryInterface { private $decorated; - private $options; + private $defaults; - public function __construct(ResourceMetadataFactoryInterface $decorated, array $options) + public function __construct(ResourceMetadataFactoryInterface $decorated, array $defaults) { $this->decorated = $decorated; - $this->options = $options; + $this->defaults = $defaults; } /** @@ -39,13 +39,13 @@ public function create(string $resourceClass): ResourceMetadata { $resourceMetadata = $this->decorated->create($resourceClass); $attributes = $resourceMetadata->getAttributes(); + $attributesConfig = $this->defaults['attributes'] ?? []; - $baseConfig = array_filter($this->options, function (string $key) { - return 'attributes' !== $key; - }, \ARRAY_FILTER_USE_KEY); - $attributesConfig = $this->options['attributes'] ?? []; + foreach ($this->defaults as $key => $value) { + if ('attributes' === $key) { + continue; + } - foreach ($baseConfig as $key => $value) { if (method_exists($resourceMetadata, 'get'.$key) && null === $resourceMetadata->{'get'.$key}() && method_exists($resourceMetadata, 'with'.$key)) { $resourceMetadata = $resourceMetadata->{'with'.$key}($value); } diff --git a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php index 72e9adca18a..7bb63186306 100644 --- a/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php +++ b/tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php @@ -144,6 +144,9 @@ class ApiPlatformExtensionTest extends TestCase 'doctrine_mongodb_odm' => [ 'enabled' => false, ], + 'defaults' => [ + 'attributes' => [], + ], ]]; private $extension; @@ -805,6 +808,7 @@ private function getPartialContainerBuilderProphecy() 'api_platform.http_cache.shared_max_age' => null, 'api_platform.http_cache.vary' => ['Accept'], 'api_platform.http_cache.public' => null, + 'api_platform.defaults' => ['attributes' => []], 'api_platform.enable_entrypoint' => true, 'api_platform.enable_docs' => true, ]; diff --git a/tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php b/tests/Metadata/Resource/Factory/DefaultsResourceMetadataFactoryTest.php similarity index 83% rename from tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php rename to tests/Metadata/Resource/Factory/DefaultsResourceMetadataFactoryTest.php index cb711862a66..98b743185d5 100644 --- a/tests/Metadata/Resource/Factory/DefaultConfigurationResourceMetadataFactoryTest.php +++ b/tests/Metadata/Resource/Factory/DefaultsResourceMetadataFactoryTest.php @@ -13,15 +13,15 @@ namespace ApiPlatform\Core\Tests\Metadata\Resource\Factory; -use ApiPlatform\Core\Metadata\Resource\Factory\DefaultConfigurationResourceMetadataFactory; +use ApiPlatform\Core\Metadata\Resource\Factory\DefaultsResourceMetadataFactory; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; use PHPUnit\Framework\TestCase; -class DefaultConfigurationResourceMetadataFactoryTest extends TestCase +class DefaultsResourceMetadataFactoryTest extends TestCase { - public function testCreate() + public function testCreate(): void { $decoratedProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $decoratedProphecy->create(Dummy::class)->shouldBeCalled()->willReturn(new ResourceMetadata( @@ -44,7 +44,7 @@ public function testCreate() ], ]; - $factory = new DefaultConfigurationResourceMetadataFactory($decoratedProphecy->reveal(), $defaults); + $factory = new DefaultsResourceMetadataFactory($decoratedProphecy->reveal(), $defaults); $resourceMetadata = $factory->create(Dummy::class); $this->assertEquals('foo', $resourceMetadata->getShortName());