diff --git a/src/Symfony/Component/Messenger/CHANGELOG.md b/src/Symfony/Component/Messenger/CHANGELOG.md index 72d056f367c9e..1625551bfdbda 100644 --- a/src/Symfony/Component/Messenger/CHANGELOG.md +++ b/src/Symfony/Component/Messenger/CHANGELOG.md @@ -7,6 +7,7 @@ CHANGELOG * Deprecate `StopWorkerOnSignalsListener` in favor of using the `SignalableCommandInterface` * Add `HandlerDescriptor::getOptions` * Add support for multiple Redis Sentinel hosts + * Add `--all` option to the `messenger:failed:remove` command 6.3 --- diff --git a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php index 4c708fbc4f225..09d8e898b8988 100644 --- a/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php +++ b/src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php @@ -20,7 +20,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; -use Symfony\Component\Messenger\Transport\Receiver\ReceiverInterface; +use Symfony\Component\Messenger\Transport\Receiver\MessageCountAwareInterface; /** * @author Ryan Weaver @@ -32,7 +32,8 @@ protected function configure(): void { $this ->setDefinition([ - new InputArgument('id', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Specific message id(s) to remove'), + new InputArgument('id', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Specific message id(s) to remove'), + new InputOption('all', null, InputOption::VALUE_NONE, 'Remove all failed messages from the transport'), new InputOption('force', null, InputOption::VALUE_NONE, 'Force the operation without confirmation'), new InputOption('transport', null, InputOption::VALUE_OPTIONAL, 'Use a specific failure transport', self::DEFAULT_TRANSPORT_OPTION), new InputOption('show-messages', null, InputOption::VALUE_NONE, 'Display messages before removing it (if multiple ids are given)'), @@ -43,6 +44,10 @@ protected function configure(): void php %command.full_name% {id1} [{id2} ...] The specific ids can be found via the messenger:failed:show command. + +You can remove all failed messages from the failure transport by using the "--all" option: + + php %command.full_name% --all EOF ) ; @@ -61,18 +66,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int $shouldForce = $input->getOption('force'); $ids = (array) $input->getArgument('id'); - $shouldDisplayMessages = $input->getOption('show-messages') || 1 === \count($ids); - $this->removeMessages($failureTransportName, $ids, $receiver, $io, $shouldForce, $shouldDisplayMessages); + $shouldDeleteAllMessages = $input->getOption('all'); - return 0; - } + $idsCount = \count($ids); + if (!$shouldDeleteAllMessages && !$idsCount) { + throw new RuntimeException('Please specify at least one message id. If you want to remove all failed messages, use the "--all" option.'); + } elseif ($shouldDeleteAllMessages && $idsCount) { + throw new RuntimeException('You cannot specify message ids when using the "--all" option.'); + } + + $shouldDisplayMessages = $input->getOption('show-messages') || 1 === $idsCount; - private function removeMessages(string $failureTransportName, array $ids, ReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void - { if (!$receiver instanceof ListableReceiverInterface) { throw new RuntimeException(sprintf('The "%s" receiver does not support removing specific messages.', $failureTransportName)); } + if ($shouldDeleteAllMessages) { + $this->removeAllMessages($receiver, $io, $shouldForce, $shouldDisplayMessages); + } else { + $this->removeMessagesById($ids, $receiver, $io, $shouldForce, $shouldDisplayMessages); + } + + return 0; + } + + private function removeMessagesById(array $ids, ListableReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void + { foreach ($ids as $id) { $this->phpSerializer?->acceptPhpIncompleteClass(); try { @@ -99,4 +118,31 @@ private function removeMessages(string $failureTransportName, array $ids, Receiv } } } + + private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void + { + if (!$shouldForce) { + if ($receiver instanceof MessageCountAwareInterface) { + $question = sprintf('Do you want to permanently remove all (%d) messages?', $receiver->getMessageCount()); + } else { + $question = 'Do you want to permanently remove all failed messages?'; + } + + if (!$io->confirm($question, false)) { + return; + } + } + + $count = 0; + foreach ($receiver->all() as $envelope) { + if ($shouldDisplayMessages) { + $this->displaySingleMessage($envelope, $io); + } + + $receiver->reject($envelope); + ++$count; + } + + $io->note(sprintf('%d messages were removed.', $count)); + } } diff --git a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php index 2aa1738422872..bb8365d351637 100644 --- a/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php +++ b/src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php @@ -12,9 +12,11 @@ namespace Symfony\Component\Messenger\Tests\Command; use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Tester\CommandCompletionTester; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\ServiceLocator; +use Symfony\Component\Messenger\Bridge\Doctrine\Transport\DoctrineReceiver; use Symfony\Component\Messenger\Command\FailedMessagesRemoveCommand; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Exception\InvalidArgumentException; @@ -207,7 +209,7 @@ public function testCompleteId() $globalFailureReceiverName = 'failure_receiver'; $receiver = $this->createMock(ListableReceiverInterface::class); - $receiver->expects($this->once())->method('all')->with(50)->willReturn([ + $receiver->expects($this->once())->method('all')->willReturn([ Envelope::wrap(new \stdClass(), [new TransportMessageIdStamp('2ab50dfa1fbf')]), Envelope::wrap(new \stdClass(), [new TransportMessageIdStamp('78c2da843723')]), ]); @@ -233,7 +235,7 @@ public function testCompleteIdWithSpecifiedTransport() $anotherFailureReceiverName = 'another_receiver'; $receiver = $this->createMock(ListableReceiverInterface::class); - $receiver->expects($this->once())->method('all')->with(50)->willReturn([ + $receiver->expects($this->once())->method('all')->willReturn([ Envelope::wrap(new \stdClass(), [new TransportMessageIdStamp('2ab50dfa1fbf')]), Envelope::wrap(new \stdClass(), [new TransportMessageIdStamp('78c2da843723')]), ]); @@ -253,4 +255,100 @@ public function testCompleteIdWithSpecifiedTransport() $this->assertSame(['2ab50dfa1fbf', '78c2da843723'], $suggestions); } + + public function testOptionAllIsSetWithIdsThrows() + { + $globalFailureReceiverName = 'failure_receiver'; + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true); + $serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($this->createMock(ListableReceiverInterface::class)); + + $command = new FailedMessagesRemoveCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('You cannot specify message ids when using the "--all" option.'); + $tester->execute(['id' => [20], '--all' => true]); + } + + public function testOptionAllIsSetWithoutForceAsksConfirmation() + { + $globalFailureReceiverName = 'failure_receiver'; + + $receiver = $this->createMock(ListableReceiverInterface::class); + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true); + $serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + + $tester->execute(['--all' => true]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertStringContainsString('Do you want to permanently remove all failed messages? (yes/no)', $tester->getDisplay()); + } + + public function testOptionAllIsSetWithoutForceAsksConfirmationOnMessageCountAwareInterface() + { + $globalFailureReceiverName = 'failure_receiver'; + + $receiver = $this->createMock(DoctrineReceiver::class); + $receiver->expects($this->once())->method('getMessageCount')->willReturn(2); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true); + $serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + + $tester->execute(['--all' => true]); + + $this->assertSame(0, $tester->getStatusCode()); + $this->assertStringContainsString('Do you want to permanently remove all (2) messages? (yes/no)', $tester->getDisplay()); + } + + public function testOptionAllIsNotSetNorIdsThrows() + { + $globalFailureReceiverName = 'failure_receiver'; + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true); + $serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($this->createMock(ListableReceiverInterface::class)); + + $command = new FailedMessagesRemoveCommand('failure_receiver', $serviceLocator); + $tester = new CommandTester($command); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Please specify at least one message id. If you want to remove all failed messages, use the "--all" option.'); + $tester->execute([]); + } + + public function testRemoveAllMessages() + { + $globalFailureReceiverName = 'failure_receiver'; + $receiver = $this->createMock(ListableReceiverInterface::class); + + $series = [ + new Envelope(new \stdClass()), + new Envelope(new \stdClass()), + new Envelope(new \stdClass()), + new Envelope(new \stdClass()), + ]; + + $receiver->expects($this->once())->method('all')->willReturn($series); + + $serviceLocator = $this->createMock(ServiceLocator::class); + $serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true); + $serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($receiver); + + $command = new FailedMessagesRemoveCommand($globalFailureReceiverName, $serviceLocator); + $tester = new CommandTester($command); + $tester->execute(['--all' => true, '--force' => true, '--show-messages' => true]); + + $this->assertStringContainsString('Failed Message Details', $tester->getDisplay()); + $this->assertStringContainsString('4 messages were removed.', $tester->getDisplay()); + } } 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