From 31e1344570ce0f8ca1b15dd21b2f34836a34e018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Pineau?= Date: Tue, 26 Mar 2024 08:50:59 +0100 Subject: [PATCH] [Workflow] Add a way to add more definition validator --- .../FrameworkExtension.php | 5 ++ .../FrameworkBundle/FrameworkBundle.php | 2 + .../Resources/config/workflow.php | 6 ++ .../Validator/DefinitionValidator.php | 17 ++++ .../CacheWarmer/DefinitionAndValidator.php | 25 ++++++ .../DefinitionValidatorCacheWarmer.php | 43 ++++++++++ .../DefinitionValidatorPass.php | 86 +++++++++++++++++++ ...ConfiguredDefinitionValidatorInterface.php | 23 +++++ 8 files changed, 207 insertions(+) create mode 100644 src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php create mode 100644 src/Symfony/Component/Workflow/CacheWarmer/DefinitionAndValidator.php create mode 100644 src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php create mode 100644 src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php create mode 100644 src/Symfony/Component/Workflow/Validator/ConfiguredDefinitionValidatorInterface.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 1fd7881f34877..b13785d1fcf67 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -183,6 +183,7 @@ use Symfony\Component\Webhook\Controller\WebhookController; use Symfony\Component\WebLink\HttpHeaderSerializer; use Symfony\Component\Workflow; +use Symfony\Component\Workflow\Validator\ConfiguredDefinitionValidatorInterface; use Symfony\Component\Workflow\WorkflowInterface; use Symfony\Component\Yaml\Command\LintCommand as BaseYamlLintCommand; use Symfony\Component\Yaml\Yaml; @@ -1010,6 +1011,7 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $definitionDefinition->addArgument($transitions); $definitionDefinition->addArgument($initialMarking); $definitionDefinition->addArgument(new Reference(sprintf('%s.metadata_store', $workflowId))); + $definitionDefinition->addTag('workflow.definition', ['name' => $name]); // Create MarkingStore $markingStoreDefinition = null; @@ -1104,6 +1106,9 @@ private function registerWorkflowConfiguration(array $config, ContainerBuilder $ $container->setParameter('workflow.has_guard_listeners', true); } } + + $container->registerForAutoconfiguration(ConfiguredDefinitionValidatorInterface::class) + ->addTag('workflow.definition_validator'); } private function registerDebugConfiguration(array $config, ContainerBuilder $container, PhpFileLoader $loader): void diff --git a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php index 26784bec367d2..9c780f61a1903 100644 --- a/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php +++ b/src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php @@ -70,6 +70,7 @@ use Symfony\Component\Validator\DependencyInjection\AddValidatorInitializersPass; use Symfony\Component\VarExporter\Internal\Hydrator; use Symfony\Component\VarExporter\Internal\Registry; +use Symfony\Component\Workflow\DependencyInjection\DefinitionValidatorPass; use Symfony\Component\Workflow\DependencyInjection\WorkflowDebugPass; use Symfony\Component\Workflow\DependencyInjection\WorkflowGuardListenerPass; @@ -158,6 +159,7 @@ public function build(ContainerBuilder $container): void $container->addCompilerPass(new CachePoolPrunerPass(), PassConfig::TYPE_AFTER_REMOVING); $this->addCompilerPassIfExists($container, FormPass::class); $this->addCompilerPassIfExists($container, WorkflowGuardListenerPass::class); + $this->addCompilerPassIfExists($container, DefinitionValidatorPass::class); $container->addCompilerPass(new ResettableServicePass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32); $container->addCompilerPass(new RegisterLocaleAwareServicesPass()); $container->addCompilerPass(new TestServiceContainerWeakRefPass(), PassConfig::TYPE_BEFORE_REMOVING, -32); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php index b6c784bdbeaa9..8bdb1fb88e471 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/workflow.php @@ -11,6 +11,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use Symfony\Component\Workflow\CacheWarmer\DefinitionValidatorCacheWarmer; use Symfony\Component\Workflow\EventListener\ExpressionLanguage; use Symfony\Component\Workflow\MarkingStore\MethodMarkingStore; use Symfony\Component\Workflow\Registry; @@ -42,5 +43,10 @@ ->set('workflow.registry', Registry::class) ->alias(Registry::class, 'workflow.registry') ->set('workflow.security.expression_language', ExpressionLanguage::class) + ->set('workflow.cache_warmer.definition_validator', DefinitionValidatorCacheWarmer::class) + ->args([ + abstract_arg('definition and validators'), + ]) + ->tag('kernel.cache_warmer') ; }; diff --git a/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php new file mode 100644 index 0000000000000..29fd60ef3e437 --- /dev/null +++ b/src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Fixtures/Workflow/Validator/DefinitionValidator.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\CacheWarmer; + +use Symfony\Component\Workflow\Definition; +use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface; + +final class DefinitionAndValidator +{ + public function __construct( + public readonly DefinitionValidatorInterface $validator, + public readonly Definition $definition, + public readonly string $name, + ) { + } +} diff --git a/src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php b/src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php new file mode 100644 index 0000000000000..52c16a247201b --- /dev/null +++ b/src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\CacheWarmer; + +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; +use Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator; + +final class DefinitionValidatorCacheWarmer implements CacheWarmerInterface +{ + /** + * @param iterable $definitionAndValidators + */ + public function __construct( + private readonly iterable $definitionAndValidators, + ) { + } + + public function isOptional(): bool + { + return false; + } + + public function warmUp(string $cacheDir, ?string $buildDir = null): array + { + foreach ($this->definitionAndValidators as $definitionAndValidator) { + $definitionAndValidator + ->validator + ->validate($definitionAndValidator->definition, $definitionAndValidator->name) + ; + } + + return []; + } +} diff --git a/src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php b/src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php new file mode 100644 index 0000000000000..ba189822b79c1 --- /dev/null +++ b/src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\DependencyInjection; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\Workflow\CacheWarmer\DefinitionAndValidator; +use Symfony\Component\Workflow\Validator\ConfiguredDefinitionValidatorInterface; + +/** + * @author Grégoire Pineau + */ +class DefinitionValidatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition('workflow.cache_warmer.definition_validator')) { + return; + } + + $definitions = []; + foreach ($container->findTaggedServiceIds('workflow.definition') as $id => $attributes) { + $name = $attributes[0]['name'] ?? throw new InvalidArgumentException(sprintf('The "name" attribute is mandatory for the "workflow.definition" tag. Check the tag for service "%s".', $id)); + $definitions[$name] = new Reference($id); + } + + + $definitionAndValidators = []; + foreach ($container->findTaggedServiceIds('workflow.definition_validator') as $id => $attributes) + { + $def = $container->getDefinition($id); + + $class = $def->getClass(); + + if (!$r = $container->getReflectionClass($class)) { + throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $id)); + } + if (!$r->isSubclassOf(ConfiguredDefinitionValidatorInterface::class)) { + throw new InvalidArgumentException(sprintf('Service "%s" must implement interface "%s".', $id, ConfiguredDefinitionValidatorInterface::class)); + } + + foreach ($class::getSupportedWorkflows() as $name) { + if ('*' === $name) { + foreach ($definitions as $definitionName => $definition) { + $definitionAndValidators[] = new Definition( + DefinitionAndValidator::class, + [ + new Reference($id), + $definition, + $definitionName, + ] + ); + } + } elseif (isset($definitions[$name])) { + $definitionAndValidators[] = new Definition( + DefinitionAndValidator::class, + [ + new Reference($id), + $definitions[$name], + $name, + ] + ); + } else { + throw new InvalidArgumentException(sprintf('The workflow "%s" does not exist. Check the "getConfiguration()" method of the service "%s".', $name, $id)); + } + } + } + + $container + ->getDefinition('workflow.cache_warmer.definition_validator') + ->replaceArgument(0, $definitionAndValidators) + ; + } +} diff --git a/src/Symfony/Component/Workflow/Validator/ConfiguredDefinitionValidatorInterface.php b/src/Symfony/Component/Workflow/Validator/ConfiguredDefinitionValidatorInterface.php new file mode 100644 index 0000000000000..2ceb5044b7c57 --- /dev/null +++ b/src/Symfony/Component/Workflow/Validator/ConfiguredDefinitionValidatorInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Workflow\Validator; + +/** + * @author Grégoire Pineau + */ +interface ConfiguredDefinitionValidatorInterface extends DefinitionValidatorInterface +{ + /** + * @return list A list of workflow name, or "*" for all workflows + */ + public static function getSupportedWorkflows(): iterable; +}