Skip to content

Commit

Permalink
[Workflow] Add a way to add more definition validator
Browse files Browse the repository at this point in the history
  • Loading branch information
lyrixx committed Mar 26, 2024
1 parent 78c6ceb commit 31e1344
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 0 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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')
;
};
@@ -0,0 +1,17 @@
<?php

namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Fixtures\Workflow\Validator;

use Symfony\Component\Workflow\Definition;
use Symfony\Component\Workflow\Validator\DefinitionValidatorInterface;

class DefinitionValidator implements DefinitionValidatorInterface
{
public static bool $called = false;

public function validate(Definition $definition, string $name): void
{
self::$called = true;
}
}

@@ -0,0 +1,25 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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,
) {
}
}
@@ -0,0 +1,43 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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<DefinitionAndValidator> $definitionAndValidators

Check failure on line 20 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:20:15: UndefinedDocblockClass: Docblock-defined class, interface or enum named Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator does not exist (see https://psalm.dev/200)

Check failure on line 20 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:20:15: UndefinedDocblockClass: Docblock-defined class, interface or enum named Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator does not exist (see https://psalm.dev/200)
*/
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

Check failure on line 35 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:35:13: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)

Check failure on line 35 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:35:13: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)
->validator
->validate($definitionAndValidator->definition, $definitionAndValidator->name)

Check failure on line 37 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:37:28: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)

Check failure on line 37 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:37:65: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)

Check failure on line 37 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:37:28: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)

Check failure on line 37 in src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedDocblockClass

src/Symfony/Component/Workflow/CacheWarmer/DefinitionValidatorCacheWarmer.php:37:65: UndefinedDocblockClass: Cannot get properties of undefined docblock class Symfony\Component\Workflow\CacheWarmer\Model\DefinitionAndValidator (see https://psalm.dev/200)
;
}

return [];
}
}
@@ -0,0 +1,86 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <lyrixx@lyrixx.info>
*/
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) {

Check failure on line 54 in src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php:54:22: UndefinedClass: Type null cannot be called as a class (see https://psalm.dev/019)

Check failure on line 54 in src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedClass

src/Symfony/Component/Workflow/DependencyInjection/DefinitionValidatorPass.php:54:22: UndefinedClass: Type null cannot be called as a class (see https://psalm.dev/019)
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)
;
}
}
@@ -0,0 +1,23 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 <lyrixx@lyrixx.info>
*/
interface ConfiguredDefinitionValidatorInterface extends DefinitionValidatorInterface
{
/**
* @return list<string> A list of workflow name, or "*" for all workflows
*/
public static function getSupportedWorkflows(): iterable;
}

0 comments on commit 31e1344

Please sign in to comment.