10000 feature #59978 [Messenger] Add `--class-filter` option to the `messen… · symfony/symfony@322995c · GitHub
[go: up one dir, main page]

Skip to content

Commit 322995c

Browse files
committed
feature #59978 [Messenger] Add --class-filter option to the messenger:failed:remove command (arnaud-deabreu)
This PR was squashed before being merged into the 7.3 branch. Discussion ---------- [Messenger] Add `--class-filter` option to the `messenger:failed:remove` command | Q | A | ------------- | --- | Branch? | 7.3 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | - | License | MIT This PR adds the `--class-filter` to the `messenger:failed:remove` command which is already available on `messenger:failed:show` command. It will yield the message ids that match the given class and use it as if it were passed as the `id` argument in the command so the `--force` and `--show-messages` can work with this filter. When using the filter, the command will prompt for confirmation before removing the messages. Example output: ``` $ bin/console messenger:failed:remove --class-filter="App\Message\MyMessage" There is 16 messages to remove. Do you want to continue? (yes/no) [yes]: ``` <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too). - Features and deprecations must be submitted against the latest branch. - For new features, provide some code snippets to help understand usage. - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Commits ------- 1924719 [Messenger] Add `--class-filter` option to the `messenger:failed:remove` command
2 parents cf5b2f0 + 1924719 commit 322995c

File tree

3 files changed

+87
-4
lines changed

3 files changed

+87
-4
lines changed

src/Symfony/Component/Messenger/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add `CloseableTransportInterface` to allow closing the transport
88
* Add `SentForRetryStamp` that identifies whether a failed message was sent for retry
99
* Add `Symfony\Component\Messenger\Middleware\DeduplicateMiddleware` and `Symfony\Component\Messenger\Stamp\DeduplicateStamp`
10+
* Add `--class-filter` option to the `messenger:failed:remove` command
1011

1112
7.2
1213
---

src/Symfony/Component/Messenger/Command/FailedMessagesRemoveCommand.php

