diff --git a/src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php b/src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php index 3821d01e3fb19..ea785bfefcb09 100644 --- a/src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php +++ b/src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php @@ -19,12 +19,12 @@ * * @author Tobias Nyholm */ -class DelayedMessageHandlingException extends RuntimeException implements WrappedExceptionsInterface +class DelayedMessageHandlingException extends RuntimeException implements WrappedExceptionsInterface, EnvelopeAwareExceptionInterface { + use EnvelopeAwareExceptionTrait; use WrappedExceptionsTrait; private array $exceptions; - private ?Envelope $envelope; public function __construct(array $exceptions, ?Envelope $envelope = null) { @@ -55,9 +55,4 @@ public function getExceptions(): array return $this->exceptions; } - - public function getEnvelope(): ?Envelope - { - return $this->envelope; - } } diff --git a/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionInterface.php b/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionInterface.php new file mode 100644 index 0000000000000..b57aa034e44ff --- /dev/null +++ b/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionInterface.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Exception; + +use Symfony\Component\Messenger\Envelope; + +/** + * @internal + */ +interface EnvelopeAwareExceptionInterface +{ + public function getEnvelope(): ?Envelope; +} diff --git a/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionTrait.php b/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionTrait.php new file mode 100644 index 0000000000000..3bb7676b86e69 --- /dev/null +++ b/src/Symfony/Component/Messenger/Exception/EnvelopeAwareExceptionTrait.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Messenger\Exception; + +use Symfony\Component\Messenger\Envelope; + +/** + * @internal + */ +trait EnvelopeAwareExceptionTrait +{ + private ?Envelope $envelope = null; + + public function getEnvelope(): ?Envelope + { + return $this->envelope; + } +} diff --git a/src/Symfony/Component/Messenger/Exception/HandlerFailedException.php b/src/Symfony/Component/Messenger/Exception/HandlerFailedException.php index f854238ec59ec..ed1699583d47e 100644 --- a/src/Symfony/Component/Messenger/Exception/HandlerFailedException.php +++ b/src/Symfony/Component/Messenger/Exception/HandlerFailedException.php @@ -13,7 +13,7 @@ use Symfony\Component\Messenger\Envelope; -class HandlerFailedException extends RuntimeException implements WrappedExceptionsInterface +class HandlerFailedException extends RuntimeException implements WrappedExceptionsInterface, EnvelopeAwareExceptionInterface { use WrappedExceptionsTrait; diff --git a/src/Symfony/Component/Messenger/Exception/ValidationFailedException.php b/src/Symfony/Component/Messenger/Exception/ValidationFailedException.php index 0e91372f62d7e..0a8655e5c3eb5 100644 --- a/src/Symfony/Component/Messenger/Exception/ValidationFailedException.php +++ b/src/Symfony/Component/Messenger/Exception/ValidationFailedException.php @@ -11,20 +11,24 @@ namespace Symfony\Component\Messenger\Exception; +use Symfony\Component\Messenger\Envelope; use Symfony\Component\Validator\ConstraintViolationListInterface; /** * @author Tobias Nyholm */ -class ValidationFailedException extends RuntimeException +class ValidationFailedException extends RuntimeException implements EnvelopeAwareExceptionInterface { + use EnvelopeAwareExceptionTrait; + private ConstraintViolationListInterface $violations; private object $violatingMessage; - public function __construct(object $violatingMessage, ConstraintViolationListInterface $violations) + public function __construct(object $violatingMessage, ConstraintViolationListInterface $violations, ?Envelope $envelope = null) { $this->violatingMessage = $violatingMessage; $this->violations = $violations; + $this->envelope = $envelope; parent::__construct(sprintf('Message of type "%s" failed validation.', $this->violatingMessage::class)); } diff --git a/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php b/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php index 851ef295eb3d0..bbfe7e02962c4 100644 --- a/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php +++ b/src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php @@ -39,7 +39,7 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope $violations = $this->validator->validate($message, null, $groups); if (\count($violations)) { - throw new ValidationFailedException($message, $violations); + throw new ValidationFailedException($message, $violations, $envelope); } return $stack->next()->handle($envelope, $stack); diff --git a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php index 654cae4b93ae1..d711097ee21d4 100644 --- a/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php +++ b/src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php @@ -24,6 +24,7 @@ use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener; use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException; use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\Exception\ValidationFailedException; use Symfony\Component\Messenger\Handler\HandlerDescriptor; use Symfony\Component\Messenger\Handler\HandlersLocator; use Symfony\Component\Messenger\MessageBus; @@ -32,6 +33,7 @@ use Symfony\Component\Messenger\Middleware\FailedMessageProcessingMiddleware; use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; use Symfony\Component\Messenger\Middleware\SendMessageMiddleware; +use Symfony\Component\Messenger\Middleware\ValidationMiddleware; use Symfony\Component\Messenger\Retry\MultiplierRetryStrategy; use Symfony\Component\Messenger\Stamp\BusNameStamp; use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp; @@ -42,6 +44,9 @@ use Symfony\Component\Messenger\Transport\Sender\SenderInterface; use Symfony\Component\Messenger\Transport\Sender\SendersLocator; use Symfony\Component\Messenger\Worker; +use Symfony\Component\Validator\ConstraintViolation; +use Symfony\Component\Validator\ConstraintViolationList; +use Symfony\Component\Validator\Validator\ValidatorInterface; class FailureIntegrationTest extends TestCase { @@ -440,6 +445,87 @@ public function testStampsAddedByMiddlewaresDontDisappearWhenDelayedMessageFails $this->assertCount(1, $messagesWaiting); $this->assertSame('some.bus', $messagesWaiting[0]->last(BusNameStamp::class)?->getBusName()); } + + public function testStampsAddedByMiddlewaresDontDisappearWhenValidationFails() + { + $transport1 = new DummyFailureTestSenderAndReceiver(); + + $transports = [ + 'transport1' => $transport1, + ]; + + $locator = $this->createMock(ContainerInterface::class); + $locator->expects($this->any()) + ->method('has') + ->willReturn(true); + $locator->expects($this->any()) + ->method('get') + ->willReturnCallback(fn ($transportName) => $transports[$transportName]); + $senderLocator = new SendersLocator([], $locator); + + $retryStrategyLocator = $this->createMock(ContainerInterface::class); + $retryStrategyLocator->expects($this->any()) + ->method('has') + ->willReturn(true); + $retryStrategyLocator->expects($this->any()) + ->method('get') + ->willReturn(new MultiplierRetryStrategy(1)); + + $violationList = new ConstraintViolationList([new ConstraintViolation('validation failed', null, [], null, null, null)]); + $validator = $this->createMock(ValidatorInterface::class); + $validator->expects($this->once())->method('validate')->willReturn($violationList); + + $middlewareStack = new \ArrayIterator([ + new AddBusNameStampMiddleware('some.bus'), + new ValidationMiddleware($validator), + new SendMessageMiddleware($senderLocator), + ]); + + $bus = new MessageBus($middlewareStack); + + $transport1Handler = fn () => $bus->dispatch(new \stdClass(), [new DispatchAfterCurrentBusStamp()]); + + $handlerLocator = new HandlersLocator([ + DummyMessage::class => [new HandlerDescriptor($transport1Handler)], + ]); + + $middlewareStack->append(new HandleMessageMiddleware($handlerLocator)); + + $dispatcher = new EventDispatcher(); + + $dispatcher->addSubscriber(new SendFailedMessageForRetryListener($locator, $retryStrategyLocator)); + $dispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1)); + + $runWorker = function (string $transportName) use ($transports, $bus, $dispatcher): ?\Throwable { + $throwable = null; + $failedListener = function (WorkerMessageFailedEvent $event) use (&$throwable) { + $throwable = $event->getThrowable(); + }; + $dispatcher->addListener(WorkerMessageFailedEvent::class, $failedListener); + + $worker = new Worker([$transportName => $transports[$transportName]], $bus, $dispatcher); + + $worker->run(); + + $dispatcher->removeListener(WorkerMessageFailedEvent::class, $failedListener); + + return $throwable; + }; + + // Simulate receive from external source + $transport1->send(new Envelope(new DummyMessage('API'))); + + // Receive the message from "transport1" + $throwable = $runWorker('transport1'); + + $this->assertInstanceOf(ValidationFailedException::class, $throwable, $throwable->getMessage()); + + $messagesWaiting = $transport1->getMessagesWaitingToBeReceived(); + + // Stamps should not be dropped on message that's queued for retry + $this->assertCount(1, $messagesWaiting); + $this->assertSame('some.bus', $messagesWaiting[0]->last(BusNameStamp::class)?->getBusName()); + } } class DummyFailureTestSenderAndReceiver implements ReceiverInterface, SenderInterface diff --git a/src/Symfony/Component/Messenger/Worker.php b/src/Symfony/Component/Messenger/Worker.php index 7e25f7de1b54f..3d69dd6135190 100644 --- a/src/Symfony/Component/Messenger/Worker.php +++ b/src/Symfony/Component/Messenger/Worker.php @@ -22,8 +22,7 @@ use Symfony\Component\Messenger\Event\WorkerRunningEvent; use Symfony\Component\Messenger\Event\WorkerStartedEvent; use Symfony\Component\Messenger\Event\WorkerStoppedEvent; -use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException; -use Symfony\Component\Messenger\Exception\HandlerFailedException; +use Symfony\Component\Messenger\Exception\EnvelopeAwareExceptionInterface; use Symfony\Component\Messenger\Exception\RejectRedeliveredMessageException; use Symfony\Component\Messenger\Exception\RuntimeException; use Symfony\Component\Messenger\Stamp\AckStamp; @@ -189,7 +188,7 @@ private function ack(): bool $receiver->reject($envelope); } - if ($e instanceof HandlerFailedException || ($e instanceof DelayedMessageHandlingException && null !== $e->getEnvelope())) { + if ($e instanceof EnvelopeAwareExceptionInterface && null !== $e->getEnvelope()) { $envelope = $e->getEnvelope(); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy