From 94bffd23ff04878b89ac6cf75611386f2bae9203 Mon Sep 17 00:00:00 2001 From: Adrian Dumitrache Date: Fri, 3 May 2024 12:03:43 +0300 Subject: [PATCH] Retry failed messages from admin --- .../Integration/MessengerIntegrationTest.php | 7 ++++ .../Message/RetryFailedMessageMessage.php | 34 ++++++++++++++++ .../RetryFailedMessageMessageHandler.php | 40 +++++++++++++++++++ .../DependencyInjection/Configuration.php | 3 ++ .../DrawSonataIntegrationExtension.php | 2 + .../Messenger/Admin/MessengerMessageAdmin.php | 27 ++++++++++++- .../Controller/MessageController.php | 38 ++++++++++++++++++ .../translations/DrawMessengerAdmin.en.yaml | 2 + .../Message/list__action_retry.html.twig | 8 ++++ .../Message/show__action_retry.html.twig | 8 ++++ .../DependencyInjection/ConfigurationTest.php | 5 ++- ...tegrationExtensionMessengerEnabledTest.php | 2 + 12 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 packages/messenger/Message/RetryFailedMessageMessage.php create mode 100644 packages/messenger/MessageHandler/RetryFailedMessageMessageHandler.php create mode 100644 packages/sonata-integration-bundle/Messenger/Controller/MessageController.php create mode 100644 packages/sonata-integration-bundle/Resources/translations/DrawMessengerAdmin.en.yaml create mode 100644 packages/sonata-integration-bundle/Resources/views/Messenger/Message/list__action_retry.html.twig create mode 100644 packages/sonata-integration-bundle/Resources/views/Messenger/Message/show__action_retry.html.twig diff --git a/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/MessengerIntegrationTest.php b/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/MessengerIntegrationTest.php index 50d800aa0..b54372554 100644 --- a/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/MessengerIntegrationTest.php +++ b/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/MessengerIntegrationTest.php @@ -26,6 +26,7 @@ use Draw\Component\Messenger\Message\AsyncHighPriorityMessageInterface; use Draw\Component\Messenger\Message\AsyncLowPriorityMessageInterface; use Draw\Component\Messenger\Message\AsyncMessageInterface; +use Draw\Component\Messenger\MessageHandler\RetryFailedMessageMessageHandler; use Draw\Component\Messenger\Retry\EventDrivenRetryStrategy; use Draw\Component\Messenger\Searchable\EnvelopeFinder; use Draw\Component\Messenger\Searchable\TransportRepository; @@ -167,6 +168,12 @@ public static function provideTestLoad(): iterable TransportRepository::class, ] ), + new ServiceConfiguration( + 'draw.messenger.message_handler.retry_failed_message_message_handler', + [ + RetryFailedMessageMessageHandler::class, + ] + ), new ServiceConfiguration( 'draw.messenger.manual_trigger.action.click_message_action', [ClickMessageAction::class] diff --git a/packages/messenger/Message/RetryFailedMessageMessage.php b/packages/messenger/Message/RetryFailedMessageMessage.php new file mode 100644 index 000000000..6d703c45f --- /dev/null +++ b/packages/messenger/Message/RetryFailedMessageMessage.php @@ -0,0 +1,34 @@ +message = $message; + } + + public function getMessage(): MessengerMessage + { + if (null === $this->message) { + throw new UnrecoverableMessageHandlingException('Message is not set.'); + } + + return $this->message; + } + + public function getPropertiesWithDoctrineObject(): array + { + return ['message']; + } +} diff --git a/packages/messenger/MessageHandler/RetryFailedMessageMessageHandler.php b/packages/messenger/MessageHandler/RetryFailedMessageMessageHandler.php new file mode 100644 index 000000000..9a02fbedd --- /dev/null +++ b/packages/messenger/MessageHandler/RetryFailedMessageMessageHandler.php @@ -0,0 +1,40 @@ +kernel); + $application->setAutoExit(false); + + $application->run( + new ArrayInput( + [ + 'command' => 'messenger:failed:retry', + 'id' => [ + $message->getMessage()->getId(), + ], + '--force' => true, + ] + ), + new NullOutput() + ); + } +} diff --git a/packages/sonata-integration-bundle/DependencyInjection/Configuration.php b/packages/sonata-integration-bundle/DependencyInjection/Configuration.php index d9a99cc91..15a428621 100644 --- a/packages/sonata-integration-bundle/DependencyInjection/Configuration.php +++ b/packages/sonata-integration-bundle/DependencyInjection/Configuration.php @@ -8,6 +8,7 @@ use Draw\Bundle\SonataIntegrationBundle\Console\Controller\ExecutionController; use Draw\Bundle\SonataIntegrationBundle\CronJob\Controller\CronJobController; use Draw\Bundle\SonataIntegrationBundle\CronJob\Controller\CronJobExecutionController; +use Draw\Bundle\SonataIntegrationBundle\Messenger\Controller\MessageController; use Draw\Bundle\SonataIntegrationBundle\User\Extension\TwoFactorAuthenticationExtension; use Draw\Bundle\UserBundle\DrawUserBundle; use Draw\Bundle\UserBundle\Entity\UserLock; @@ -143,8 +144,10 @@ private function createMessengerNode(): ArrayNodeDefinition (new SonataAdminNodeConfiguration(MessengerMessage::class, 'Messenger', 'admin')) ->addDefaultsIfNotSet() ->pagerTypeDefaultValue('simple') + ->controllerClassDefaultValue(MessageController::class) ->iconDefaultValue('fas fa-rss') ->labelDefaultValue('Message') + ->translationDomainDefaultValue('DrawMessengerAdmin') ->canBeDisabled() ) ->end(); diff --git a/packages/sonata-integration-bundle/DependencyInjection/DrawSonataIntegrationExtension.php b/packages/sonata-integration-bundle/DependencyInjection/DrawSonataIntegrationExtension.php index b8865b21a..2ec574232 100644 --- a/packages/sonata-integration-bundle/DependencyInjection/DrawSonataIntegrationExtension.php +++ b/packages/sonata-integration-bundle/DependencyInjection/DrawSonataIntegrationExtension.php @@ -182,6 +182,8 @@ private function configureMessenger(array $config, Loader\FileLoader $loader, Co ->setAutowired(true) ->setAutoconfigured(true) ); + + $this->setControllerClassDefinition($config['admin'], $container); } } diff --git a/packages/sonata-integration-bundle/Messenger/Admin/MessengerMessageAdmin.php b/packages/sonata-integration-bundle/Messenger/Admin/MessengerMessageAdmin.php index b7afa4a47..f26577777 100644 --- a/packages/sonata-integration-bundle/Messenger/Admin/MessengerMessageAdmin.php +++ b/packages/sonata-integration-bundle/Messenger/Admin/MessengerMessageAdmin.php @@ -90,7 +90,20 @@ protected function configureListFields(ListMapper $list): void ->add('availableAt') ->add('deliveredAt') ->add('expiresAt') - ->add('tags', 'list'); + ->add('tags', 'list') + ->add( + ListMapper::NAME_ACTIONS, + ListMapper::TYPE_ACTIONS, + [ + 'actions' => [ + 'show' => [], + 'retry' => [ + 'template' => '@DrawSonataIntegration/Messenger/Message/list__action_retry.html.twig', + ], + 'delete' => [], + ], + ] + ); } public function dumpMessage(DrawMessageInterface $message): string @@ -109,6 +122,7 @@ public function dumpMessage(DrawMessageInterface $message): string protected function configureRoutes(RouteCollectionInterface $collection): void { + $collection->add('retry', sprintf('%s/retry', $this->getRouterIdParameter())); $collection->remove('create'); $collection->remove('edit'); } @@ -130,4 +144,15 @@ protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterfa return $query; } + + protected function configureActionButtons(array $buttonList, string $action, ?object $object = null): array + { + if ('show' === $action && 'failed' === $object?->getQueueName()) { + $buttonList['retry'] = [ + 'template' => '@DrawSonataIntegration/Messenger/Message/show__action_retry.html.twig', + ]; + } + + return $buttonList; + } } diff --git a/packages/sonata-integration-bundle/Messenger/Controller/MessageController.php b/packages/sonata-integration-bundle/Messenger/Controller/MessageController.php new file mode 100644 index 000000000..57807afd3 --- /dev/null +++ b/packages/sonata-integration-bundle/Messenger/Controller/MessageController.php @@ -0,0 +1,38 @@ +getQueueName()) { + $this->addFlash( + 'sonata_flash_error', + $this->trans('message_cannot_be_retried') + ); + + return $this->redirectToList(); + } + + $messageBus->dispatch( + new RetryFailedMessageMessage($message) + ); + + $this->addFlash( + 'sonata_flash_success', + $this->trans('retry_message_successfully_dispatched') + ); + + return $this->redirectToList(); + } +} diff --git a/packages/sonata-integration-bundle/Resources/translations/DrawMessengerAdmin.en.yaml b/packages/sonata-integration-bundle/Resources/translations/DrawMessengerAdmin.en.yaml new file mode 100644 index 000000000..f76e5734c --- /dev/null +++ b/packages/sonata-integration-bundle/Resources/translations/DrawMessengerAdmin.en.yaml @@ -0,0 +1,2 @@ +message_cannot_be_retried: Message cannot be retried. +retry_message_successfully_dispatched: Retry message successfully dispatched. diff --git a/packages/sonata-integration-bundle/Resources/views/Messenger/Message/list__action_retry.html.twig b/packages/sonata-integration-bundle/Resources/views/Messenger/Message/list__action_retry.html.twig new file mode 100644 index 000000000..28b3ff665 --- /dev/null +++ b/packages/sonata-integration-bundle/Resources/views/Messenger/Message/list__action_retry.html.twig @@ -0,0 +1,8 @@ +{% if 'failed' == object.getQueueName() %} + + Retry + +{% endif %} diff --git a/packages/sonata-integration-bundle/Resources/views/Messenger/Message/show__action_retry.html.twig b/packages/sonata-integration-bundle/Resources/views/Messenger/Message/show__action_retry.html.twig new file mode 100644 index 000000000..3427ccf1c --- /dev/null +++ b/packages/sonata-integration-bundle/Resources/views/Messenger/Message/show__action_retry.html.twig @@ -0,0 +1,8 @@ +
  • + + Retry + +
  • diff --git a/packages/sonata-integration-bundle/Tests/DependencyInjection/ConfigurationTest.php b/packages/sonata-integration-bundle/Tests/DependencyInjection/ConfigurationTest.php index f2612bd5e..7db564152 100644 --- a/packages/sonata-integration-bundle/Tests/DependencyInjection/ConfigurationTest.php +++ b/packages/sonata-integration-bundle/Tests/DependencyInjection/ConfigurationTest.php @@ -8,6 +8,7 @@ use Draw\Bundle\SonataIntegrationBundle\CronJob\Controller\CronJobController; use Draw\Bundle\SonataIntegrationBundle\CronJob\Controller\CronJobExecutionController; use Draw\Bundle\SonataIntegrationBundle\DependencyInjection\Configuration; +use Draw\Bundle\SonataIntegrationBundle\Messenger\Controller\MessageController; use Draw\Bundle\SonataIntegrationBundle\User\Extension\TwoFactorAuthenticationExtension; use Draw\Bundle\UserBundle\Entity\UserLock; use Draw\Component\Application\Configuration\Entity\Config; @@ -103,12 +104,12 @@ public function getDefaultConfiguration(): array 'admin' => [ 'group' => 'Messenger', 'entity_class' => MessengerMessage::class, - 'controller_class' => 'sonata.admin.controller.crud', + 'controller_class' => MessageController::class, 'icon' => 'fas fa-rss', 'label' => 'Message', 'pager_type' => 'simple', 'show_in_dashboard' => true, - 'translation_domain' => 'SonataAdminBundle', + 'translation_domain' => 'DrawMessengerAdmin', 'enabled' => true, ], ], diff --git a/packages/sonata-integration-bundle/Tests/DependencyInjection/DrawSonataIntegrationExtensionMessengerEnabledTest.php b/packages/sonata-integration-bundle/Tests/DependencyInjection/DrawSonataIntegrationExtensionMessengerEnabledTest.php index fbf80c056..5c25956ef 100644 --- a/packages/sonata-integration-bundle/Tests/DependencyInjection/DrawSonataIntegrationExtensionMessengerEnabledTest.php +++ b/packages/sonata-integration-bundle/Tests/DependencyInjection/DrawSonataIntegrationExtensionMessengerEnabledTest.php @@ -4,6 +4,7 @@ use Draw\Bundle\SonataIntegrationBundle\DependencyInjection\DrawSonataIntegrationExtension; use Draw\Bundle\SonataIntegrationBundle\Messenger\Admin\MessengerMessageAdmin; +use Draw\Bundle\SonataIntegrationBundle\Messenger\Controller\MessageController; use Draw\Bundle\SonataIntegrationBundle\Messenger\EventListener\FinalizeContextQueueCountEventListener; use Draw\Bundle\SonataIntegrationBundle\Messenger\Security\CanShowMessageVoter; use PHPUnit\Framework\Attributes\CoversClass; @@ -28,6 +29,7 @@ public function getConfiguration(): array public static function provideTestHasServiceDefinition(): iterable { yield [MessengerMessageAdmin::class]; + yield [MessageController::class]; yield [FinalizeContextQueueCountEventListener::class]; yield [CanShowMessageVoter::class]; }