+39-4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ protected function configure(): void
3737
new InputOption('force', null, InputOption::VALUE_NONE, 'Force the operation without confirmation'),
3838
new InputOption('transport', null, InputOption::VALUE_OPTIONAL, 'Use a specific failure transport', self::DEFAULT_TRANSPORT_OPTION),
3939
new InputOption('show-messages', null, InputOption::VALUE_NONE, 'Display messages before removing it (if multiple ids are given)'),
40+
new InputOption('class-filter', null, InputOption::VALUE_REQUIRED, 'Filter by a specific class name'),
4041
])
4142
->setHelp(<<<'EOF'
4243
The <info>%command.name%</info> removes given messages that are pending in the failure transport.
@@ -69,6 +70,24 @@ protected function execute(InputInterface $input, OutputInterface $output): int
6970
$shouldDeleteAllMessages = $input->getOption('all');
7071

7172
$idsCount = \count($ids);
73+
74+
if (!$receiver instanceof ListableReceiverInterface) {
75+
throw new RuntimeException(\sprintf('The "%s" receiver does not support removing specific messages.', $failureTransportName));
76+
}
77+
78+
if (!$idsCount && null !== $input->getOption('class-filter')) {
79+
$ids = $this->getMessageIdsByClassFilter($input->getOption('class-filter'), $receiver);
80+
$idsCount = \count($ids);
81+
82+
if (!$idsCount) {
83+
throw new RuntimeException('No failed messages were found with this filter.');
84+
}
85+
86+
if (!$io->confirm(\sprintf('Can you confirm you want to remove %d message%s?', $idsCount, 1 === $idsCount ? '' : 's'))) {
87+
return 0;
88+
}
89+
}
90+
7291
if (!$shouldDeleteAllMessages && !$idsCount) {
7392
throw new RuntimeException('Please specify at least one message id. If you want to remove all failed messages, use the "--all" option.');
7493
} elseif ($shouldDeleteAllMessages && $idsCount) {
@@ -77,10 +96,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
7796

7897
$shouldDisplayMessages = $input->getOption('show-messages') || 1 === $idsCount;
7998

80-
if (!$receiver instanceof ListableReceiverInterface) {
81-
throw new RuntimeException(\sprintf('The "%s" receiver does not support removing specific messages.', $failureTransportName));
82-
}
83-
8499
if ($shouldDeleteAllMessages) {
85100
$this->removeAllMessages($receiver, $io, $shouldForce, $shouldDisplayMessages);
86101
} else {
@@ -119,6 +134,26 @@ private function removeMessagesById(array $ids, ListableReceiverInterface $recei
119134
}
120135
}
121136

137+
private function getMessageIdsByClassFilter(string $classFilter, ListableReceiverInterface $receiver): array
138+
{
139+
$ids = [];
140+
141+
$this->phpSerializer?->acceptPhpIncompleteClass();
142+
try {
143+
foreach ($receiver->all() as $envelope) {
144+
if ($classFilter !== $envelope->getMessage()::class) {
145+
continue;
146+
}
147+
148+
$ids[] = $this->getMessageId($envelope);
149+
};
150+
} finally {
151+
$this->phpSerializer?->rejectPhpIncompleteClass();
152+
}
153+
154+
return $ids;
155+
}
156+
122157
private function removeAllMessages(ListableReceiverInterface $receiver, SymfonyStyle $io, bool $shouldForce, bool $shouldDisplayMessages): void
123158
{
124159
if (!$shouldForce) {

src/Symfony/Component/Messenger/Tests/Command/FailedMessagesRemoveCommandTest.php

+47
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,53 @@ public function testRemoveMultipleMessagesAndDisplayMessagesWithServiceLocator()
182182
$this->assertStringContainsString('Message with id 30 removed.', $tester->getDisplay());
183183
}
184184

185+
public function testRemoveMessagesFilteredByClassMessage()
186+
{
187+
$globalFailureReceiverName = 'failure_receiver';
188+
$receiver = $this->createMock(ListableReceiverInterface::class);
189+
190+
$anotherClass = new class extends \stdClass {};
191+
192+
$series = [
193+
new Envelope(new \stdClass(), [new TransportMessageIdStamp(10)]),
194+
new Envelope(new $anotherClass(), [new TransportMessageIdStamp(20)]),
195+
new Envelope(new \stdClass(), [new TransportMessageIdStamp(30)]),
196+
];
197+
$receiver->expects($this->once())->method('all')->willReturn($series);
198+
199+
$expectedRemovedIds = [10, 30];
200+
$receiver->expects($this->exactly(2))->method('find')
201+
->willReturnCallback(function (...$args) use ($series, &$expectedRemovedIds) {
202+
$expectedArgs = array_shift($expectedRemovedIds);
203+
$this->assertSame([$expectedArgs], $args);
204+
205+
$return = array_filter(
206+
$series,
207+
static fn (Envelope $envelope) => [$envelope->last(TransportMessageIdStamp::class)->getId()] === $args,
208+
);
209+
210+
return current($return);
211+
})
212+
;
213+
214+
$serviceLocator = $this->createMock(ServiceLocator::class);
215+
$serviceLocator->expects($this->once())->method('has')->with($globalFailureReceiverName)->willReturn(true);
216+
$serviceLocator->expects($this->any())->method('get')->with($globalFailureReceiverName)->willReturn($receiver);
217+
218+
$command = new FailedMessagesRemoveCommand(
219+
$globalFailureReceiverName,
220+
$serviceLocator
221+
);
222+
223+
$tester = new CommandTester($command);
224+
$tester->execute(['--class-filter' => "stdClass", '--force' => true, '--show-messages' => true]);
225+
226+
$this->assertStringContainsString('There is 2 messages to remove. Do you want to continue? (yes/no)', $tester->getDisplay());
227+
$this->assertStringContainsString('Failed Message Details', $tester->getDisplay());
228+
$this->assertStringContainsString('Message with id 10 removed.', $tester->getDisplay());
229+
$this->assertStringContainsString('Message with id 30 removed.', $tester->getDisplay());
230+
}
231+
185232
public function testCompletingTransport()
186233
{
187234
$globalFailureReceiverName = 'failure_receiver';

0 commit comments

Comments
 (0)
0