forked from shopware/shopware
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'next-39778/auto-imported-from-github' into 'trunk'
NEXT-39778 - Improve mail sender using private fs over mq See merge request shopware/6/product/platform!15384
- Loading branch information
Showing
10 changed files
with
436 additions
and
75 deletions.
There are no files selected for viewing
10 changes: 10 additions & 0 deletions
10
changelog/_unreleased/2024-11-14-improve-mail-sender-fs.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
--- | ||
title: Improve mail sender by using the private file system over message queue | ||
issue: NEXT-00000 | ||
author: Benjamin Wittwer | ||
author_email: [email protected] | ||
author_github: akf-bw | ||
--- | ||
# Core | ||
* Deprecated the `envelope` parameter in `Shopware\Core\Content\Mail\Service\AbstractMailSender::send` | ||
* Changed `\Shopware\Core\Content\Mail\Service\MailSender` to write the serialized mail to the private file system and dispatch a `\Shopware\Core\Content\Mail\Message\SendMailMessage` to the message bus instead of directly sending the mail to the Symfony mailer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Content\Mail\Message; | ||
|
||
use League\Flysystem\FilesystemException; | ||
use League\Flysystem\FilesystemOperator; | ||
use Psr\Log\LoggerInterface; | ||
use Shopware\Core\Framework\Log\Package; | ||
use Symfony\Component\Mailer\Exception\TransportExceptionInterface; | ||
use Symfony\Component\Mailer\Transport\TransportInterface; | ||
use Symfony\Component\Messenger\Attribute\AsMessageHandler; | ||
use Symfony\Component\Mime\Email; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
#[AsMessageHandler(handles: SendMailMessage::class)] | ||
#[Package('services-settings')] | ||
final class SendMailHandler | ||
{ | ||
/** | ||
* @internal | ||
*/ | ||
public function __construct( | ||
private readonly TransportInterface $transport, | ||
private readonly FilesystemOperator $filesystem, | ||
private readonly LoggerInterface $logger | ||
) { | ||
} | ||
|
||
/** | ||
* @throws TransportExceptionInterface | ||
* @throws FilesystemException | ||
*/ | ||
public function __invoke(SendMailMessage $message): void | ||
{ | ||
$mailDataPath = $message->mailDataPath; | ||
try { | ||
$mailData = $this->filesystem->read($mailDataPath); | ||
} catch (FilesystemException $e) { | ||
if (!$this->filesystem->fileExists($mailDataPath)) { | ||
$this->logger->error('The mail data file does not exist. Mail could not be sent.', ['mailDataPath' => $mailDataPath, 'exception' => $e->getMessage()]); | ||
|
||
return; | ||
} | ||
|
||
throw $e; | ||
} | ||
|
||
$mail = unserialize($mailData); | ||
if (!is_a($mail, Email::class)) { | ||
$this->logger->error('The mail data file does not contain a valid email object. Mail could not be sent.', ['mailDataPath' => $mailDataPath]); | ||
|
||
return; | ||
} | ||
|
||
$this->transport->send($mail); | ||
$this->cleanup($message); | ||
} | ||
|
||
private function cleanup(SendMailMessage $message): void | ||
{ | ||
$mailDataPath = $message->mailDataPath; | ||
|
||
try { | ||
$this->filesystem->delete($mailDataPath); | ||
} catch (FilesystemException $e) { | ||
$this->logger->error('Could not delete mail data file after sending mail.', ['mailDataPath' => $mailDataPath, 'exception' => $e->getMessage()]); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Core\Content\Mail\Message; | ||
|
||
use Shopware\Core\Framework\Log\Package; | ||
use Shopware\Core\Framework\MessageQueue\AsyncMessageInterface; | ||
|
||
/** | ||
* @codeCoverageIgnore | ||
*/ | ||
#[Package('services-settings')] | ||
class SendMailMessage implements AsyncMessageInterface | ||
{ | ||
/** | ||
* @internal | ||
*/ | ||
public function __construct(public readonly string $mailDataPath) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
tests/integration/Core/Content/Mail/Service/EmailSenderTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
<?php declare(strict_types=1); | ||
|
||
namespace Shopware\Tests\Integration\Core\Content\Mail\Service; | ||
|
||
use League\Flysystem\FilesystemOperator; | ||
use PHPUnit\Framework\TestCase; | ||
use Shopware\Core\Content\Mail\Service\MailFactory; | ||
use Shopware\Core\Content\Mail\Service\MailSender; | ||
use Shopware\Core\Framework\Test\TestCaseBase\KernelLifecycleManager; | ||
use Shopware\Core\Framework\Test\TestCaseBase\KernelTestBehaviour; | ||
use Shopware\Core\Framework\Test\TestCaseBase\QueueTestBehaviour; | ||
use Shopware\Core\Framework\Util\Hasher; | ||
use Symfony\Component\Mailer\Transport\TransportInterface; | ||
use Symfony\Component\Mime\Email; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
class EmailSenderTest extends TestCase | ||
{ | ||
use KernelTestBehaviour; | ||
use QueueTestBehaviour; | ||
|
||
public function testSendEmail(): void | ||
{ | ||
try { | ||
$this->doRunTest(); | ||
} finally { | ||
// test updates container state, reset everything. | ||
KernelLifecycleManager::ensureKernelShutdown(); | ||
} | ||
} | ||
|
||
private function doRunTest(): void | ||
{ | ||
// other tests might have already booted the kernel... | ||
KernelLifecycleManager::ensureKernelShutdown(); | ||
$container = static::getContainer(); | ||
$transport = $this->createMock(TransportInterface::class); | ||
$container->set('mailer.transports', $transport); | ||
$mailFactory = $container->get(MailFactory::class); | ||
static::assertInstanceOf(MailFactory::class, $mailFactory); | ||
$filesystem = $container->get('shopware.filesystem.private'); | ||
static::assertInstanceOf(FilesystemOperator::class, $filesystem); | ||
|
||
$subject = 'mail create test'; | ||
$sender = ['[email protected]' => 'Sales Channel']; | ||
$recipients = ['[email protected]' => 'Receiver name', '[email protected]' => null]; | ||
$contents = ['text/html' => 'Message']; | ||
$attachments = ['test']; | ||
|
||
$additionalData = [ | ||
'recipientsCc' => '[email protected]', | ||
'recipientsBcc' => [ | ||
'[email protected]' => 'bccMailRecipient1', | ||
'[email protected]' => 'bccMailRecipient2', | ||
], | ||
]; | ||
$binAttachments = [['content' => 'Content', 'fileName' => 'content.txt', 'mimeType' => 'application/txt']]; | ||
|
||
$mail = $mailFactory->create( | ||
$subject, | ||
$sender, | ||
$recipients, | ||
$contents, | ||
$attachments, | ||
$additionalData, | ||
$binAttachments | ||
); | ||
|
||
$mailSender = $container->get(MailSender::class); | ||
$serializedMail = serialize($mail); | ||
$expectedMailPath = 'mail-data/' . Hasher::hash($serializedMail); | ||
$transport->expects(static::once()) | ||
->method('send') | ||
->with( | ||
static::callback( | ||
fn (Email $email) => $email->getSubject() === $mail->getSubject() && $email->getHtmlBody() === $mail->getHtmlBody() | ||
) | ||
); | ||
|
||
$mailSender->send($mail); | ||
static::assertSame($serializedMail, $filesystem->read($expectedMailPath)); | ||
|
||
$this->runWorker(); | ||
static::assertFalse($filesystem->fileExists($expectedMailPath)); | ||
} | ||
} |
Oops, something went wrong.