From 54c2541d94b38e4af9ca167e5864515cf3d16614 Mon Sep 17 00:00:00 2001 From: Yonel Ceruto Date: Thu, 10 May 2018 16:58:59 -0400 Subject: [PATCH] Select alternatives on missing receiver arg or typo --- .../Command/ConsumeMessagesCommand.php | 49 +++++++++++++++++-- .../DependencyInjection/MessengerPass.php | 11 +++-- .../Command/ConsumeMessagesCommandTest.php | 6 +-- .../DependencyInjection/MessengerPassTest.php | 21 +------- 4 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php index e64d10f1953e7..06b48a50c5ac3 100644 --- a/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php +++ b/src/Symfony/Component/Messenger/Command/ConsumeMessagesCommand.php @@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMemoryUsageIsExceededReceiver; use Symfony\Component\Messenger\Transport\Enhancers\StopWhenMessageCountIsExceededReceiver; @@ -37,14 +38,14 @@ class ConsumeMessagesCommand extends Command private $bus; private $receiverLocator; private $logger; - private $defaultReceiverName; + private $receiverNames; - public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, string $defaultReceiverName = null) + public function __construct(MessageBusInterface $bus, ContainerInterface $receiverLocator, LoggerInterface $logger = null, array $receiverNames = array()) { $this->bus = $bus; $this->receiverLocator = $receiverLocator; $this->logger = $logger; - $this->defaultReceiverName = $defaultReceiverName; + $this->receiverNames = $receiverNames; parent::__construct(); } @@ -54,9 +55,11 @@ public function __construct(MessageBusInterface $bus, ContainerInterface $receiv */ protected function configure(): void { + $defaultReceiverName = 1 === \count($this->receiverNames) ? current($this->receiverNames) : null; + $this ->setDefinition(array( - new InputArgument('receiver', $this->defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $this->defaultReceiverName), + new InputArgument('receiver', $defaultReceiverName ? InputArgument::OPTIONAL : InputArgument::REQUIRED, 'Name of the receiver', $defaultReceiverName), new InputOption('limit', 'l', InputOption::VALUE_REQUIRED, 'Limit the number of received messages'), new InputOption('memory-limit', 'm', InputOption::VALUE_REQUIRED, 'The memory limit the worker can consume'), new InputOption('time-limit', 't', InputOption::VALUE_REQUIRED, 'The time limit in seconds the worker can run'), @@ -83,6 +86,27 @@ protected function configure(): void ; } + /** + * {@inheritdoc} + */ + protected function interact(InputInterface $input, OutputInterface $output) + { + if (!$this->receiverNames || $this->receiverLocator->has($receiverName = $input->getArgument('receiver'))) { + return; + } + + $style = new SymfonyStyle($input, $output); + if (null === $receiverName) { + $style->block('Missing receiver argument.', null, 'error', ' ', true); + $input->setArgument('receiver', $style->choice('Select one of the available receivers', $this->receiverNames)); + } elseif ($alternatives = $this->findAlternatives($receiverName, $this->receiverNames)) { + $style->block(sprintf('Receiver "%s" is not defined.', $receiverName), null, 'error', ' ', true); + if ($style->confirm(sprintf('Do you want to receive from "%s" instead? ', $alternatives[0]), false)) { + $input->setArgument('receiver', $alternatives[0]); + } + } + } + /** * {@inheritdoc} */ @@ -134,4 +158,21 @@ private function convertToBytes(string $memoryLimit): int return $max; } + + private function findAlternatives($name, array $collection) + { + $alternatives = array(); + foreach ($collection as $item) { + $lev = levenshtein($name, $item); + if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) { + $alternatives[$item] = isset($alternatives[$item]) ? $alternatives[$item] - $lev : $lev; + } + } + + $threshold = 1e3; + $alternatives = array_filter($alternatives, function ($lev) use ($threshold) { return $lev < 2 * $threshold; }); + ksort($alternatives, SORT_NATURAL | SORT_FLAG_CASE); + + return array_keys($alternatives); + } } diff --git a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php index 81f84617064cc..88e468770c001 100644 --- a/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php +++ b/src/Symfony/Component/Messenger/DependencyInjection/MessengerPass.php @@ -166,9 +166,8 @@ private function guessHandledClasses(\ReflectionClass $handlerClass, string $ser private function registerReceivers(ContainerBuilder $container) { $receiverMapping = array(); - $taggedReceivers = $container->findTaggedServiceIds($this->receiverTag); - foreach ($taggedReceivers as $id => $tags) { + foreach ($container->findTaggedServiceIds($this->receiverTag) as $id => $tags) { $receiverClass = $container->findDefinition($id)->getClass(); if (!is_subclass_of($receiverClass, ReceiverInterface::class)) { throw new RuntimeException(sprintf('Invalid receiver "%s": class "%s" must implement interface "%s".', $id, $receiverClass, ReceiverInterface::class)); @@ -183,8 +182,12 @@ private function registerReceivers(ContainerBuilder $container) } } - if (1 === \count($taggedReceivers) && $container->hasDefinition('console.command.messenger_consume_messages')) { - $container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(3, (string) current($receiverMapping)); + if ($container->hasDefinition('console.command.messenger_consume_messages')) { + $receiverNames = array(); + foreach ($receiverMapping as $name => $reference) { + $receiverNames[(string) $reference] = $name; + } + $container->getDefinition('console.command.messenger_consume_messages')->replaceArgument(3, array_values($receiverNames)); } $container->getDefinition('messenger.receiver_locator')->replaceArgument(0, $receiverMapping); diff --git a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php index 4eca2423aa47c..de489b545f0e4 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/ConsumeMessagesCommandTest.php @@ -20,15 +20,15 @@ class ConsumeMessagesCommandTest extends TestCase { public function testConfigurationWithDefaultReceiver() { - $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, 'messenger.transport.amqp'); + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, array('amqp')); $inputArgument = $command->getDefinition()->getArgument('receiver'); $this->assertFalse($inputArgument->isRequired()); - $this->assertSame('messenger.transport.amqp', $inputArgument->getDefault()); + $this->assertSame('amqp', $inputArgument->getDefault()); } public function testConfigurationWithoutDefaultReceiver() { - $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class)); + $command = new ConsumeMessagesCommand($this->createMock(MessageBus::class), $this->createMock(ServiceLocator::class), null, array('amqp', 'dummy')); $inputArgument = $command->getDefinition()->getArgument('receiver'); $this->assertTrue($inputArgument->isRequired()); $this->assertNull($inputArgument->getDefault()); diff --git a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php index d49a38421b1d3..5e35b18590ca5 100644 --- a/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php +++ b/src/Symfony/Component/Messenger/Tests/DependencyInjection/MessengerPassTest.php @@ -118,24 +118,7 @@ public function testItRegistersReceiversWithoutTagName() $this->assertEquals(array(AmqpReceiver::class => new Reference(AmqpReceiver::class)), $container->getDefinition('messenger.receiver_locator')->getArgument(0)); } - public function testItRegistersOneReceiverAndSetsTheDefaultOneOnTheCommand() - { - $container = $this->getContainerBuilder(); - $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( - new Reference('message_bus'), - new Reference('messenger.receiver_locator'), - null, - null, - )); - - $container->register(AmqpReceiver::class, AmqpReceiver::class)->addTag('messenger.receiver', array('alias' => 'amqp')); - - (new MessengerPass())->process($container); - - $this->assertSame(AmqpReceiver::class, $container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); - } - - public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheCommand() + public function testItRegistersMultipleReceiversAndSetsTheReceiverNamesOnTheCommand() { $container = $this->getContainerBuilder(); $container->register('console.command.messenger_consume_messages', ConsumeMessagesCommand::class)->setArguments(array( @@ -150,7 +133,7 @@ public function testItRegistersMultipleReceiversAndDoesNotSetTheDefaultOneOnTheC (new MessengerPass())->process($container); - $this->assertNull($container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); + $this->assertSame(array('amqp', 'dummy'), $container->getDefinition('console.command.messenger_consume_messages')->getArgument(3)); } public function testItRegistersSenders()