Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1924719

Browse filesBrowse files
arnaud-deabreufabpot
authored andcommitted
[Messenger] Add --class-filter option to the messenger:failed:remove command
1 parent 6ef1ab4 commit 1924719
Copy full SHA for 1924719

File tree

3 files changed

+87
-4
lines changed
Filter options

3 files changed

+87
-4
lines changed

‎src/Symfony/Component/Messenger/CHANGELOG.md

Copy file name to clipboardExpand all lines: 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

Copy file name to clipboardExpand all lines: 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

Copy file name to clipboardExpand all lines: 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.