Skip to content

Commit 9c9d571

Browse files
committed
Merge branch '7.0' into 7.1
* 7.0: [Messenger] Don't drop stamps when message validation fails [WebProfilerBundle] Fix assignment to constant variable [Mailer] [Sendgrid] Use DataPart::getContentId() when DataPart::setContentId() is used.
2 parents eccdbea + 4a9d377 commit 9c9d571

File tree

11 files changed

+194
-15
lines changed

11 files changed

+194
-15
lines changed

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
}
7777
7878
tab.addEventListener('click', function(e) {
79-
const activeTab = e.target || e.srcElement;
79+
let activeTab = e.target || e.srcElement;
8080
8181
/* needed because when the tab contains HTML contents, user can click */
8282
/* on any of those elements instead of their parent '<button>' element */

src/Symfony/Component/Mailer/Bridge/Sendgrid/Tests/Transport/SendgridApiTransportTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,45 @@ public function testTagAndMetadataHeaders()
245245
$this->assertSame('blue', $payload['personalizations'][0]['custom_args']['Color']);
246246
$this->assertSame('12345', $payload['personalizations'][0]['custom_args']['Client-ID']);
247247
}
248+
249+
public function testInlineWithCustomContentId()
250+
{
251+
$imagePart = (new DataPart('text-contents', 'text.txt'));
252+
$imagePart->asInline();
253+
$imagePart->setContentId('content-identifier@symfony');
254+
255+
$email = new Email();
256+
$email->addPart($imagePart);
257+
$envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]);
258+
259+
$transport = new SendgridApiTransport('ACCESS_KEY');
260+
$method = new \ReflectionMethod(SendgridApiTransport::class, 'getPayload');
261+
$payload = $method->invoke($transport, $email, $envelope);
262+
263+
$this->assertArrayHasKey('attachments', $payload);
264+
$this->assertCount(1, $payload['attachments']);
265+
$this->assertArrayHasKey('content_id', $payload['attachments'][0]);
266+
267+
$this->assertSame('content-identifier@symfony', $payload['attachments'][0]['content_id']);
268+
}
269+
270+
public function testInlineWithoutCustomContentId()
271+
{
272+
$imagePart = (new DataPart('text-contents', 'text.txt'));
273+
$imagePart->asInline();
274+
275+
$email = new Email();
276+
$email->addPart($imagePart);
277+
$envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]);
278+
279+
$transport = new SendgridApiTransport('ACCESS_KEY');
280+
$method = new \ReflectionMethod(SendgridApiTransport::class, 'getPayload');
281+
$payload = $method->invoke($transport, $email, $envelope);
282+
283+
$this->assertArrayHasKey('attachments', $payload);
284+
$this->assertCount(1, $payload['attachments']);
285+
$this->assertArrayHasKey('content_id', $payload['attachments'][0]);
286+
287+
$this->assertSame('text.txt', $payload['attachments'][0]['content_id']);
288+
}
248289
}

src/Symfony/Component/Mailer/Bridge/Sendgrid/Transport/SendgridApiTransport.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ private function getAttachments(Email $email): array
179179
];
180180

181181
if ('inline' === $disposition) {
182-
$att['content_id'] = $filename;
182+
$att['content_id'] = $attachment->hasContentId() ? $attachment->getContentId() : $filename;
183183
}
184184

185185
$attachments[] = $att;

src/Symfony/Component/Messenger/Exception/DelayedMessageHandlingException.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@
1919
*
2020
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
2121
*/
22-
class DelayedMessageHandlingException extends RuntimeException implements WrappedExceptionsInterface
22+
class DelayedMessageHandlingException extends RuntimeException implements WrappedExceptionsInterface, EnvelopeAwareExceptionInterface
2323
{
24+
use EnvelopeAwareExceptionTrait;
2425
use WrappedExceptionsTrait;
2526

2627
public function __construct(
2728
private array $exceptions,
28-
private ?Envelope $envelope = null,
29+
?Envelope $envelope = null,
2930
) {
31+
$this->envelope = $envelope;
32+
3033
$exceptionMessages = implode(", \n", array_map(
3134
fn (\Throwable $e) => $e::class.': '.$e->getMessage(),
3235
$exceptions
@@ -40,9 +43,4 @@ public function __construct(
4043

4144
parent::__construct($message, 0, $exceptions[array_key_first($exceptions)]);
4245
}
43-
44-
public function getEnvelope(): ?Envelope
45-
{
46-
return $this->envelope;
47-
}
4846
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Exception;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
16+
/**
17+
* @internal
18+
*/
19+
interface EnvelopeAwareExceptionInterface
20+
{
21+
public function getEnvelope(): ?Envelope;
22+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Messenger\Exception;
13+
14+
use Symfony\Component\Messenger\Envelope;
15+
16+
/**
17+
* @internal
18+
*/
19+
trait EnvelopeAwareExceptionTrait
20+
{
21+
private ?Envelope $envelope = null;
22+
23+
public function getEnvelope(): ?Envelope
24+
{
25+
return $this->envelope;
26+
}
27+
}

src/Symfony/Component/Messenger/Exception/HandlerFailedException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
use Symfony\Component\Messenger\Envelope;
1515

16-
class HandlerFailedException extends RuntimeException implements WrappedExceptionsInterface
16+
class HandlerFailedException extends RuntimeException implements WrappedExceptionsInterface, EnvelopeAwareExceptionInterface
1717
{
1818
use WrappedExceptionsTrait;
1919

src/Symfony/Component/Messenger/Exception/ValidationFailedException.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,23 @@
1111

1212
namespace Symfony\Component\Messenger\Exception;
1313

14+
use Symfony\Component\Messenger\Envelope;
1415
use Symfony\Component\Validator\ConstraintViolationListInterface;
1516

1617
/**
1718
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
1819
*/
19-
class ValidationFailedException extends RuntimeException
20+
class ValidationFailedException extends RuntimeException implements EnvelopeAwareExceptionInterface
2021
{
22+
use EnvelopeAwareExceptionTrait;
23+
2124
public function __construct(
2225
private object $violatingMessage,
2326
private ConstraintViolationListInterface $violations,
27+
?Envelope $envelope = null,
2428
) {
29+
$this->envelope = $envelope;
30+
2531
parent::__construct(sprintf('Message of type "%s" failed validation.', $this->violatingMessage::class));
2632
}
2733

src/Symfony/Component/Messenger/Middleware/ValidationMiddleware.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function handle(Envelope $envelope, StackInterface $stack): Envelope
3737

3838
$violations = $this->validator->validate($message, null, $groups);
3939
if (\count($violations)) {
40-
throw new ValidationFailedException($message, $violations);
40+
throw new ValidationFailedException($message, $violations, $envelope);
4141
}
4242

4343
return $stack->next()->handle($envelope, $stack);

src/Symfony/Component/Messenger/Tests/FailureIntegrationTest.php

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use Symfony\Component\Messenger\EventListener\StopWorkerOnMessageLimitListener;
2525
use Symfony\Component\Messenger\Exception\DelayedMessageHandlingException;
2626
use Symfony\Component\Messenger\Exception\HandlerFailedException;
27+
use Symfony\Component\Messenger\Exception\ValidationFailedException;
2728
use Symfony\Component\Messenger\Handler\HandlerDescriptor;
2829
use Symfony\Component\Messenger\Handler\HandlersLocator;
2930
use Symfony\Component\Messenger\MessageBus;
@@ -32,6 +33,7 @@
3233
use Symfony\Component\Messenger\Middleware\FailedMessageProcessingMiddleware;
3334
use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware;
3435
use Symfony\Component\Messenger\Middleware\SendMessageMiddleware;
36+
use Symfony\Component\Messenger\Middleware\ValidationMiddleware;
3537
use Symfony\Component\Messenger\Retry\MultiplierRetryStrategy;
3638
use Symfony\Component\Messenger\Stamp\BusNameStamp;
3739
use Symfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;
@@ -42,6 +44,9 @@
4244
use Symfony\Component\Messenger\Transport\Sender\SenderInterface;
4345
use Symfony\Component\Messenger\Transport\Sender\SendersLocator;
4446
use Symfony\Component\Messenger\Worker;
47+
use Symfony\Component\Validator\ConstraintViolation;
48+
use Symfony\Component\Validator\ConstraintViolationList;
49+
use Symfony\Component\Validator\Validator\ValidatorInterface;
4550

4651
class FailureIntegrationTest extends TestCase
4752
{
@@ -419,6 +424,87 @@ public function testStampsAddedByMiddlewaresDontDisappearWhenDelayedMessageFails
419424
$this->assertCount(1, $messagesWaiting);
420425
$this->assertSame('some.bus', $messagesWaiting[0]->last(BusNameStamp::class)?->getBusName());
421426
}
427+
428+
public function testStampsAddedByMiddlewaresDontDisappearWhenValidationFails()
429+
{
430+
$transport1 = new DummyFailureTestSenderAndReceiver();
431+
432+
$transports = [
433+
'transport1' => $transport1,
434+
];
435+
436+
$locator = $this->createMock(ContainerInterface::class);
437+
$locator->expects($this->any())
438+
->method('has')
439+
->willReturn(true);
440+
$locator->expects($this->any())
441+
->method('get')
442+
->willReturnCallback(fn ($transportName) => $transports[$transportName]);
443+
$senderLocator = new SendersLocator([], $locator);
444+
445+
$retryStrategyLocator = $this->createMock(ContainerInterface::class);
446+
$retryStrategyLocator->expects($this->any())
447+
->method('has')
448+
->willReturn(true);
449+
$retryStrategyLocator->expects($this->any())
450+
->method('get')
451+
->willReturn(new MultiplierRetryStrategy(1));
452+
453+
$violationList = new ConstraintViolationList([new ConstraintViolation('validation failed', null, [], null, null, null)]);
454+
$validator = $this->createMock(ValidatorInterface::class);
455+
$validator->expects($this->once())->method('validate')->willReturn($violationList);
456+
457+
$middlewareStack = new \ArrayIterator([
458+
new AddBusNameStampMiddleware('some.bus'),
459+
new ValidationMiddleware($validator),
460+
new SendMessageMiddleware($senderLocator),
461+
]);
462+
463+
$bus = new MessageBus($middlewareStack);
464+
465+
$transport1Handler = fn () => $bus->dispatch(new \stdClass(), [new DispatchAfterCurrentBusStamp()]);
466+
467+
$handlerLocator = new HandlersLocator([
468+
DummyMessage::class => [new HandlerDescriptor($transport1Handler)],
469+
]);
470+
471+
$middlewareStack->append(new HandleMessageMiddleware($handlerLocator));
472+
473+
$dispatcher = new EventDispatcher();
474+
475+
$dispatcher->addSubscriber(new SendFailedMessageForRetryListener($locator, $retryStrategyLocator));
476+
$dispatcher->addSubscriber(new StopWorkerOnMessageLimitListener(1));
477+
478+
$runWorker = function (string $transportName) use ($transports, $bus, $dispatcher): ?\Throwable {
479+
$throwable = null;
480+
$failedListener = function (WorkerMessageFailedEvent $event) use (&$throwable) {
481+
$throwable = $event->getThrowable();
482+
};
483+
$dispatcher->addListener(WorkerMessageFailedEvent::class, $failedListener);
484+
485+
$worker = new Worker([$transportName => $transports[$transportName]], $bus, $dispatcher);
486+
487+
$worker->run();
488+
489+
$dispatcher->removeListener(WorkerMessageFailedEvent::class, $failedListener);
490+
491+
return $throwable;
492+
};
493+
494+
// Simulate receive from external source
495+
$transport1->send(new Envelope(new DummyMessage('API')));
496+
497+
// Receive the message from "transport1"
498+
$throwable = $runWorker('transport1');
499+
500+
$this->assertInstanceOf(ValidationFailedException::class, $throwable, $throwable->getMessage());
501+
502+
$messagesWaiting = $transport1->getMessagesWaitingToBeReceived();
503+
504+
// Stamps should not be dropped on message that's queued for retry
505+
$this->assertCount(1, $messagesWaiting);
506+
$this->assertSame('some.bus', $messagesWaiting[0]->last(BusNameStamp::class)?->getBusName());
507+
}
422508
}
423509

424510
class DummyFailureTestSenderAndReceiver implements ReceiverInterface, SenderInterface

0 commit comments

Comments
 (0)