diff --git a/documentation/user/getting-started/_index.fr.md b/documentation/user/getting-started/_index.fr.md index 0566b3f3..95f52c51 100644 --- a/documentation/user/getting-started/_index.fr.md +++ b/documentation/user/getting-started/_index.fr.md @@ -152,15 +152,21 @@ when@dev: ### Marquer les workflows -Toute classe portant `#[AsWorkflow]` dans votre espace de noms de workflows est enregistrée automatiquement dès que vous marquez le dossier : +Rien à écrire. Une classe portant `#[AsWorkflow]` est enregistrée dès qu'elle est un service — ce +qu'avec l'`autoconfigure: true` par défaut d'une application Symfony elle est déjà. + +Les versions précédentes demandaient de marquer le dossier à la main : ```yaml -# config/services.yaml +# config/services.yaml — désormais inutile App\Workflow\: resource: '../src/Workflow/' tags: [durable.workflow] ``` +La balise fonctionne toujours : une application qui l'écrit continue de marcher, elle fait +simplement double emploi. + ### Déclarer les implémentations d'activité Les classes d'implémentation d'activité sont des services Symfony ordinaires (l'autowiring s'applique). Si vous posez `#[AsActivityHandler]` sur la classe, le bundle les ramasse tout seul dès que le service est marqué. diff --git a/documentation/user/getting-started/_index.md b/documentation/user/getting-started/_index.md index 1703ec75..cb3ec37b 100644 --- a/documentation/user/getting-started/_index.md +++ b/documentation/user/getting-started/_index.md @@ -152,15 +152,20 @@ when@dev: ### Tag workflows -Any class annotated with `#[AsWorkflow]` in your workflow namespace is auto-registered when you tag the folder: +Nothing to write. A class carrying `#[AsWorkflow]` is registered as soon as it is a service, which +with the default `autoconfigure: true` of a Symfony application it already is. + +Earlier versions required tagging the folder by hand: ```yaml -# config/services.yaml +# config/services.yaml — no longer needed App\Workflow\: resource: '../src/Workflow/' tags: [durable.workflow] ``` +The tag still works, so an application that writes it keeps working; it is simply redundant. + ### Register activity implementations Activity implementation classes are registered as normal Symfony services (autowiring applies). If you use `#[AsActivityHandler]` on the class, the bundle picks them up automatically when the service is tagged. diff --git a/documentation/user/packages/_index.fr.md b/documentation/user/packages/_index.fr.md index 919e2bdf..7ef5501d 100644 --- a/documentation/user/packages/_index.fr.md +++ b/documentation/user/packages/_index.fr.md @@ -72,8 +72,10 @@ composer require gplanchat/durable-bundle Ce qu'il fait, et que vous écririez autrement à la main : -- **L'autoconfiguration.** Les classes portant `#[AsWorkflow]` et `#[AsActivity]` s'enregistrent seules ; - vous ne les listez pas dans un fichier de conteneur. +- **L'autoconfiguration.** Les classes portant `#[AsWorkflow]` ou `#[AsActivityHandler]` + s'enregistrent seules ; vous ne les listez pas dans un fichier de conteneur, et vous ne les + balisez pas non plus. `#[AsActivity]` est un attribut de nommage posé sur le contrat, pas + d'enregistrement. - **Le câblage Messenger.** Reprises de workflow et envois d'activité sont routés vers les transports que vous nommez dans `durable.yaml`, si bien qu'un workflow qui se suspend reprend par vos files existantes. diff --git a/documentation/user/packages/_index.md b/documentation/user/packages/_index.md index b6917bfd..c9912c54 100644 --- a/documentation/user/packages/_index.md +++ b/documentation/user/packages/_index.md @@ -68,8 +68,9 @@ composer require gplanchat/durable-bundle What it does that you would otherwise write by hand: -- **Autoconfiguration.** Classes carrying `#[AsWorkflow]` and `#[AsActivity]` are registered on their - own; you do not list them in a container file. +- **Autoconfiguration.** Classes carrying `#[AsWorkflow]` or `#[AsActivityHandler]` register + themselves; you do not list them in a container file, and you do not tag them either. + `#[AsActivity]` is a naming attribute on the contract, not a registration one. - **Messenger wiring.** Workflow resumes and activity dispatches are routed to the transports you name in `durable.yaml`, so a workflow that suspends resumes through your existing queues. - **One console command.** `durable:execution:diagnose ` prints what the engine holds diff --git a/src/DurableBundle/DurableBundle.php b/src/DurableBundle/DurableBundle.php index 2c9feaca..458d2a2c 100644 --- a/src/DurableBundle/DurableBundle.php +++ b/src/DurableBundle/DurableBundle.php @@ -5,6 +5,7 @@ namespace Gplanchat\Durable\Bundle; use Gplanchat\Durable\Attribute\AsActivityHandler; +use Gplanchat\Durable\Attribute\AsWorkflow; use Gplanchat\Durable\Attribute\AsNexusServiceHandler; use Gplanchat\Durable\Attribute\FulfilsNexusOperation; use Gplanchat\Durable\Bundle\DependencyInjection\Compiler\ActivityHandlerPass; @@ -28,6 +29,16 @@ static function (ChildDefinition $definition, AsActivityHandler $attribute, \Ref }, ); + // Le quatrième, et il manquait. `WorkflowDefinitionLoader` lit déjà `#[AsWorkflow]` pour + // nommer le type ; c'est ici que la classe devient trouvable par le registre, sans balise + // écrite à la main dans le `services.yaml` de l'application. + $container->registerAttributeForAutoconfiguration( + AsWorkflow::class, + static function (ChildDefinition $definition, AsWorkflow $_attribute, \Reflector $_reflector): void { + $definition->addTag('durable.workflow'); + }, + ); + $container->registerAttributeForAutoconfiguration( AsNexusServiceHandler::class, static function (ChildDefinition $definition, AsNexusServiceHandler $attribute, \Reflector $_reflector): void { diff --git a/tests/unit/DurableBundle/DependencyInjection/AsWorkflowAutoconfigurationTest.php b/tests/unit/DurableBundle/DependencyInjection/AsWorkflowAutoconfigurationTest.php new file mode 100644 index 00000000..2735e9c8 --- /dev/null +++ b/tests/unit/DurableBundle/DependencyInjection/AsWorkflowAutoconfigurationTest.php @@ -0,0 +1,98 @@ +compileWith([WorkflowSansRien::class]); + + self::assertContains( + WorkflowSansRien::class, + $this->registeredClasses($container), + "l'attribut doit suffire, comme il suffit déjà pour les trois autres", + ); + } + + /** + * Le cœur de la question. Si le piège de #255 mordait, cet appel lèverait à la compilation. + */ + public function testUnWorkflowQuiRecoitLEnvironnementCompileQuandMeme(): void + { + $container = $this->compileWith([WorkflowAvecEnvironnement::class]); + + self::assertContains(WorkflowAvecEnvironnement::class, $this->registeredClasses($container)); + } + + public function testUneClasseSansAttributNeRejointPasLeRegistre(): void + { + $container = $this->compileWith([PasUnWorkflow::class]); + + self::assertNotContains(PasUnWorkflow::class, $this->registeredClasses($container)); + } + + /** + * @param list $classes + */ + private function compileWith(array $classes): ContainerBuilder + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + (new DurableExtension())->load([[]], $container); + + // Ce que FrameworkExtension pose et que ce conteneur synthétique n'a pas : le bus par + // défaut, référencé par le dispatcher de reprise. + $container->register('messenger.default_bus', \stdClass::class)->setPublic(true); + + foreach ($classes as $class) { + $container->register($class, $class) + ->setAutoconfigured(true) + ->setAutowired(true) + ->setPublic(false) + ; + } + + (new DurableBundle())->build($container); + $container->compile(); + + return $container; + } + + /** + * @return list + */ + private function registeredClasses(ContainerBuilder $container): array + { + $registered = []; + foreach ($container->getDefinition(WorkflowRegistry::class)->getMethodCalls() as [$method, $arguments]) { + if ('registerClass' === $method) { + $registered[] = (string) $arguments[0]; + } + } + + return $registered; + } +} diff --git a/tests/unit/DurableBundle/Fixtures/PasUnWorkflow.php b/tests/unit/DurableBundle/Fixtures/PasUnWorkflow.php new file mode 100644 index 00000000..103fa976 --- /dev/null +++ b/tests/unit/DurableBundle/Fixtures/PasUnWorkflow.php @@ -0,0 +1,16 @@ +