From ec73a7650537e67e5178ec454ff6c2d51f3bec97 Mon Sep 17 00:00:00 2001 From: Ruud Kamphuis Date: Mon, 18 Nov 2024 15:57:41 +0100 Subject: [PATCH] Introduce PluginConfigurator This allows for configuring plugins via the normal config. When you have a plugin that does not require config, the old behavior is the easiest. But when you do want custom config per client, it will be cumbersome to create separate services and reference them everywhere. It would be easier to have the same type of configuration as the built-in clients. That's now possible with the PluginConfigurator. Define the plugins as follows: ```yaml 'plugins' => [ [ 'configurator' => [ 'id' => CustomPluginConfigurator::class, 'config' => [ 'name' => 'foo', ] ], ], ], ``` The `CustomPluginConfigurator` looks like this: ```php final class CustomPluginConfigurator implements PluginConfigurator { public static function getConfigTreeBuilder() : TreeBuilder { $treeBuilder = new TreeBuilder('custom_plugin'); $rootNode = $treeBuilder->getRootNode(); $rootNode ->children() ->scalarNode('name') ->isRequired() ->cannotBeEmpty() ->end() ->end(); return $treeBuilder; } public function create(array $config) : CustomPlugin { return new CustomPlugin($config['name']); } } ``` On compile time, the config will be evaluated. It will create the service definition by calling the `create` method with the given config. On runtime you will have the CustomPlugin instantiated with the custom config. --- CHANGELOG.md | 4 +++ src/DependencyInjection/Configuration.php | 16 +++++++++ src/DependencyInjection/HttplugExtension.php | 13 +++++++ src/PluginConfigurator.php | 20 +++++++++++ tests/Resources/CustomPlugin.php | 24 +++++++++++++ tests/Resources/CustomPluginConfigurator.php | 32 +++++++++++++++++ tests/Resources/Fixtures/config/full.php | 10 ++++++ tests/Resources/Fixtures/config/full.xml | 7 ++++ tests/Resources/Fixtures/config/full.yml | 5 +++ .../DependencyInjection/ConfigurationTest.php | 10 ++++++ .../HttplugExtensionTest.php | 36 +++++++++++++++++++ 11 files changed, 177 insertions(+) create mode 100644 src/PluginConfigurator.php create mode 100644 tests/Resources/CustomPlugin.php create mode 100644 tests/Resources/CustomPluginConfigurator.php diff --git a/CHANGELOG.md b/CHANGELOG.md index e37bd31a..bbd21e5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ The change log describes what is "Added", "Removed", "Changed" or "Fixed" betwee # Version 2 +# 2.1.0 + +- Added PluginConfigurator + # 2.0.0 - 2024-09-16 - Increased min PHP version to 8.1 diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 8456049c..f9d0fdb1 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -275,6 +275,22 @@ private function createClientPluginNode(): ArrayNodeDefinition ->end() ->end() ->end() + ->arrayNode('configurator') + ->canBeEnabled() + ->info('Configure a plugin with a configurator') + ->children() + ->scalarNode('id') + ->info('Service id of a plugin configurator') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->arrayNode('config') + ->normalizeKeys(false) + ->prototype('variable') + ->end() + ->end() + ->end() + ->end() ->arrayNode('add_host') ->canBeEnabled() ->addDefaultsIfNotSet() diff --git a/src/DependencyInjection/HttplugExtension.php b/src/DependencyInjection/HttplugExtension.php index 28519227..d8c913c3 100644 --- a/src/DependencyInjection/HttplugExtension.php +++ b/src/DependencyInjection/HttplugExtension.php @@ -16,6 +16,7 @@ use Http\Client\HttpAsyncClient; use Http\Client\Plugin\Vcr\RecordPlugin; use Http\Client\Plugin\Vcr\ReplayPlugin; +use Http\HttplugBundle\PluginConfigurator; use Http\Message\Authentication\BasicAuth; use Http\Message\Authentication\Bearer; use Http\Message\Authentication\Header; @@ -429,6 +430,18 @@ private function configureClient(ContainerBuilder $container, string $clientName case 'reference': $plugins[] = new Reference($pluginConfig['id']); break; + case 'configurator': + if (!is_a($pluginConfig['id'], PluginConfigurator::class, true)) { + throw new \LogicException(sprintf('The plugin "%s" is not a valid PluginConfigurator.', $pluginConfig['id'])); + } + + $config = $pluginConfig['id']::getConfigTreeBuilder()->buildTree()->finalize($pluginConfig['config']); + + $definition = new Definition(null, [$config]); + $definition->setFactory([new Reference($pluginConfig['id']), 'create']); + + $plugins[] = $definition; + break; case 'authentication': $plugins = array_merge( $plugins, diff --git a/src/PluginConfigurator.php b/src/PluginConfigurator.php new file mode 100644 index 00000000..ecad1761 --- /dev/null +++ b/src/PluginConfigurator.php @@ -0,0 +1,20 @@ + $config + */ + public function create(array $config): Plugin; +} diff --git a/tests/Resources/CustomPlugin.php b/tests/Resources/CustomPlugin.php new file mode 100644 index 00000000..c5d639f8 --- /dev/null +++ b/tests/Resources/CustomPlugin.php @@ -0,0 +1,24 @@ +name = $name; + } + + public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise + { + return $next($request); + } +} diff --git a/tests/Resources/CustomPluginConfigurator.php b/tests/Resources/CustomPluginConfigurator.php new file mode 100644 index 00000000..1d9fa32a --- /dev/null +++ b/tests/Resources/CustomPluginConfigurator.php @@ -0,0 +1,32 @@ +getRootNode(); + + $rootNode + ->children() + ->scalarNode('name') + ->isRequired() + ->cannotBeEmpty() + ->end() + ->end(); + + return $treeBuilder; + } + + public function create(array $config): CustomPlugin + { + return new CustomPlugin($config['name']); + } +} diff --git a/tests/Resources/Fixtures/config/full.php b/tests/Resources/Fixtures/config/full.php index b468f177..3c4c81b9 100644 --- a/tests/Resources/Fixtures/config/full.php +++ b/tests/Resources/Fixtures/config/full.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use Http\HttplugBundle\Tests\Resources\CustomPluginConfigurator; + $container->loadFromExtension('httplug', [ 'default_client_autowiring' => false, 'main_alias' => [ @@ -27,6 +29,14 @@ 'http_methods_client' => true, 'plugins' => [ 'httplug.plugin.redirect', + [ + 'configurator' => [ + 'id' => CustomPluginConfigurator::class, + 'config' => [ + 'name' => 'foo', + ], + ], + ], [ 'add_host' => [ 'host' => 'http://localhost', diff --git a/tests/Resources/Fixtures/config/full.xml b/tests/Resources/Fixtures/config/full.xml index d47867d7..a233cde4 100644 --- a/tests/Resources/Fixtures/config/full.xml +++ b/tests/Resources/Fixtures/config/full.xml @@ -22,6 +22,13 @@ httplug.plugin.redirect + + + + foo + + + diff --git a/tests/Resources/Fixtures/config/full.yml b/tests/Resources/Fixtures/config/full.yml index 7e054599..5d71166a 100644 --- a/tests/Resources/Fixtures/config/full.yml +++ b/tests/Resources/Fixtures/config/full.yml @@ -21,6 +21,11 @@ httplug: http_methods_client: true plugins: - 'httplug.plugin.redirect' + - + configurator: + id: Http\HttplugBundle\Tests\Resources\CustomPluginConfigurator + config: + name: foo - add_host: host: http://localhost diff --git a/tests/Unit/DependencyInjection/ConfigurationTest.php b/tests/Unit/DependencyInjection/ConfigurationTest.php index b7654501..6ba3d3a4 100644 --- a/tests/Unit/DependencyInjection/ConfigurationTest.php +++ b/tests/Unit/DependencyInjection/ConfigurationTest.php @@ -7,6 +7,7 @@ use Http\Adapter\Guzzle7\Client; use Http\HttplugBundle\DependencyInjection\Configuration; use Http\HttplugBundle\DependencyInjection\HttplugExtension; +use Http\HttplugBundle\Tests\Resources\CustomPluginConfigurator; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionConfigurationTestCase; use Nyholm\Psr7\Factory\Psr17Factory; use Symfony\Component\Config\Definition\ConfigurationInterface; @@ -155,6 +156,15 @@ public function testSupportsAllConfigFormats(): void 'id' => 'httplug.plugin.redirect', ], ], + [ + 'configurator' => [ + 'enabled' => true, + 'id' => CustomPluginConfigurator::class, + 'config' => [ + 'name' => 'foo', + ], + ], + ], [ 'add_host' => [ 'enabled' => true, diff --git a/tests/Unit/DependencyInjection/HttplugExtensionTest.php b/tests/Unit/DependencyInjection/HttplugExtensionTest.php index 8e105771..a1c51a24 100644 --- a/tests/Unit/DependencyInjection/HttplugExtensionTest.php +++ b/tests/Unit/DependencyInjection/HttplugExtensionTest.php @@ -7,8 +7,10 @@ use Http\Adapter\Guzzle7\Client; use Http\Client\Plugin\Vcr\Recorder\InMemoryRecorder; use Http\HttplugBundle\DependencyInjection\HttplugExtension; +use Http\HttplugBundle\Tests\Resources\CustomPluginConfigurator; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; use Psr\Http\Client\ClientInterface; +use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; /** @@ -203,6 +205,40 @@ public function testClientPlugins(): void $this->assertContainerBuilderHasService('httplug.client.mock'); } + public function testPluginConfiguratorConfig(): void + { + $config = [ + 'clients' => [ + 'acme' => [ + 'factory' => 'httplug.factory.curl', + 'plugins' => [ + [ + 'configurator' => [ + 'id' => CustomPluginConfigurator::class, + 'config' => [ + 'name' => 'foo', + ], + ], + ], + ], + ], + ], + ]; + + $this->load($config); + + $definition = new Definition(null, [ + ['name' => 'foo'], + ]); + $definition->setFactory([CustomPluginConfigurator::class, 'create']); + + $this->assertContainerBuilderHasService('httplug.client.acme'); + $this->assertContainerBuilderHasServiceDefinitionWithArgument('httplug.client.acme', 1, [ + $definition, + ]); + $this->assertContainerBuilderHasService('httplug.client.mock'); + } + public function testNoProfilingWhenNotInDebugMode(): void { $this->setParameter('kernel.debug', false);