From aaa5c7ce9701fe68b24590feeacda2f7550d23f1 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Sat, 28 Jan 2023 11:43:27 +0100 Subject: [PATCH] [FrameworkBundle][Messenger] Add `AsRoutedMessage` attribute --- .../Compiler/UnusedTagsPass.php | 1 + .../FrameworkExtension.php | 9 +++ .../Messenger/Attribute/AsRoutedMessage.php | 33 ++++++++++ src/Symfony/Component/Messenger/CHANGELOG.md | 1 + .../DependencyInjection/MessengerPass.php | 30 +++++++++ .../DependencyInjection/MessengerPassTest.php | 63 +++++++++++++++++++ 6 files changed, 137 insertions(+) create mode 100644 src/Symfony/Component/Messenger/Attribute/AsRoutedMessage.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php index 169a1c0a6909a..d15ca71c76f48 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php @@ -65,6 +65,7 @@ class UnusedTagsPass implements CompilerPassInterface 'messenger.bus', 'messenger.message_handler', 'messenger.receiver', + 'messenger.routed_message', 'messenger.transport_factory', 'mime.mime_type_guesser', 'monolog.logger', diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 7292ed0f79f63..6ad0eeb78df54 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -110,6 +110,7 @@ use Symfony\Component\Mailer\Mailer; use Symfony\Component\Mercure\HubRegistry; use Symfony\Component\Messenger\Attribute\AsMessageHandler; +use Symfony\Component\Messenger\Attribute\AsRoutedMessage; use Symfony\Component\Messenger\Bridge\AmazonSqs\Transport\AmazonSqsTransportFactory; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory; use Symfony\Component\Messenger\Bridge\Beanstalkd\Transport\BeanstalkdTransportFactory; @@ -687,6 +688,14 @@ public function load(array $configs, ContainerBuilder $container) } $definition->addTag('messenger.message_handler', $tagAttributes); }); + $container->registerAttributeForAutoconfiguration(AsRoutedMessage::class, static function (ChildDefinition $definition, AsRoutedMessage $attribute, \ReflectionClass $reflector): void { + $tagAttributes = [ + 'class' => $reflector->getName(), + 'transports' => $attribute->getTransports(), + ]; + + $definition->addTag('messenger.routed_message', $tagAttributes); + }); if (!$container->getParameter('kernel.debug')) { // remove tagged iterator argument for resource checkers diff --git a/src/Symfony/Component/Messenger/Attribute/AsRoutedMessage.php b/src/Symfony/Component/Messenger/Attribute/AsRoutedMessage.php new file mode 100644 index 0000000000000..7520c2b15ae64 --- /dev/null +++ b/src/Symfony/Component/Messenger/Attribute/AsRoutedMessage.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Attribute; + +/** + * Attribute to configure transports to be used to dispatch a message. + * + * @author Alexandre Daubois + */ +#[\Attribute(\Attribute::TARGET_CLASS)] +class AsRoutedMessage +{ + private array $transports; + + public function __construct(array|string $transports) + { + $this->transports = (array) $transports; + } + + public function getTransports(): array + { + return $this->transports; + } +} diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index b60f066320d64..067e84ea900a1 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -10,6 +10,7 @@ CHANGELOG `Symfony\Component\Messenger\Transport\InMemory\InMemoryTransport` and `Symfony\Component\Messenger\Transport\InMemory\InMemoryTransportFactory` * Allow passing a string instead of an array in `TransportNamesStamp` + * Add `#[AsRoutedMessage]` attribute 6.2 --- diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index ac2b97fc3df85..19412caa2b5e6 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -56,6 +56,10 @@ public function process(ContainerBuilder $container) $this->registerReceivers($container, $busIds); } $this->registerHandlers($container, $busIds); + + if ($container->hasDefinition('messenger.senders_locator')) { + $this->registerRoutedMessages($container); + } } private function registerHandlers(ContainerBuilder $container, array $busIds): void @@ -405,4 +409,30 @@ private function getServiceClass(ContainerBuilder $container, string $serviceId) return $definition->getClass(); } } + + private function registerRoutedMessages(ContainerBuilder $container): void + { + $mapping = $container->getDefinition('messenger.senders_locator')->getArgument(0) ?? []; + $receiverAliases = array_map( + static fn ($tagAttributes) => $tagAttributes[0]['alias'], + $container->findTaggedServiceIds('messenger.receiver') + ); + + foreach ($container->findTaggedServiceIds('messenger.routed_message') as [$routedMessage]) { + $messageClass = $routedMessage['class']; + $mapping[$routedMessage['class']] ??= []; + + foreach ($routedMessage['transports'] as $transport) { + if (!\in_array($transport, $receiverAliases)) { + throw new RuntimeException(sprintf('Invalid Messenger routing configuration: the "%s" class is being routed to a sender called "%s". This is not a valid transport or service id.', $messageClass, $transport)); + } + + $mapping[$messageClass][] = $transport; + } + } + + $container->getDefinition('messenger.senders_locator') + ->setArgument(0, $mapping) + ; + } } diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index a72efcc15e29e..a93bbea98142a 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -25,6 +25,7 @@ use Symfony\Component\DependencyInjection\ServiceLocator; use Symfony\Component\Messenger\Attribute\AsMessageHandler; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpReceiver; +use Symfony\Component\Messenger\Bridge\Redis\Transport\RedisReceiver; use Symfony\Component\Messenger\Command\ConsumeMessagesCommand; use Symfony\Component\Messenger\Command\DebugCommand; use Symfony\Component\Messenger\Command\FailedMessagesRetryCommand; @@ -918,6 +919,68 @@ public function testFailedCommandsRegisteredWithServiceLocatorArgumentReplaced() $removeDefinition = $container->getDefinition('console.command.messenger_failed_messages_remove'); $this->assertNotNull($removeDefinition->getArgument(1)); } + + public function testRegisterRoutedMessageWithEmptySenderLocator() + { + $container = $this->getContainerBuilder(); + $container->register('messenger.senders_locator', ServiceLocator::class)->setArgument(0, []); + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', ['alias' => 'amqp']); + + $container->register(DummyMessage::class) + ->addTag('messenger.routed_message', [ + 'class' => DummyMessage::class, + 'transports' => ['amqp'], + ]) + ; + + (new MessengerPass())->process($container); + $mapping = $container->getDefinition('messenger.senders_locator')->getArgument(0); + + $this->assertArrayHasKey(DummyMessage::class, $mapping); + $this->assertSame($mapping[DummyMessage::class][0], 'amqp'); + } + + public function testRegisterRoutedMessageWithInvalidTransport() + { + $container = $this->getContainerBuilder(); + $container->register('messenger.senders_locator', ServiceLocator::class)->setArgument(0, []); + + $container->register(DummyMessage::class) + ->addTag('messenger.routed_message', [ + 'class' => DummyMessage::class, + 'transports' => ['amqp'], + ]) + ; + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Invalid Messenger routing configuration: the "Symfony\Component\Messenger\Tests\Fixtures\DummyMessage" class is being routed to a sender called "amqp". This is not a valid transport or service id.'); + (new MessengerPass())->process($container); + } + + public function testRegisterRoutedMessageWithExistingSenders() + { + $container = $this->getContainerBuilder(); + $container->register('messenger.senders_locator', ServiceLocator::class)->setArgument(0, [ + DummyMessage::class => ['amqp'], + ]); + + $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', ['alias' => 'amqp']); + $container->register(RedisReceiver::class, RedisReceiver::class)->addTag('messenger.receiver', ['alias' => 'redis']); + + $container->register(DummyMessage::class) + ->addTag('messenger.routed_message', [ + 'class' => DummyMessage::class, + 'transports' => ['redis'], + ]) + ; + + (new MessengerPass())->process($container); + $mapping = $container->getDefinition('messenger.senders_locator')->getArgument(0); + + $this->assertArrayHasKey(DummyMessage::class, $mapping); + $this->assertSame($mapping[DummyMessage::class][0], 'amqp'); + $this->assertSame($mapping[DummyMessage::class][1], 'redis'); + } } class DummyHandler pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy