feature #27230 [Messenger] Select alternatives on missing receiver arg or typo (yceruto)
This PR was merged into the 4.1 branch.
Discussion
----------
[Messenger] Select alternatives on missing receiver arg or typo
| Q | A
| ------------- | ---
| Branch? | 4.1
| Bug fix? | no
| New feature? | yes
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | -
| License | MIT
| Doc PR | -
If there is more than one receiver available - `bin/console messenger:consume-messages`:
![messenger_consume_missing](https://user-images.githubusercontent.com/2028198/39895594-d4f652c6-5478-11e8-87cf-a1e08e681320.png)
If typo and there is similarities - `bin/console messenger:consume-messages amq`:
![messenger_consume_typo](https://user-images.githubusercontent.com/2028198/39895599-d9f0705e-5478-11e8-9d6b-59d62930d4cb.png)
requires https://github.com/symfony/symfony/pull/27224
Commits
-------
54c2541d94
Select alternatives on missing receiver arg or typo
This commit is contained in:
commit
160c97e57e
@ -19,6 +19,7 @@ use Symfony\Component\Console\Input\InputArgument;
|
||||
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 @@ class ConsumeMessagesCommand extends Command
|
||||
*/
|
||||
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 @@ EOF
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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 @@ EOF
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -193,9 +193,8 @@ class MessengerPass implements CompilerPassInterface
|
||||
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));
|
||||
@ -210,8 +209,12 @@ class MessengerPass implements CompilerPassInterface
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -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());
|
||||
|
@ -166,24 +166,7 @@ class MessengerPassTest extends TestCase
|
||||
$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(
|
||||
@ -198,7 +181,7 @@ class MessengerPassTest extends TestCase
|
||||
|
||||
(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()
|
||||
|
Reference in New Issue
Block a user