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 8557685

Browse filesBrowse files
[Messenger] Add --class-filter option to the messenger:failed:remove command
1 parent 6c0058a commit 8557685
Copy full SHA for 8557685

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('There is %d messages to remove. Do you want to continue?', $idsCount))) {
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.