From 6807f716c6f92c9f4596297eb278a2f08a72aad8 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 12:17:41 +0100 Subject: [PATCH 01/11] TASK: Lint PHP 8.2 --- .ddev/config.yaml | 2 +- .github/workflows/php_linter.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ddev/config.yaml b/.ddev/config.yaml index e0c6fef..7172625 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -1,7 +1,7 @@ name: t3-messenger type: typo3 docroot: .Build/Web -php_version: "8.1" +php_version: "8.2" webserver_type: nginx-fpm router_http_port: "80" router_https_port: "443" diff --git a/.github/workflows/php_linter.yaml b/.github/workflows/php_linter.yaml index 26079cd..f1e3472 100644 --- a/.github/workflows/php_linter.yaml +++ b/.github/workflows/php_linter.yaml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php-versions: [ 7.4, 8.0, 8.1 ] + php-versions: [ 7.4, 8.0, 8.1, 8.2 ] steps: - uses: actions/checkout@v4 From 43b1a2b3bb27193a503f3d0a5abadc66ecaee9f6 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 12:41:53 +0100 Subject: [PATCH 02/11] TASK: Add delete button --- Resources/Private/Language/locallang.xlf | 18 ++++++++++++++++++ .../Widget/ListOfFailedMessagesWidget.html | 17 ++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index 0dea6c6..fff81c1 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -22,9 +22,27 @@ Retries + + Actions + No messages found + + Delete + + + Remove Message + + + Are you sure you want to remove this message? + + + Delete + + + Close + diff --git a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html index ab6d725..5df6956 100644 --- a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html +++ b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html @@ -1,4 +1,4 @@ - + @@ -12,6 +12,7 @@ + @@ -34,6 +35,20 @@ {failedMessage.retryCount} + + + + + From 1bc63c368a2b4c2d93d795f05dd8221a0cfe711b Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 13:28:29 +0100 Subject: [PATCH 03/11] TASK: Add custom require js module --- .../Widgets/ListOfFailedMessagesWidget.php | 12 +++++++++- Resources/Public/JavaScript/FailedMessages.js | 22 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 Resources/Public/JavaScript/FailedMessages.js diff --git a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php index 1d48ff4..c23a2b2 100644 --- a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php +++ b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php @@ -11,12 +11,14 @@ namespace Ssch\T3Messenger\Dashboard\Widgets; +use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; use TYPO3\CMS\Dashboard\Widgets\ListDataProviderInterface; +use TYPO3\CMS\Dashboard\Widgets\RequireJsModuleInterface; use TYPO3\CMS\Dashboard\Widgets\WidgetConfigurationInterface; use TYPO3\CMS\Dashboard\Widgets\WidgetInterface; use TYPO3\CMS\Fluid\View\StandaloneView; -final class ListOfFailedMessagesWidget implements WidgetInterface +final class ListOfFailedMessagesWidget implements WidgetInterface, RequireJsModuleInterface { private WidgetConfigurationInterface $configuration; @@ -54,4 +56,12 @@ public function getOptions(): array { return $this->options; } + + + public function getRequireJsModules(): array + { + return [ + 'TYPO3/CMS/T3Messenger/FailedMessages', + ]; + } } diff --git a/Resources/Public/JavaScript/FailedMessages.js b/Resources/Public/JavaScript/FailedMessages.js new file mode 100644 index 0000000..4fbbeab --- /dev/null +++ b/Resources/Public/JavaScript/FailedMessages.js @@ -0,0 +1,22 @@ +define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", ], function(require, Modal, Event, Severity) { + "use strict"; + return new class { + constructor() { + this.selector = ".js-t3-messenger-remove-message", this.initialize() + } + + initialize() { + new Event("click", (function (e) { + e.preventDefault(); + Modal.confirm(this.dataset.modalTitle, this.dataset.modalQuestion, Severity.SeverityEnum.warning, [{ + text: this.dataset.modalCancel, + active: !0, + btnClass: "btn-default", + name: "cancel" + }, {text: this.dataset.modalOk, btnClass: "btn-warning", name: "delete"}]).on("button.clicked", e => { + "delete" === e.target.getAttribute("name") && (window.location.href = this.getAttribute("href")), Modal.dismiss() + }) + })).delegateTo(document, this.selector) + } + } +}); From e4201188934eb9aee52f9bcd65257e969ad19d8e Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 16:47:00 +0100 Subject: [PATCH 04/11] TASK: Add delete button for a message --- .../Controller/FailedMessageController.php | 41 ++++++++++++++ .../Widgets/Dto/MessageSpecification.php | 54 +++++++++++++++++++ .../Widgets/ListOfFailedMessagesWidget.php | 6 +-- .../Widgets/Serializer/JsonSerializer.php | 24 +++++++++ Classes/Domain/Dto/FailedMessage.php | 16 ++++-- .../Repository/FailedMessageRepository.php | 27 +++++++++- Configuration/Backend/AjaxRoutes.php | 19 +++++++ .../Widget/ListOfFailedMessagesWidget.html | 17 +++--- Resources/Public/JavaScript/FailedMessages.js | 20 +++++-- composer.json | 3 +- 10 files changed, 205 insertions(+), 22 deletions(-) create mode 100644 Classes/Dashboard/Widgets/Controller/FailedMessageController.php create mode 100644 Classes/Dashboard/Widgets/Dto/MessageSpecification.php create mode 100644 Classes/Dashboard/Widgets/Serializer/JsonSerializer.php create mode 100644 Configuration/Backend/AjaxRoutes.php diff --git a/Classes/Dashboard/Widgets/Controller/FailedMessageController.php b/Classes/Dashboard/Widgets/Controller/FailedMessageController.php new file mode 100644 index 0000000..e118407 --- /dev/null +++ b/Classes/Dashboard/Widgets/Controller/FailedMessageController.php @@ -0,0 +1,41 @@ +failedMessageRepository = $failedMessageRepository; + $this->jsonSerializer = $jsonSerializer; + } + + public function deleteMessageAction(ServerRequestInterface $request): ResponseInterface + { + $messageSpecification = $this->jsonSerializer->decode($request->getBody()->__toString()); + $this->failedMessageRepository->removeMessage($messageSpecification); + return new JsonResponse([ + 'result' => 1, + ]); + } +} diff --git a/Classes/Dashboard/Widgets/Dto/MessageSpecification.php b/Classes/Dashboard/Widgets/Dto/MessageSpecification.php new file mode 100644 index 0000000..d9bf67e --- /dev/null +++ b/Classes/Dashboard/Widgets/Dto/MessageSpecification.php @@ -0,0 +1,54 @@ +id = $id; + $this->transport = $transport; + } + + public static function fromArray(array $array): self + { + Assert::keyExists($array, 'id'); + Assert::keyExists($array, 'transport'); + + return new self($array['id'], $array['transport']); + } + + /** + * @return string|int + */ + public function getId() + { + return $this->id; + } + + public function getTransport(): string + { + return $this->transport; + } +} diff --git a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php index c23a2b2..32e66f4 100644 --- a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php +++ b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php @@ -11,7 +11,6 @@ namespace Ssch\T3Messenger\Dashboard\Widgets; -use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; use TYPO3\CMS\Dashboard\Widgets\ListDataProviderInterface; use TYPO3\CMS\Dashboard\Widgets\RequireJsModuleInterface; use TYPO3\CMS\Dashboard\Widgets\WidgetConfigurationInterface; @@ -57,11 +56,8 @@ public function getOptions(): array return $this->options; } - public function getRequireJsModules(): array { - return [ - 'TYPO3/CMS/T3Messenger/FailedMessages', - ]; + return ['TYPO3/CMS/T3Messenger/FailedMessages']; } } diff --git a/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php b/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php new file mode 100644 index 0000000..2547c88 --- /dev/null +++ b/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php @@ -0,0 +1,24 @@ +message = $message; $this->errorMessage = $errorMessage; $this->redelivered = $redelivered; $this->retryCount = $retryCount; $this->messageId = $messageId; + $this->transportName = $transportName; } /** @@ -79,12 +83,17 @@ public function getErrorMessage(): string return $this->errorMessage; } + public function getTransportName(): string + { + return $this->transportName; + } + public function getRetryCount(): int { return $this->retryCount; } - public static function createFromEnvelope(Envelope $failedMessage): self + public static function createFromEnvelope(Envelope $failedMessage, string $transportName): self { $errorDetailsStamp = $failedMessage->last(ErrorDetailsStamp::class); @@ -110,7 +119,8 @@ public static function createFromEnvelope(Envelope $failedMessage): self $errorMessage, $redeliveryStamp->getRedeliveredAt(), $redeliveryStamp->getRetryCount(), - $transportMessageIdStamp->getId() + $transportMessageIdStamp->getId(), + $transportName ); } } diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index 97d41d1..e06338c 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -11,7 +11,9 @@ namespace Ssch\T3Messenger\Repository; +use Ssch\T3Messenger\Dashboard\Widgets\Dto\MessageSpecification; use Ssch\T3Messenger\Domain\Dto\FailedMessage; +use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; use Symfony\Contracts\Service\ServiceProviderInterface; @@ -41,13 +43,36 @@ public function list(): array $failedMessages = $this->inReverseOrder($failureTransport->all()); foreach ($failedMessages as $failedMessage) { - $allFailedMessages[] = FailedMessage::createFromEnvelope($failedMessage); + $allFailedMessages[] = FailedMessage::createFromEnvelope($failedMessage, $serviceId); } } return $allFailedMessages; } + public function removeMessage(MessageSpecification $messageSpecification): void + { + $failureTransport = $this->failureTransports->get($messageSpecification->getTransport()); + + if (! $failureTransport instanceof ListableReceiverInterface) { + throw new RuntimeException(sprintf( + 'The "%s" receiver does not support removing specific messages.', + $messageSpecification->getTransport() + )); + } + + $envelope = $failureTransport->find($messageSpecification->getId()); + + if ($envelope === null) { + throw new RuntimeException(sprintf( + 'The message with id "%s" was not found.', + $messageSpecification->getId() + )); + } + + $failureTransport->reject($envelope); + } + /** * @param Envelope[] $failedMessages * diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php new file mode 100644 index 0000000..bf95410 --- /dev/null +++ b/Configuration/Backend/AjaxRoutes.php @@ -0,0 +1,19 @@ + [ + 'path' => '/t3-messenger/failed-messages/delete', + 'target' => FailedMessageController::class . '::deleteMessageAction', + ], +]; diff --git a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html index 5df6956..c1d926b 100644 --- a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html +++ b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html @@ -36,18 +36,17 @@ {failedMessage.retryCount} - - + diff --git a/Resources/Public/JavaScript/FailedMessages.js b/Resources/Public/JavaScript/FailedMessages.js index 4fbbeab..1d1a946 100644 --- a/Resources/Public/JavaScript/FailedMessages.js +++ b/Resources/Public/JavaScript/FailedMessages.js @@ -1,4 +1,4 @@ -define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", ], function(require, Modal, Event, Severity) { +define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest",], function (require, Modal, Event, Severity, AjaxRequest) { "use strict"; return new class { constructor() { @@ -8,13 +8,27 @@ define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent initialize() { new Event("click", (function (e) { e.preventDefault(); + var anchor = this; Modal.confirm(this.dataset.modalTitle, this.dataset.modalQuestion, Severity.SeverityEnum.warning, [{ text: this.dataset.modalCancel, active: !0, btnClass: "btn-default", name: "cancel" - }, {text: this.dataset.modalOk, btnClass: "btn-warning", name: "delete"}]).on("button.clicked", e => { - "delete" === e.target.getAttribute("name") && (window.location.href = this.getAttribute("href")), Modal.dismiss() + }, { + text: this.dataset.modalOk, + btnClass: "btn-warning", + name: "delete" + }]).on("button.clicked", function (e) { + if ("delete" === e.target.getAttribute("name")) { + var payload = {'id': anchor.dataset.messageId, 'transport': anchor.dataset.messageTransport}; + new AjaxRequest(TYPO3.settings.ajaxUrls.t3_messenger_failed_messages_delete) + .delete(JSON.stringify(payload)) + .then(async function () { + Modal.dismiss(); + }); + } else { + Modal.dismiss(); + } }) })).delegateTo(document, this.selector) } diff --git a/composer.json b/composer.json index 9abf4b3..f790d1d 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "symfony/options-resolver": "^5.0 || ^6.2", "symfony/doctrine-messenger": "^5.0 || ^6.2", "psr/cache": "^1.0 || ^2.0", - "ssch/typo3-psr-cache-adapter": "^1.2" + "ssch/typo3-psr-cache-adapter": "^1.2", + "ext-json": "*" }, "require-dev": { "symplify/easy-coding-standard": "^12.0", From 655969e0f3562f2d692df8dd0242ffe7212f4100 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 16:55:22 +0100 Subject: [PATCH 05/11] TASK: Add Retry functionality --- .../Repository/FailedMessageRepository.php | 64 +++++++++++++++++-- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index e06338c..0455248 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -11,11 +11,16 @@ namespace Ssch\T3Messenger\Repository; +use Psr\Log\LoggerInterface; use Ssch\T3Messenger\Dashboard\Widgets\Dto\MessageSpecification; use Ssch\T3Messenger\Domain\Dto\FailedMessage; use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; +use Symfony\Component\Messenger\Transport\Receiver\SingleMessageReceiver; +use Symfony\Component\Messenger\Worker; use Symfony\Contracts\Service\ServiceProviderInterface; use TYPO3\CMS\Core\SingletonInterface; @@ -23,9 +28,18 @@ final class FailedMessageRepository implements SingletonInterface { private ServiceProviderInterface $failureTransports; - public function __construct(ServiceProviderInterface $failureTransports) + private MessageBusInterface $messageBus; + + private EventDispatcherInterface $eventDispatcher; + + private LoggerInterface $logger; + + public function __construct(ServiceProviderInterface $failureTransports, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger) { $this->failureTransports = $failureTransports; + $this->messageBus = $messageBus; + $this->eventDispatcher = $eventDispatcher; + $this->logger = $logger; } /** @@ -52,15 +66,24 @@ public function list(): array public function removeMessage(MessageSpecification $messageSpecification): void { - $failureTransport = $this->failureTransports->get($messageSpecification->getTransport()); + $failureTransport = $this->getReceiver($messageSpecification->getTransport()); - if (! $failureTransport instanceof ListableReceiverInterface) { + $envelope = $failureTransport->find($messageSpecification->getId()); + + if ($envelope === null) { throw new RuntimeException(sprintf( - 'The "%s" receiver does not support removing specific messages.', - $messageSpecification->getTransport() + 'The message with id "%s" was not found.', + $messageSpecification->getId() )); } + $failureTransport->reject($envelope); + } + + public function retryMessage(MessageSpecification $messageSpecification): void + { + $failureTransport = $this->getReceiver($messageSpecification->getTransport()); + $envelope = $failureTransport->find($messageSpecification->getId()); if ($envelope === null) { @@ -70,7 +93,8 @@ public function removeMessage(MessageSpecification $messageSpecification): void )); } - $failureTransport->reject($envelope); + $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); + $this->runWorker($messageSpecification->getTransport(), $singleReceiver); } /** @@ -86,4 +110,32 @@ private function inReverseOrder(iterable $failedMessages): array return array_reverse($failedMessages); } + + private function getReceiver(string $transport): ListableReceiverInterface + { + $failureTransport = $this->failureTransports->get($transport); + + if (! $failureTransport instanceof ListableReceiverInterface) { + throw new RuntimeException(sprintf( + 'The "%s" receiver does not support removing specific messages.', + $transport + )); + } + + return $failureTransport; + } + + private function runWorker(string $transport, SingleMessageReceiver $receiver): void + { + $worker = new Worker( + [ + $transport => $receiver, + ], + $this->messageBus, + $this->eventDispatcher, + $this->logger + ); + + $worker->run(); + } } From 4549177e9f23ca3aab8ddbac6092f8caf8ac8156 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 21:27:42 +0100 Subject: [PATCH 06/11] TASK: Add Retry functionality --- .../Controller/FailedMessageController.php | 9 +++++ .../Widgets/ListOfFailedMessagesWidget.php | 2 +- .../Repository/FailedMessageRepository.php | 8 +++-- Configuration/Backend/AjaxRoutes.php | 4 +++ Configuration/Services.php | 4 ++- Resources/Private/Language/locallang.xlf | 15 ++++++++ .../Widget/ListOfFailedMessagesWidget.html | 11 ++++++ ...iledMessages.js => DeleteFailedMessage.js} | 0 .../Public/JavaScript/RetryFailedMessage.js | 36 +++++++++++++++++++ 9 files changed, 84 insertions(+), 5 deletions(-) rename Resources/Public/JavaScript/{FailedMessages.js => DeleteFailedMessage.js} (100%) create mode 100644 Resources/Public/JavaScript/RetryFailedMessage.js diff --git a/Classes/Dashboard/Widgets/Controller/FailedMessageController.php b/Classes/Dashboard/Widgets/Controller/FailedMessageController.php index e118407..8a95dc4 100644 --- a/Classes/Dashboard/Widgets/Controller/FailedMessageController.php +++ b/Classes/Dashboard/Widgets/Controller/FailedMessageController.php @@ -38,4 +38,13 @@ public function deleteMessageAction(ServerRequestInterface $request): ResponseIn 'result' => 1, ]); } + + public function retryMessageAction(ServerRequestInterface $request): ResponseInterface + { + $messageSpecification = $this->jsonSerializer->decode($request->getBody()->__toString()); + $this->failedMessageRepository->retryMessage($messageSpecification); + return new JsonResponse([ + 'result' => 1, + ]); + } } diff --git a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php index 32e66f4..cccbfca 100644 --- a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php +++ b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php @@ -58,6 +58,6 @@ public function getOptions(): array public function getRequireJsModules(): array { - return ['TYPO3/CMS/T3Messenger/FailedMessages']; + return ['TYPO3/CMS/T3Messenger/RetryFailedMessage', 'TYPO3/CMS/T3Messenger/DeleteFailedMessage']; } } diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index 0455248..eec3402 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -15,12 +15,13 @@ use Ssch\T3Messenger\Dashboard\Widgets\Dto\MessageSpecification; use Ssch\T3Messenger\Domain\Dto\FailedMessage; use Symfony\Component\Console\Exception\RuntimeException; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; use Symfony\Component\Messenger\Transport\Receiver\SingleMessageReceiver; use Symfony\Component\Messenger\Worker; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Service\ServiceProviderInterface; use TYPO3\CMS\Core\SingletonInterface; @@ -34,7 +35,7 @@ final class FailedMessageRepository implements SingletonInterface private LoggerInterface $logger; - public function __construct(ServiceProviderInterface $failureTransports, MessageBusInterface $messageBus, EventDispatcherInterface $eventDispatcher, LoggerInterface $logger) + public function __construct(ServiceProviderInterface $failureTransports, EventDispatcherInterface $eventDispatcher, MessageBusInterface $messageBus, LoggerInterface $logger) { $this->failureTransports = $failureTransports; $this->messageBus = $messageBus; @@ -86,6 +87,7 @@ public function retryMessage(MessageSpecification $messageSpecification): void $envelope = $failureTransport->find($messageSpecification->getId()); + // $sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class); if ($envelope === null) { throw new RuntimeException(sprintf( 'The message with id "%s" was not found.', @@ -93,7 +95,7 @@ public function retryMessage(MessageSpecification $messageSpecification): void )); } - $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); + // $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); $this->runWorker($messageSpecification->getTransport(), $singleReceiver); } diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php index bf95410..d3158f2 100644 --- a/Configuration/Backend/AjaxRoutes.php +++ b/Configuration/Backend/AjaxRoutes.php @@ -16,4 +16,8 @@ 'path' => '/t3-messenger/failed-messages/delete', 'target' => FailedMessageController::class . '::deleteMessageAction', ], + 't3_messenger_failed_messages_retry' => [ + 'path' => '/t3-messenger/failed-messages/retry', + 'target' => FailedMessageController::class . '::retryMessageAction', + ], ]; diff --git a/Configuration/Services.php b/Configuration/Services.php index 83b745c..b9a807c 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -420,7 +420,9 @@ static function (ChildDefinition $definition, AsMessageHandler $attribute): void ]); // Dashboard Integration - $services->set(FailedMessageRepository::class)->args([abstract_arg('failure_transports')]); + $services->set(FailedMessageRepository::class)->args( + [abstract_arg('failure_transports'), service('event_dispatcher')] + ); $services ->set('dashboard.widget.failedMessages') ->class(ListOfFailedMessagesWidget::class) diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf index fff81c1..f9a8e23 100644 --- a/Resources/Private/Language/locallang.xlf +++ b/Resources/Private/Language/locallang.xlf @@ -43,6 +43,21 @@ Close + + Retry + + + Retry Message + + + Are you sure you want to retry this message? + + + Retry + + + Close + diff --git a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html index c1d926b..af76253 100644 --- a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html +++ b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html @@ -47,6 +47,17 @@ > + + + diff --git a/Resources/Public/JavaScript/FailedMessages.js b/Resources/Public/JavaScript/DeleteFailedMessage.js similarity index 100% rename from Resources/Public/JavaScript/FailedMessages.js rename to Resources/Public/JavaScript/DeleteFailedMessage.js diff --git a/Resources/Public/JavaScript/RetryFailedMessage.js b/Resources/Public/JavaScript/RetryFailedMessage.js new file mode 100644 index 0000000..07b5e6c --- /dev/null +++ b/Resources/Public/JavaScript/RetryFailedMessage.js @@ -0,0 +1,36 @@ +define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest",], function (require, Modal, Event, Severity, AjaxRequest) { + "use strict"; + return new class { + constructor() { + this.selector = ".js-t3-messenger-retry-message", this.initialize() + } + + initialize() { + new Event("click", (function (e) { + e.preventDefault(); + var anchor = this; + Modal.confirm(this.dataset.modalTitle, this.dataset.modalQuestion, Severity.SeverityEnum.warning, [{ + text: this.dataset.modalCancel, + active: !0, + btnClass: "btn-default", + name: "cancel" + }, { + text: this.dataset.modalOk, + btnClass: "btn-warning", + name: "retry" + }]).on("button.clicked", function (e) { + if ("retry" === e.target.getAttribute("name")) { + var payload = {'id': anchor.dataset.messageId, 'transport': anchor.dataset.messageTransport}; + new AjaxRequest(TYPO3.settings.ajaxUrls.t3_messenger_failed_messages_retry) + .post(JSON.stringify(payload)) + .then(async function () { + Modal.dismiss(); + }); + } else { + Modal.dismiss(); + } + }) + })).delegateTo(document, this.selector) + } + } +}); From 46b20af0001e0d9204719cc91917df6142608429 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Thu, 7 Dec 2023 21:44:41 +0100 Subject: [PATCH 07/11] TASK: Add Refresh module --- Classes/Repository/FailedMessageRepository.php | 2 +- Resources/Public/JavaScript/DeleteFailedMessage.js | 3 ++- .../Public/JavaScript/RefreshFailedMessages.js | 13 +++++++++++++ Resources/Public/JavaScript/RetryFailedMessage.js | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 Resources/Public/JavaScript/RefreshFailedMessages.js diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index eec3402..cfd3e55 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -95,7 +95,7 @@ public function retryMessage(MessageSpecification $messageSpecification): void )); } - // $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); + $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); $this->runWorker($messageSpecification->getTransport(), $singleReceiver); } diff --git a/Resources/Public/JavaScript/DeleteFailedMessage.js b/Resources/Public/JavaScript/DeleteFailedMessage.js index 1d1a946..d2ede62 100644 --- a/Resources/Public/JavaScript/DeleteFailedMessage.js +++ b/Resources/Public/JavaScript/DeleteFailedMessage.js @@ -1,4 +1,4 @@ -define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest",], function (require, Modal, Event, Severity, AjaxRequest) { +define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest", "TYPO3/CMS/T3Messenger/RefreshFailedMessages"], function (require, Modal, Event, Severity, AjaxRequest, RefreshFailedMessages) { "use strict"; return new class { constructor() { @@ -25,6 +25,7 @@ define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent .delete(JSON.stringify(payload)) .then(async function () { Modal.dismiss(); + RefreshFailedMessages.refresh(); }); } else { Modal.dismiss(); diff --git a/Resources/Public/JavaScript/RefreshFailedMessages.js b/Resources/Public/JavaScript/RefreshFailedMessages.js new file mode 100644 index 0000000..77b4364 --- /dev/null +++ b/Resources/Public/JavaScript/RefreshFailedMessages.js @@ -0,0 +1,13 @@ +define(['require', "TYPO3/CMS/Dashboard/WidgetContentCollector"], function (require, DashboardWidget) { + "use strict"; + return new class { + constructor() { + + } + + refresh() { + const failedMessages = document.querySelector('[data-widget-key="failedMessages"]'); + DashboardWidget.getContentForWidget(failedMessages); + } + } +}); diff --git a/Resources/Public/JavaScript/RetryFailedMessage.js b/Resources/Public/JavaScript/RetryFailedMessage.js index 07b5e6c..7d2ec85 100644 --- a/Resources/Public/JavaScript/RetryFailedMessage.js +++ b/Resources/Public/JavaScript/RetryFailedMessage.js @@ -1,4 +1,4 @@ -define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest",], function (require, Modal, Event, Severity, AjaxRequest) { +define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest", "TYPO3/CMS/T3Messenger/RefreshFailedMessages"], function (require, Modal, Event, Severity, AjaxRequest, RefreshWidget) { "use strict"; return new class { constructor() { @@ -25,6 +25,7 @@ define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent .post(JSON.stringify(payload)) .then(async function () { Modal.dismiss(); + RefreshWidget.refresh(); }); } else { Modal.dismiss(); From bd23a626b6d9051d4eb2b6377ec90f872b518488 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Fri, 8 Dec 2023 10:52:58 +0100 Subject: [PATCH 08/11] TASK: Add Worker for Single Message --- .../Repository/FailedMessageRepository.php | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index cfd3e55..6ec2d77 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -15,13 +15,13 @@ use Ssch\T3Messenger\Dashboard\Widgets\Dto\MessageSpecification; use Ssch\T3Messenger\Domain\Dto\FailedMessage; use Symfony\Component\Console\Exception\RuntimeException; +use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Messenger\Envelope; +use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener; use Symfony\Component\Messenger\MessageBusInterface; -use Symfony\Component\Messenger\Stamp\SentToFailureTransportStamp; use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface; use Symfony\Component\Messenger\Transport\Receiver\SingleMessageReceiver; use Symfony\Component\Messenger\Worker; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\Service\ServiceProviderInterface; use TYPO3\CMS\Core\SingletonInterface; @@ -31,11 +31,11 @@ final class FailedMessageRepository implements SingletonInterface private MessageBusInterface $messageBus; - private EventDispatcherInterface $eventDispatcher; + private EventDispatcher $eventDispatcher; private LoggerInterface $logger; - public function __construct(ServiceProviderInterface $failureTransports, EventDispatcherInterface $eventDispatcher, MessageBusInterface $messageBus, LoggerInterface $logger) + public function __construct(ServiceProviderInterface $failureTransports, EventDispatcher $eventDispatcher, MessageBusInterface $messageBus, LoggerInterface $logger) { $this->failureTransports = $failureTransports; $this->messageBus = $messageBus; @@ -87,7 +87,6 @@ public function retryMessage(MessageSpecification $messageSpecification): void $envelope = $failureTransport->find($messageSpecification->getId()); - // $sentToFailureTransportStamp = $envelope->last(SentToFailureTransportStamp::class); if ($envelope === null) { throw new RuntimeException(sprintf( 'The message with id "%s" was not found.', @@ -96,7 +95,21 @@ public function retryMessage(MessageSpecification $messageSpecification): void } $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); - $this->runWorker($messageSpecification->getTransport(), $singleReceiver); + + $subscriber = new StopWorkerOnMessageLimitListener(1); + $this->eventDispatcher->addSubscriber($subscriber); + + $worker = new Worker( + [ + $messageSpecification->getTransport() => $singleReceiver, + ], + $this->messageBus, + $this->eventDispatcher, + $this->logger + ); + + $worker->run(); + $this->eventDispatcher->removeSubscriber($subscriber); } /** @@ -126,18 +139,4 @@ private function getReceiver(string $transport): ListableReceiverInterface return $failureTransport; } - - private function runWorker(string $transport, SingleMessageReceiver $receiver): void - { - $worker = new Worker( - [ - $transport => $receiver, - ], - $this->messageBus, - $this->eventDispatcher, - $this->logger - ); - - $worker->run(); - } } From 1a24360c3c5b1ef7a6fc46c278323b1a75bda273 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Fri, 8 Dec 2023 10:56:52 +0100 Subject: [PATCH 09/11] TASK: Use ShortMessageClass --- Classes/Domain/Dto/FailedMessage.php | 10 ++++++ .../Repository/FailedMessageRepository.php | 36 +++++++++---------- .../Widget/ListOfFailedMessagesWidget.html | 2 +- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/Classes/Domain/Dto/FailedMessage.php b/Classes/Domain/Dto/FailedMessage.php index d6e4492..5126423 100644 --- a/Classes/Domain/Dto/FailedMessage.php +++ b/Classes/Domain/Dto/FailedMessage.php @@ -37,12 +37,15 @@ final class FailedMessage private string $transportName; + private string $shortMessageClass; + /** * @param class-string $message * @param mixed $messageId */ private function __construct( string $message, + string $shortMessageClass, string $errorMessage, DateTimeInterface $redelivered, int $retryCount, @@ -55,6 +58,12 @@ private function __construct( $this->retryCount = $retryCount; $this->messageId = $messageId; $this->transportName = $transportName; + $this->shortMessageClass = $shortMessageClass; + } + + public function getShortMessageClass(): string + { + return $this->shortMessageClass; } /** @@ -116,6 +125,7 @@ public static function createFromEnvelope(Envelope $failedMessage, string $trans return new self( get_class($failedMessage->getMessage()), + (new \ReflectionClass($failedMessage->getMessage()))->getShortName(), $errorMessage, $redeliveryStamp->getRedeliveredAt(), $redeliveryStamp->getRetryCount(), diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php index 6ec2d77..fe319ac 100644 --- a/Classes/Repository/FailedMessageRepository.php +++ b/Classes/Repository/FailedMessageRepository.php @@ -50,8 +50,9 @@ public function list(): array { $allFailedMessages = []; foreach ($this->failureTransports->getProvidedServices() as $serviceId => $_) { - $failureTransport = $this->failureTransports->get($serviceId); - if (! $failureTransport instanceof ListableReceiverInterface) { + try { + $failureTransport = $this->getReceiver($serviceId); + } catch (\RuntimeException $runtimeException) { continue; } @@ -69,14 +70,7 @@ public function removeMessage(MessageSpecification $messageSpecification): void { $failureTransport = $this->getReceiver($messageSpecification->getTransport()); - $envelope = $failureTransport->find($messageSpecification->getId()); - - if ($envelope === null) { - throw new RuntimeException(sprintf( - 'The message with id "%s" was not found.', - $messageSpecification->getId() - )); - } + $envelope = $this->findMessage($messageSpecification->getId(), $failureTransport); $failureTransport->reject($envelope); } @@ -85,14 +79,7 @@ public function retryMessage(MessageSpecification $messageSpecification): void { $failureTransport = $this->getReceiver($messageSpecification->getTransport()); - $envelope = $failureTransport->find($messageSpecification->getId()); - - if ($envelope === null) { - throw new RuntimeException(sprintf( - 'The message with id "%s" was not found.', - $messageSpecification->getId() - )); - } + $envelope = $this->findMessage($messageSpecification->getId(), $failureTransport); $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); @@ -139,4 +126,17 @@ private function getReceiver(string $transport): ListableReceiverInterface return $failureTransport; } + + /** + * @param string|int $messageId + */ + private function findMessage($messageId, ListableReceiverInterface $failureTransport): Envelope + { + $envelope = $failureTransport->find($messageId); + if ($envelope === null) { + throw new RuntimeException(sprintf('The message with id "%s" was not found.', $messageId)); + } + + return $envelope; + } } diff --git a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html index af76253..8a66838 100644 --- a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html +++ b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html @@ -23,7 +23,7 @@ - {failedMessage.message} + {failedMessage.shortMessageClass} From 16701775a4f623e76f235d85ea827b9edab6882d Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Fri, 8 Dec 2023 12:14:32 +0100 Subject: [PATCH 10/11] TASK: Remove dashboard functionality --- .../Controller/FailedMessageController.php | 50 ------ .../Widgets/Dto/MessageSpecification.php | 54 ------- .../Widgets/ListOfFailedMessagesWidget.php | 63 -------- .../Provider/FailedMessagesDataProvider.php | 30 ---- .../Widgets/Serializer/JsonSerializer.php | 24 --- .../Compiler/FailureReceiverPass.php | 28 ---- Classes/Domain/Dto/FailedMessage.php | 136 ----------------- .../Repository/FailedMessageRepository.php | 142 ------------------ Configuration/Backend/AjaxRoutes.php | 23 --- Configuration/Services.php | 27 ---- Configuration/TypoScript/setup.typoscript | 4 - Resources/Private/Language/locallang.xlf | 63 -------- .../Widget/ListOfFailedMessagesWidget.html | 79 ---------- Resources/Public/Icons/failed-messages.svg | 53 ------- .../Public/JavaScript/DeleteFailedMessage.js | 37 ----- .../JavaScript/RefreshFailedMessages.js | 13 -- .../Public/JavaScript/RetryFailedMessage.js | 37 ----- .../FailedMessageRepositoryTest.php | 87 ----------- composer.json | 3 +- ext_localconf.php | 13 -- phpstan-baseline.neon | 22 +-- 21 files changed, 7 insertions(+), 981 deletions(-) delete mode 100644 Classes/Dashboard/Widgets/Controller/FailedMessageController.php delete mode 100644 Classes/Dashboard/Widgets/Dto/MessageSpecification.php delete mode 100644 Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php delete mode 100644 Classes/Dashboard/Widgets/Provider/FailedMessagesDataProvider.php delete mode 100644 Classes/Dashboard/Widgets/Serializer/JsonSerializer.php delete mode 100644 Classes/DependencyInjection/Compiler/FailureReceiverPass.php delete mode 100644 Classes/Domain/Dto/FailedMessage.php delete mode 100644 Classes/Repository/FailedMessageRepository.php delete mode 100644 Configuration/Backend/AjaxRoutes.php delete mode 100644 Configuration/TypoScript/setup.typoscript delete mode 100644 Resources/Private/Language/locallang.xlf delete mode 100644 Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html delete mode 100644 Resources/Public/Icons/failed-messages.svg delete mode 100644 Resources/Public/JavaScript/DeleteFailedMessage.js delete mode 100644 Resources/Public/JavaScript/RefreshFailedMessages.js delete mode 100644 Resources/Public/JavaScript/RetryFailedMessage.js delete mode 100644 Tests/Functional/Repository/FailedMessageRepositoryTest.php diff --git a/Classes/Dashboard/Widgets/Controller/FailedMessageController.php b/Classes/Dashboard/Widgets/Controller/FailedMessageController.php deleted file mode 100644 index 8a95dc4..0000000 --- a/Classes/Dashboard/Widgets/Controller/FailedMessageController.php +++ /dev/null @@ -1,50 +0,0 @@ -failedMessageRepository = $failedMessageRepository; - $this->jsonSerializer = $jsonSerializer; - } - - public function deleteMessageAction(ServerRequestInterface $request): ResponseInterface - { - $messageSpecification = $this->jsonSerializer->decode($request->getBody()->__toString()); - $this->failedMessageRepository->removeMessage($messageSpecification); - return new JsonResponse([ - 'result' => 1, - ]); - } - - public function retryMessageAction(ServerRequestInterface $request): ResponseInterface - { - $messageSpecification = $this->jsonSerializer->decode($request->getBody()->__toString()); - $this->failedMessageRepository->retryMessage($messageSpecification); - return new JsonResponse([ - 'result' => 1, - ]); - } -} diff --git a/Classes/Dashboard/Widgets/Dto/MessageSpecification.php b/Classes/Dashboard/Widgets/Dto/MessageSpecification.php deleted file mode 100644 index d9bf67e..0000000 --- a/Classes/Dashboard/Widgets/Dto/MessageSpecification.php +++ /dev/null @@ -1,54 +0,0 @@ -id = $id; - $this->transport = $transport; - } - - public static function fromArray(array $array): self - { - Assert::keyExists($array, 'id'); - Assert::keyExists($array, 'transport'); - - return new self($array['id'], $array['transport']); - } - - /** - * @return string|int - */ - public function getId() - { - return $this->id; - } - - public function getTransport(): string - { - return $this->transport; - } -} diff --git a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php b/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php deleted file mode 100644 index cccbfca..0000000 --- a/Classes/Dashboard/Widgets/ListOfFailedMessagesWidget.php +++ /dev/null @@ -1,63 +0,0 @@ -configuration = $configuration; - $this->dataProvider = $dataProvider; - $this->view = $view; - $this->options = $options; - } - - public function renderWidgetContent(): string - { - $this->view->setTemplate('Widget/ListOfFailedMessagesWidget'); - $this->view->assignMultiple([ - 'configuration' => $this->configuration, - 'failedMessages' => $this->dataProvider->getItems(), - 'options' => $this->options, - ]); - - return $this->view->render(); - } - - public function getOptions(): array - { - return $this->options; - } - - public function getRequireJsModules(): array - { - return ['TYPO3/CMS/T3Messenger/RetryFailedMessage', 'TYPO3/CMS/T3Messenger/DeleteFailedMessage']; - } -} diff --git a/Classes/Dashboard/Widgets/Provider/FailedMessagesDataProvider.php b/Classes/Dashboard/Widgets/Provider/FailedMessagesDataProvider.php deleted file mode 100644 index fc145f4..0000000 --- a/Classes/Dashboard/Widgets/Provider/FailedMessagesDataProvider.php +++ /dev/null @@ -1,30 +0,0 @@ -failedMessageRepository = $failedMessageRepository; - } - - public function getItems(): array - { - return $this->failedMessageRepository->list(); - } -} diff --git a/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php b/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php deleted file mode 100644 index 2547c88..0000000 --- a/Classes/Dashboard/Widgets/Serializer/JsonSerializer.php +++ /dev/null @@ -1,24 +0,0 @@ -getDefinition(FailedMessageRepository::class); - $failedMessagesShowCommandDefinition = $container->getDefinition( - 'console.command.messenger_failed_messages_show' - ); - $failureMessageRepositoryDefinition->replaceArgument(0, $failedMessagesShowCommandDefinition->getArgument(1)); - } -} diff --git a/Classes/Domain/Dto/FailedMessage.php b/Classes/Domain/Dto/FailedMessage.php deleted file mode 100644 index 5126423..0000000 --- a/Classes/Domain/Dto/FailedMessage.php +++ /dev/null @@ -1,136 +0,0 @@ -message = $message; - $this->errorMessage = $errorMessage; - $this->redelivered = $redelivered; - $this->retryCount = $retryCount; - $this->messageId = $messageId; - $this->transportName = $transportName; - $this->shortMessageClass = $shortMessageClass; - } - - public function getShortMessageClass(): string - { - return $this->shortMessageClass; - } - - /** - * @return mixed - */ - public function getMessageId() - { - return $this->messageId; - } - - /** - * @return class-string - */ - public function getMessage(): string - { - return $this->message; - } - - public function getRedelivered(): DateTimeInterface - { - return $this->redelivered; - } - - public function getErrorMessage(): string - { - return $this->errorMessage; - } - - public function getTransportName(): string - { - return $this->transportName; - } - - public function getRetryCount(): int - { - return $this->retryCount; - } - - public static function createFromEnvelope(Envelope $failedMessage, string $transportName): self - { - $errorDetailsStamp = $failedMessage->last(ErrorDetailsStamp::class); - - if (! $errorDetailsStamp instanceof ErrorDetailsStamp) { - throw new \UnexpectedValueException('No error details stamp given'); - } - - $errorMessage = $errorDetailsStamp->getExceptionMessage(); - - $redeliveryStamp = $failedMessage->last(RedeliveryStamp::class); - - if (! $redeliveryStamp instanceof RedeliveryStamp) { - throw new \UnexpectedValueException('No redelivery stamp given'); - } - - $transportMessageIdStamp = $failedMessage->last(TransportMessageIdStamp::class); - if (! $transportMessageIdStamp instanceof TransportMessageIdStamp) { - throw new \UnexpectedValueException('No transport message id stamp given'); - } - - return new self( - get_class($failedMessage->getMessage()), - (new \ReflectionClass($failedMessage->getMessage()))->getShortName(), - $errorMessage, - $redeliveryStamp->getRedeliveredAt(), - $redeliveryStamp->getRetryCount(), - $transportMessageIdStamp->getId(), - $transportName - ); - } -} diff --git a/Classes/Repository/FailedMessageRepository.php b/Classes/Repository/FailedMessageRepository.php deleted file mode 100644 index fe319ac..0000000 --- a/Classes/Repository/FailedMessageRepository.php +++ /dev/null @@ -1,142 +0,0 @@ -failureTransports = $failureTransports; - $this->messageBus = $messageBus; - $this->eventDispatcher = $eventDispatcher; - $this->logger = $logger; - } - - /** - * @return FailedMessage[] - */ - public function list(): array - { - $allFailedMessages = []; - foreach ($this->failureTransports->getProvidedServices() as $serviceId => $_) { - try { - $failureTransport = $this->getReceiver($serviceId); - } catch (\RuntimeException $runtimeException) { - continue; - } - - $failedMessages = $this->inReverseOrder($failureTransport->all()); - - foreach ($failedMessages as $failedMessage) { - $allFailedMessages[] = FailedMessage::createFromEnvelope($failedMessage, $serviceId); - } - } - - return $allFailedMessages; - } - - public function removeMessage(MessageSpecification $messageSpecification): void - { - $failureTransport = $this->getReceiver($messageSpecification->getTransport()); - - $envelope = $this->findMessage($messageSpecification->getId(), $failureTransport); - - $failureTransport->reject($envelope); - } - - public function retryMessage(MessageSpecification $messageSpecification): void - { - $failureTransport = $this->getReceiver($messageSpecification->getTransport()); - - $envelope = $this->findMessage($messageSpecification->getId(), $failureTransport); - - $singleReceiver = new SingleMessageReceiver($failureTransport, $envelope); - - $subscriber = new StopWorkerOnMessageLimitListener(1); - $this->eventDispatcher->addSubscriber($subscriber); - - $worker = new Worker( - [ - $messageSpecification->getTransport() => $singleReceiver, - ], - $this->messageBus, - $this->eventDispatcher, - $this->logger - ); - - $worker->run(); - $this->eventDispatcher->removeSubscriber($subscriber); - } - - /** - * @param Envelope[] $failedMessages - * - * @return Envelope[]; - */ - private function inReverseOrder(iterable $failedMessages): array - { - if (! is_array($failedMessages)) { - $failedMessages = iterator_to_array($failedMessages); - } - - return array_reverse($failedMessages); - } - - private function getReceiver(string $transport): ListableReceiverInterface - { - $failureTransport = $this->failureTransports->get($transport); - - if (! $failureTransport instanceof ListableReceiverInterface) { - throw new RuntimeException(sprintf( - 'The "%s" receiver does not support removing specific messages.', - $transport - )); - } - - return $failureTransport; - } - - /** - * @param string|int $messageId - */ - private function findMessage($messageId, ListableReceiverInterface $failureTransport): Envelope - { - $envelope = $failureTransport->find($messageId); - if ($envelope === null) { - throw new RuntimeException(sprintf('The message with id "%s" was not found.', $messageId)); - } - - return $envelope; - } -} diff --git a/Configuration/Backend/AjaxRoutes.php b/Configuration/Backend/AjaxRoutes.php deleted file mode 100644 index d3158f2..0000000 --- a/Configuration/Backend/AjaxRoutes.php +++ /dev/null @@ -1,23 +0,0 @@ - [ - 'path' => '/t3-messenger/failed-messages/delete', - 'target' => FailedMessageController::class . '::deleteMessageAction', - ], - 't3_messenger_failed_messages_retry' => [ - 'path' => '/t3-messenger/failed-messages/retry', - 'target' => FailedMessageController::class . '::retryMessageAction', - ], -]; diff --git a/Configuration/Services.php b/Configuration/Services.php index b9a807c..acf5689 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -15,9 +15,6 @@ use Ssch\T3Messenger\Command\ShowConfigurationCommand; use Ssch\T3Messenger\CommandToHandlerMapper; use Ssch\T3Messenger\ConfigurationModuleProvider\MessengerProvider; -use Ssch\T3Messenger\Dashboard\Widgets\ListOfFailedMessagesWidget; -use Ssch\T3Messenger\Dashboard\Widgets\Provider\FailedMessagesDataProvider; -use Ssch\T3Messenger\DependencyInjection\Compiler\FailureReceiverPass; use Ssch\T3Messenger\DependencyInjection\Compiler\MessengerAlterTableListenerPass; use Ssch\T3Messenger\DependencyInjection\Compiler\MessengerCommandToHandlerMapperPass; use Ssch\T3Messenger\DependencyInjection\Compiler\MessengerMailerPass; @@ -31,7 +28,6 @@ use Ssch\T3Messenger\Middleware\ServerRequestContextMiddleware; use Ssch\T3Messenger\Middleware\ValidationMiddleware; use Ssch\T3Messenger\Mime\BodyRenderer; -use Ssch\T3Messenger\Repository\FailedMessageRepository; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -419,28 +415,6 @@ static function (ChildDefinition $definition, AsMessageHandler $attribute): void 'command' => 't3_messenger:show-configuration', ]); - // Dashboard Integration - $services->set(FailedMessageRepository::class)->args( - [abstract_arg('failure_transports'), service('event_dispatcher')] - ); - $services - ->set('dashboard.widget.failedMessages') - ->class(ListOfFailedMessagesWidget::class) - ->arg('$dataProvider', service(FailedMessagesDataProvider::class)) - ->arg('$view', service('dashboard.views.widget')) - ->tag( - 'dashboard.widget', - [ - 'identifier' => 'failedMessages', - 'groupNames' => 'news', - 'title' => 'LLL:EXT:t3_messenger/Resources/Private/Language/locallang.xlf:widgets.failedMessages.title', - 'description' => 'LLL:EXT:t3_messenger/Resources/Private/Language/locallang.xlf:widgets.failedMessages.description', - 'iconIdentifier' => 'tx-messenger-failed-messages-icon', - 'height' => 'large', - 'width' => 'large', - ] - ); - // must be registered before removing private services as some might be listeners/subscribers // but as late as possible to get resolved parameters $containerBuilder->addCompilerPass($registerListenersPass, PassConfig::TYPE_BEFORE_REMOVING); @@ -450,5 +424,4 @@ static function (ChildDefinition $definition, AsMessageHandler $attribute): void $containerBuilder->addCompilerPass(new ConsoleCommandPass('console.command')); $containerBuilder->addCompilerPass(new MessengerCommandToHandlerMapperPass()); $containerBuilder->addCompilerPass(new MessengerAlterTableListenerPass()); - $containerBuilder->addCompilerPass(new FailureReceiverPass()); }; diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript deleted file mode 100644 index c9112f7..0000000 --- a/Configuration/TypoScript/setup.typoscript +++ /dev/null @@ -1,4 +0,0 @@ -module.tx_dashboard.view { - templateRootPaths.110 = EXT:t3_messenger/Resources/Private/Templates - partialRootPaths.110 = EXT:t3_messenger/Resources/Private/Partials -} diff --git a/Resources/Private/Language/locallang.xlf b/Resources/Private/Language/locallang.xlf deleted file mode 100644 index f9a8e23..0000000 --- a/Resources/Private/Language/locallang.xlf +++ /dev/null @@ -1,63 +0,0 @@ - -> - -
- - - Recently failed messages - - - Shows a list of recently failed messages. - - - Message - - - Error Message - - - Redelivered - - - Retries - - - Actions - - - No messages found - - - Delete - - - Remove Message - - - Are you sure you want to remove this message? - - - Delete - - - Close - - - Retry - - - Retry Message - - - Are you sure you want to retry this message? - - - Retry - - - Close - - - - diff --git a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html b/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html deleted file mode 100644 index 8a66838..0000000 --- a/Resources/Private/Templates/Widget/ListOfFailedMessagesWidget.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - -
Id
- {failedMessage.messageId} - - - {failedMessage.shortMessageClass} - - - {failedMessage.errorMessage} - - {failedMessage.redelivered -> f:format.date(format: 'd.m.Y - H:i:s')} - - {failedMessage.retryCount} - - - - - - - -
-
-
- -
-
-

- -

-
-
-
-
-
- diff --git a/Resources/Public/Icons/failed-messages.svg b/Resources/Public/Icons/failed-messages.svg deleted file mode 100644 index 664d042..0000000 --- a/Resources/Public/Icons/failed-messages.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Resources/Public/JavaScript/DeleteFailedMessage.js b/Resources/Public/JavaScript/DeleteFailedMessage.js deleted file mode 100644 index d2ede62..0000000 --- a/Resources/Public/JavaScript/DeleteFailedMessage.js +++ /dev/null @@ -1,37 +0,0 @@ -define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest", "TYPO3/CMS/T3Messenger/RefreshFailedMessages"], function (require, Modal, Event, Severity, AjaxRequest, RefreshFailedMessages) { - "use strict"; - return new class { - constructor() { - this.selector = ".js-t3-messenger-remove-message", this.initialize() - } - - initialize() { - new Event("click", (function (e) { - e.preventDefault(); - var anchor = this; - Modal.confirm(this.dataset.modalTitle, this.dataset.modalQuestion, Severity.SeverityEnum.warning, [{ - text: this.dataset.modalCancel, - active: !0, - btnClass: "btn-default", - name: "cancel" - }, { - text: this.dataset.modalOk, - btnClass: "btn-warning", - name: "delete" - }]).on("button.clicked", function (e) { - if ("delete" === e.target.getAttribute("name")) { - var payload = {'id': anchor.dataset.messageId, 'transport': anchor.dataset.messageTransport}; - new AjaxRequest(TYPO3.settings.ajaxUrls.t3_messenger_failed_messages_delete) - .delete(JSON.stringify(payload)) - .then(async function () { - Modal.dismiss(); - RefreshFailedMessages.refresh(); - }); - } else { - Modal.dismiss(); - } - }) - })).delegateTo(document, this.selector) - } - } -}); diff --git a/Resources/Public/JavaScript/RefreshFailedMessages.js b/Resources/Public/JavaScript/RefreshFailedMessages.js deleted file mode 100644 index 77b4364..0000000 --- a/Resources/Public/JavaScript/RefreshFailedMessages.js +++ /dev/null @@ -1,13 +0,0 @@ -define(['require', "TYPO3/CMS/Dashboard/WidgetContentCollector"], function (require, DashboardWidget) { - "use strict"; - return new class { - constructor() { - - } - - refresh() { - const failedMessages = document.querySelector('[data-widget-key="failedMessages"]'); - DashboardWidget.getContentForWidget(failedMessages); - } - } -}); diff --git a/Resources/Public/JavaScript/RetryFailedMessage.js b/Resources/Public/JavaScript/RetryFailedMessage.js deleted file mode 100644 index 7d2ec85..0000000 --- a/Resources/Public/JavaScript/RetryFailedMessage.js +++ /dev/null @@ -1,37 +0,0 @@ -define(['require', 'TYPO3/CMS/Backend/Modal', "TYPO3/CMS/Core/Event/RegularEvent", "TYPO3/CMS/Backend/Enum/Severity", "TYPO3/CMS/Core/Ajax/AjaxRequest", "TYPO3/CMS/T3Messenger/RefreshFailedMessages"], function (require, Modal, Event, Severity, AjaxRequest, RefreshWidget) { - "use strict"; - return new class { - constructor() { - this.selector = ".js-t3-messenger-retry-message", this.initialize() - } - - initialize() { - new Event("click", (function (e) { - e.preventDefault(); - var anchor = this; - Modal.confirm(this.dataset.modalTitle, this.dataset.modalQuestion, Severity.SeverityEnum.warning, [{ - text: this.dataset.modalCancel, - active: !0, - btnClass: "btn-default", - name: "cancel" - }, { - text: this.dataset.modalOk, - btnClass: "btn-warning", - name: "retry" - }]).on("button.clicked", function (e) { - if ("retry" === e.target.getAttribute("name")) { - var payload = {'id': anchor.dataset.messageId, 'transport': anchor.dataset.messageTransport}; - new AjaxRequest(TYPO3.settings.ajaxUrls.t3_messenger_failed_messages_retry) - .post(JSON.stringify(payload)) - .then(async function () { - Modal.dismiss(); - RefreshWidget.refresh(); - }); - } else { - Modal.dismiss(); - } - }) - })).delegateTo(document, this.selector) - } - } -}); diff --git a/Tests/Functional/Repository/FailedMessageRepositoryTest.php b/Tests/Functional/Repository/FailedMessageRepositoryTest.php deleted file mode 100644 index 0ed7483..0000000 --- a/Tests/Functional/Repository/FailedMessageRepositoryTest.php +++ /dev/null @@ -1,87 +0,0 @@ -testExtensionsToLoad = [ - 'typo3conf/ext/typo3_psr_cache_adapter', - 'typo3conf/ext/t3_messenger', - 'typo3conf/ext/t3_messenger/Tests/Functional/Fixtures/Extensions/t3_messenger_test', - ]; - parent::setUp(); - $this->subject = $this->get(FailedMessageRepository::class); - $this->messageBus = $this->get(MessageBusInterface::class); - } - - public function test(): void - { - // Arrange - $this->messageBus->dispatch(new MyFailingCommand('Add to failed queue')); - $this->runWorker(); - $this->messageBus->dispatch(new MyOtherFailingCommand('Add to failed queue')); - $this->runWorker(); - - // Act - $failedMessages = $this->subject->list(); - - // Assert - $failedMessagesInAssertion = []; - foreach ($failedMessages as $failedMessage) { - $failedMessagesInAssertion[] = [ - 'class' => $failedMessage->getMessage(), - 'error_message' => $failedMessage->getErrorMessage(), - ]; - } - - self::assertCount(2, $failedMessages); - self::assertSame([ - [ - 'class' => MyOtherFailingCommand::class, - 'error_message' => 'Failing by intention', - ], - [ - 'class' => MyFailingCommand::class, - 'error_message' => 'Failing by intention', - ], - ], $failedMessagesInAssertion); - } - - private function runWorker(): void - { - $receivers = [ - 'async' => $this->get('messenger.transport.async'), - ]; - - /** @var EventDispatcher $eventDispatcher */ - $eventDispatcher = $this->get('event_dispatcher'); - $eventDispatcher->addSubscriber(new StopWorkerOnFailureLimitListener(1)); - - $worker = new Worker($receivers, $this->get('command.bus'), $eventDispatcher); - $worker->run(); - } -} diff --git a/composer.json b/composer.json index f790d1d..3579695 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,6 @@ "php": "^7.4 || ^8.0", "typo3/cms-core": "^10.4 || ^11.5 || ^12.4", "typo3/cms-extbase": "^10.4 || ^11.5 || ^12.4", - "typo3/cms-dashboard": "^10.4 || ^11.5 || ^12.4", "symfony/messenger": "^5.0 || ^6.2", "symfony/options-resolver": "^5.0 || ^6.2", "symfony/doctrine-messenger": "^5.0 || ^6.2", @@ -91,7 +90,7 @@ }, "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "2.0.x-dev" }, "typo3/cms": { "extension-key": "t3_messenger", diff --git a/ext_localconf.php b/ext_localconf.php index f1706df..a0b7b72 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -13,17 +13,4 @@ 'groups' => ['system'], ]; } - - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup( - "@import 'EXT:t3_messenger/Configuration/TypoScript/setup.typoscript'" - ); - - $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class); - $iconRegistry->registerIcon( - 'tx-messenger-failed-messages-icon', - \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, - [ - 'source' => 'EXT:t3_messenger/Resources/Public/Icons/failed-messages.svg', - ] - ); }); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 578c812..3dc866d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,19 +1,19 @@ parameters: ignoreErrors: - - message: "#^Parameter \\#2 \\$packageCache of static method TYPO3\\\\CMS\\\\Core\\\\Core\\\\Bootstrap\\:\\:createPackageManager\\(\\) expects TYPO3\\\\CMS\\\\Core\\\\Package\\\\Cache\\\\PackageCacheInterface, TYPO3\\\\CMS\\\\Core\\\\Cache\\\\Frontend\\\\FrontendInterface given\\.$#" + message: "#^Call to an undefined method ReflectionClass\\:\\:getAttributes\\(\\)\\.$#" count: 1 path: Classes/DependencyInjection/Compiler/T3MessengerPass.php - - message: "#^Parameter \\#1 \\$mailer of class Ssch\\\\T3Messenger\\\\Mailer\\\\Event\\\\AfterMailerSentMessageEvent constructor expects Ssch\\\\T3Messenger\\\\Mailer\\\\Contract\\\\MailerInterface, \\$this\\(Ssch\\\\T3Messenger\\\\Mailer\\\\MessengerMailer\\) given\\.$#" + message: "#^Parameter \\#2 \\$packageCache of static method TYPO3\\\\CMS\\\\Core\\\\Core\\\\Bootstrap\\:\\:createPackageManager\\(\\) expects TYPO3\\\\CMS\\\\Core\\\\Package\\\\Cache\\\\PackageCacheInterface, TYPO3\\\\CMS\\\\Core\\\\Cache\\\\Frontend\\\\FrontendInterface given\\.$#" count: 1 - path: Classes/Mailer/MessengerMailer.php + path: Classes/DependencyInjection/Compiler/T3MessengerPass.php - - message: "#^Parameter \\#1 \\$mailer of class Ssch\\\\T3Messenger\\\\Mailer\\\\Event\\\\BeforeMailerSentMessageEvent constructor expects Ssch\\\\T3Messenger\\\\Mailer\\\\Contract\\\\MailerInterface, \\$this\\(Ssch\\\\T3Messenger\\\\Mailer\\\\MessengerMailer\\) given\\.$#" - count: 1 - path: Classes/Mailer/MessengerMailer.php + message: "#^Class Doctrine\\\\DBAL\\\\Platforms\\\\PostgreSqlPlatform referenced with incorrect case\\: Doctrine\\\\DBAL\\\\Platforms\\\\PostgreSQLPlatform\\.$#" + count: 2 + path: Classes/Transport/DoctrineTransportFactory.php - message: "#^Call to function method_exists\\(\\) with Symfony\\\\Component\\\\EventDispatcher\\\\DependencyInjection\\\\RegisterListenersPass and 'setNoPreloadEvents' will always evaluate to true\\.$#" @@ -25,17 +25,7 @@ parameters: count: 1 path: Configuration/Services.php - - - message: "#^Class Symfony\\\\Component\\\\Messenger\\\\Bridge\\\\Amqp\\\\Transport\\\\AmqpTransportFactory not found\\.$#" - count: 1 - path: Configuration/Services.php - - message: "#^Class Symfony\\\\Component\\\\Messenger\\\\Bridge\\\\Beanstalkd\\\\Transport\\\\BeanstalkdTransportFactory not found\\.$#" count: 1 path: Configuration/Services.php - - - - message: "#^Class Symfony\\\\Component\\\\Messenger\\\\Bridge\\\\Redis\\\\Transport\\\\RedisTransportFactory not found\\.$#" - count: 1 - path: Configuration/Services.php From 085ca840cb50f820cecb0ae2601dee8c400e3793 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Fri, 8 Dec 2023 12:18:22 +0100 Subject: [PATCH 11/11] TASK: Fix branch alias --- composer.json | 2 +- phpstan.neon | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3579695..f473e66 100644 --- a/composer.json +++ b/composer.json @@ -90,7 +90,7 @@ }, "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "2.0.x-dev" }, "typo3/cms": { "extension-key": "t3_messenger", diff --git a/phpstan.neon b/phpstan.neon index 7c3a0b3..d03e35f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -19,4 +19,6 @@ parameters: ignoreErrors: - "#Used function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\abstract_arg not found#" - "#Function Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\Configurator\\\\abstract_arg not found#" + - "#Class Symfony\\\\Component\\\\Messenger\\\\Bridge\\\\Amqp\\\\Transport\\\\AmqpTransportFactory not found#" + - "#Class Symfony\\\\Component\\\\Messenger\\\\Bridge\\\\Redis\\\\Transport\\\\RedisTransportFactory not found#"