From bd9922622f9e1584696c787c8ef146d125d716d1 Mon Sep 17 00:00:00 2001 From: MrMicky Date: Tue, 18 Jul 2023 15:49:28 +0200 Subject: [PATCH] [Mailer] Add Scaleway bridge --- .../FrameworkExtension.php | 1 + .../Resources/config/mailer_transports.php | 5 + .../Mailer/Bridge/Scaleway/.gitattributes | 4 + .../Mailer/Bridge/Scaleway/.gitignore | 3 + .../Mailer/Bridge/Scaleway/CHANGELOG.md | 7 + .../Component/Mailer/Bridge/Scaleway/LICENSE | 19 +++ .../Mailer/Bridge/Scaleway/README.md | 26 ++++ .../Transport/ScalewayApiTransportTest.php | 108 +++++++++++++ .../ScalewayTransportFactoryTest.php | 97 ++++++++++++ .../Transport/ScalewayApiTransport.php | 147 ++++++++++++++++++ .../Transport/ScalewaySmtpTransport.php | 27 ++++ .../Transport/ScalewayTransportFactory.php | 48 ++++++ .../Mailer/Bridge/Scaleway/composer.json | 32 ++++ .../Mailer/Bridge/Scaleway/phpunit.xml.dist | 31 ++++ .../Exception/UnsupportedSchemeException.php | 4 + .../UnsupportedSchemeExceptionTest.php | 3 + src/Symfony/Component/Mailer/Transport.php | 2 + 17 files changed, 564 insertions(+) create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/.gitattributes create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/.gitignore create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/CHANGELOG.md create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/LICENSE create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/README.md create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayTransportFactoryTest.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewaySmtpTransport.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayTransportFactory.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json create mode 100644 src/Symfony/Component/Mailer/Bridge/Scaleway/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 56a4363aba5e5..9934e051468a9 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -2601,6 +2601,7 @@ private function registerMailerConfiguration(array $config, ContainerBuilder $co MailerBridge\Mailchimp\Transport\MandrillTransportFactory::class => 'mailer.transport_factory.mailchimp', MailerBridge\OhMySmtp\Transport\OhMySmtpTransportFactory::class => 'mailer.transport_factory.ohmysmtp', MailerBridge\Postmark\Transport\PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', + MailerBridge\Scaleway\Transport\ScalewayTransportFactory::class => 'mailer.transport_factory.scaleway', MailerBridge\Sendgrid\Transport\SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', MailerBridge\Sendinblue\Transport\SendinblueTransportFactory::class => 'mailer.transport_factory.sendinblue', MailerBridge\Amazon\Transport\SesTransportFactory::class => 'mailer.transport_factory.amazon', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php index fa2166e6f0878..ed6e644a56982 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php @@ -22,6 +22,7 @@ use Symfony\Component\Mailer\Bridge\MailPace\Transport\MailPaceTransportFactory; use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Transport\AbstractTransportFactory; @@ -89,6 +90,10 @@ ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.scaleway', ScalewayTransportFactory::class) + ->parent('mailer.transport_factory.abstract') + ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.sendmail', SendmailTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitattributes b/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitattributes new file mode 100644 index 0000000000000..84c7add058fb5 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitattributes @@ -0,0 +1,4 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitignore b/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitignore new file mode 100644 index 0000000000000..c49a5d8df5c65 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/CHANGELOG.md b/src/Symfony/Component/Mailer/Bridge/Scaleway/CHANGELOG.md new file mode 100644 index 0000000000000..6a69a2cbdb952 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +6.4 +----- + + * Add the bridge diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/LICENSE b/src/Symfony/Component/Mailer/Bridge/Scaleway/LICENSE new file mode 100644 index 0000000000000..3ed9f412ce53d --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2023-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/README.md b/src/Symfony/Component/Mailer/Bridge/Scaleway/README.md new file mode 100644 index 0000000000000..7aa78f37d005c --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/README.md @@ -0,0 +1,26 @@ +Scaleway Bridge +=============== + +Provides [Scaleway Transactional Email](https://www.scaleway.com/en/transactional-email-tem/) integration for Symfony Mailer. + +Configuration example: + +```env +# SMTP +MAILER_DSN=scaleway+smtp://PROJECT_ID:API_KEY@default + +# API +MAILER_DSN=scaleway+api://PROJECT_ID:API_KEY@default +``` + +where: + - `PROJECT_ID` is your Scaleway project ID + - `API_KEY` is your Scaleway API secret key + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php new file mode 100644 index 0000000000000..35c34870c83be --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayApiTransportTest.php @@ -0,0 +1,108 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Scaleway\Tests\Transport; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayApiTransport; +use Symfony\Component\Mailer\Exception\HttpTransportException; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Symfony\Contracts\HttpClient\ResponseInterface; + +class ScalewayApiTransportTest extends TestCase +{ + /** + * @dataProvider getTransportData + */ + public function testToString(ScalewayApiTransport $transport, string $expected) + { + $this->assertSame($expected, (string) $transport); + } + + public static function getTransportData() + { + return [ + [ + new ScalewayApiTransport('PROJECT_ID', 'TOKEN'), + 'scaleway+api://api.scaleway.com@PROJECT_ID', + ], + [ + new ScalewayApiTransport('PROJECT_ID', 'TOKEN', 'fr-par'), + 'scaleway+api://api.scaleway.com@PROJECT_ID?region=fr-par', + ], + [ + (new ScalewayApiTransport('PROJECT_ID', 'TOKEN'))->setHost('example.com'), + 'scaleway+api://example.com@PROJECT_ID', + ], + [ + (new ScalewayApiTransport('PROJECT_ID', 'TOKEN'))->setHost('example.com')->setPort(99), + 'scaleway+api://example.com:99@PROJECT_ID', + ], + ]; + } + + public function testSend() + { + $client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface { + $this->assertSame('POST', $method); + $this->assertSame('https://api.scaleway.com:8984/transactional-email/v1alpha1/regions/fr-par/emails', $url); + $this->assertStringContainsString('X-Auth-Token: TOKEN', $options['headers'][0] ?? $options['request_headers'][0]); + + $body = json_decode($options['body'], true); + $this->assertSame(['email' => 'fabpot@symfony.com', 'name' => 'Fabien'], $body['from']); + $this->assertSame(['email' => 'saif.gmati@symfony.com', 'name' => 'Saif Eddin'], $body['to'][0]); + $this->assertSame('Hello!', $body['subject']); + $this->assertSame('Hello There!', $body['text']); + + return new MockResponse(json_encode(['emails' => [['message_id' => 'foobar']]]), [ + 'http_code' => 200, + ]); + }); + $transport = new ScalewayApiTransport('PROJECT_ID', 'TOKEN', 'fr-par', $client); + $transport->setPort(8984); + + $mail = new Email(); + $mail->subject('Hello!') + ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) + ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->text('Hello There!'); + + $message = $transport->send($mail); + + $this->assertSame('foobar', $message->getMessageId()); + } + + public function testSendThrowsForErrorResponse() + { + $client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface { + return new MockResponse(json_encode(['message' => 'i\'m a teapot']), [ + 'http_code' => 418, + 'response_headers' => [ + 'content-type' => 'application/json', + ], + ]); + }); + $transport = new ScalewayApiTransport('PROJECT_ID', 'TOKEN', 'fr-par', $client); + + $mail = new Email(); + $mail->subject('Hello!') + ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) + ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->text('Hello There!'); + + $this->expectException(HttpTransportException::class); + $this->expectExceptionMessage('Unable to send an email: i\'m a teapot (code 418).'); + $transport->send($mail); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayTransportFactoryTest.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayTransportFactoryTest.php new file mode 100644 index 0000000000000..c7c691a7be24a --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Tests/Transport/ScalewayTransportFactoryTest.php @@ -0,0 +1,97 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Scaleway\Tests\Transport; + +use Psr\Log\NullLogger; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayApiTransport; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewaySmtpTransport; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; +use Symfony\Component\Mailer\Test\TransportFactoryTestCase; +use Symfony\Component\Mailer\Transport\Dsn; +use Symfony\Component\Mailer\Transport\TransportFactoryInterface; + +class ScalewayTransportFactoryTest extends TransportFactoryTestCase +{ + public function getFactory(): TransportFactoryInterface + { + return new ScalewayTransportFactory(null, new MockHttpClient(), new NullLogger()); + } + + public static function supportsProvider(): iterable + { + yield [ + new Dsn('scaleway+api', 'default'), + true, + ]; + + yield [ + new Dsn('scaleway', 'default'), + true, + ]; + + yield [ + new Dsn('scaleway+smtp', 'default'), + true, + ]; + + yield [ + new Dsn('scaleway+smtps', 'default'), + true, + ]; + + yield [ + new Dsn('scaleway+smtp', 'example.com'), + true, + ]; + } + + public static function createProvider(): iterable + { + $logger = new NullLogger(); + + yield [ + new Dsn('scaleway+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'fr-par']), + new ScalewayApiTransport(self::USER, self::PASSWORD, 'fr-par', new MockHttpClient(), null, $logger), + ]; + + yield [ + new Dsn('scaleway', 'default', self::USER, self::PASSWORD), + new ScalewayApiTransport(self::USER, self::PASSWORD, null, new MockHttpClient(), null, $logger), + ]; + + yield [ + new Dsn('scaleway+smtp', 'default', self::USER, self::PASSWORD), + new ScalewaySmtpTransport(self::USER, self::PASSWORD, null, $logger), + ]; + + yield [ + new Dsn('scaleway+smtps', 'default', self::USER, self::PASSWORD), + new ScalewaySmtpTransport(self::USER, self::PASSWORD, null, $logger), + ]; + } + + public static function unsupportedSchemeProvider(): iterable + { + yield [ + new Dsn('scaleway+foo', 'default', self::USER, self::PASSWORD), + 'The "scaleway+foo" scheme is not supported; supported schemes for mailer "scaleway" are: "scaleway", "scaleway+api", "scaleway+smtp", "scaleway+smtps".', + ]; + } + + public static function incompleteDsnProvider(): iterable + { + yield [new Dsn('scaleway+api', 'default', self::USER)]; + + yield [new Dsn('scaleway+api', 'default', null, self::PASSWORD)]; + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php new file mode 100644 index 0000000000000..8efe2a7bd1a79 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayApiTransport.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Scaleway\Transport; + +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Exception\HttpTransportException; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\AbstractApiTransport; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface; +use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +final class ScalewayApiTransport extends AbstractApiTransport +{ + private const HOST = 'api.scaleway.com'; + + private string $projectId; + private string $token; + private ?string $region; + + public function __construct(string $projectId, string $token, string $region = null, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + { + $this->projectId = $projectId; + $this->token = $token; + $this->region = $region; + + parent::__construct($client, $dispatcher, $logger); + } + + public function __toString(): string + { + $region = $this->region ? '?region='.$this->region : ''; + + return sprintf('scaleway+api://%s@%s%s', $this->getEndpoint(), $this->projectId, $region); + } + + protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface + { + $region = $this->region ?? 'fr-par'; + $path = sprintf('/transactional-email/v1alpha1/regions/%s/emails', $region); + + $response = $this->client->request('POST', 'https://'.$this->getEndpoint().$path, [ + 'json' => $this->getPayload($email, $envelope), + 'headers' => [ + 'X-Auth-Token' => $this->token, + ], + ]); + + try { + $statusCode = $response->getStatusCode(); + $result = $response->toArray(false); + } catch (DecodingExceptionInterface $e) { + throw new HttpTransportException('Unable to send an email: '.$response->getContent(false).sprintf(' (code %d).', $statusCode), $response); + } catch (TransportExceptionInterface $e) { + throw new HttpTransportException('Could not reach the remote Scaleway server.', $response, 0, $e); + } + + if (200 !== $statusCode) { + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $statusCode), $response); + } + + $sentMessage->setMessageId($result['emails'][0]['message_id']); + + return $response; + } + + private function getPayload(Email $email, Envelope $envelope): array + { + $payload = [ + 'from' => $this->formatAddress($envelope->getSender()), + 'to' => $this->formatAddresses($this->getRecipients($email, $envelope)), + 'subject' => $email->getSubject(), + 'project_id' => $this->projectId, + ]; + if ($emails = $email->getCc()) { + $payload['cc'] = $this->formatAddresses($emails); + } + if ($emails = $email->getBcc()) { + $payload['bcc'] = $this->formatAddresses($emails); + } + if ($email->getTextBody()) { + $payload['text'] = $email->getTextBody(); + } + if ($email->getHtmlBody()) { + $payload['html'] = $email->getHtmlBody(); + } + if ($attachements = $this->prepareAttachments($email)) { + $payload['attachment'] = $attachements; + } + + return $payload; + } + + private function prepareAttachments(Email $email): array + { + $attachments = []; + foreach ($email->getAttachments() as $attachment) { + $headers = $attachment->getPreparedHeaders(); + $filename = $headers->getHeaderParameter('Content-Disposition', 'filename'); + + $attachments[] = [ + 'name' => $filename, + 'type' => $headers->get('Content-Type')->getBody(), + 'content' => base64_encode($attachment->bodyToString()), + ]; + } + + return $attachments; + } + + private function formatAddress(Address $address): array + { + $array = ['email' => $address->getAddress()]; + + if ($address->getName()) { + $array['name'] = $address->getName(); + } + + return $array; + } + + protected function formatAddresses(array $addresses): array + { + return array_map(function (Address $address) { + return $this->formatAddress($address); + }, $addresses); + } + + private function getEndpoint(): ?string + { + return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : ''); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewaySmtpTransport.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewaySmtpTransport.php new file mode 100644 index 0000000000000..2d5f4c43cd2b1 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewaySmtpTransport.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\Mailer\Bridge\Scaleway\Transport; + +use Psr\EventDispatcher\EventDispatcherInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; + +final class ScalewaySmtpTransport extends EsmtpTransport +{ + public function __construct(string $projetId, string $token, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null) + { + parent::__construct('smtp.tem.scw.cloud', 465, true, $dispatcher, $logger); + + $this->setUsername($projetId); + $this->setPassword($token); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayTransportFactory.php b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayTransportFactory.php new file mode 100644 index 0000000000000..f4347b5d73cda --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/Transport/ScalewayTransportFactory.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Scaleway\Transport; + +use Symfony\Component\Mailer\Exception\UnsupportedSchemeException; +use Symfony\Component\Mailer\Transport\AbstractTransportFactory; +use Symfony\Component\Mailer\Transport\Dsn; +use Symfony\Component\Mailer\Transport\TransportInterface; + +final class ScalewayTransportFactory extends AbstractTransportFactory +{ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + $projectId = $this->getUser($dsn); + $token = $this->getPassword($dsn); + + if ('scaleway+api' === $scheme || 'scaleway' === $scheme) { + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + $region = $dsn->getOption('region'); + + return (new ScalewayApiTransport($projectId, $token, $region, $this->client, $this->dispatcher, $this->logger)) + ->setHost($host) + ->setPort($port); + } + + if ('scaleway+smtp' === $scheme || 'scaleway+smtps' === $scheme) { + return new ScalewaySmtpTransport($projectId, $token, $this->dispatcher, $this->logger); + } + + throw new UnsupportedSchemeException($dsn, 'scaleway', $this->getSupportedSchemes()); + } + + protected function getSupportedSchemes(): array + { + return ['scaleway', 'scaleway+api', 'scaleway+smtp', 'scaleway+smtps']; + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json b/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json new file mode 100644 index 0000000000000..bcb492bde62c1 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/scaleway-mailer", + "type": "symfony-mailer-bridge", + "description": "Symfony Scaleway Mailer Bridge", + "keywords": [], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=8.1", + "symfony/mailer": "^6.2.7|^7.0" + }, + "require-dev": { + "symfony/http-client": "^5.4|^6.0|^7.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Scaleway\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Mailer/Bridge/Scaleway/phpunit.xml.dist b/src/Symfony/Component/Mailer/Bridge/Scaleway/phpunit.xml.dist new file mode 100644 index 0000000000000..7600aa9b6c756 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Scaleway/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + + ./Resources + ./Tests + ./vendor + + + diff --git a/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php index e323fece5ca1e..52b201bd98d5c 100644 --- a/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php @@ -56,6 +56,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\Postmark\Transport\PostmarkTransportFactory::class, 'package' => 'symfony/postmark-mailer', ], + 'scaleway' => [ + 'class' => Bridge\Scaleway\Transport\ScalewayTransportFactory::class, + 'package' => 'symfony/scaleway-mailer', + ], 'sendgrid' => [ 'class' => Bridge\Sendgrid\Transport\SendgridTransportFactory::class, 'package' => 'symfony/sendgrid-mailer', diff --git a/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php b/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php index 30850429af9d2..1674c20160aa2 100644 --- a/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php +++ b/src/Symfony/Component/Mailer/Tests/Exception/UnsupportedSchemeExceptionTest.php @@ -23,6 +23,7 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Exception\UnsupportedSchemeException; @@ -46,6 +47,7 @@ public static function setUpBeforeClass(): void MandrillTransportFactory::class => false, OhMySmtpTransportFactory::class => false, PostmarkTransportFactory::class => false, + ScalewayTransportFactory::class => false, SendgridTransportFactory::class => false, SendinblueTransportFactory::class => false, SesTransportFactory::class => false, @@ -76,6 +78,7 @@ public static function messageWhereSchemeIsPartOfSchemeToPackageMapProvider(): \ yield ['mandrill', 'symfony/mailchimp-mailer']; yield ['ohmysmtp', 'symfony/oh-my-smtp-mailer']; yield ['postmark', 'symfony/postmark-mailer']; + yield ['scaleway', 'symfony/scaleway-mailer']; yield ['sendgrid', 'symfony/sendgrid-mailer']; yield ['sendinblue', 'symfony/sendinblue-mailer']; yield ['ses', 'symfony/amazon-mailer']; diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php index 07a34f40a7143..8543ebbea09d1 100644 --- a/src/Symfony/Component/Mailer/Transport.php +++ b/src/Symfony/Component/Mailer/Transport.php @@ -23,6 +23,7 @@ use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\OhMySmtp\Transport\OhMySmtpTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; +use Symfony\Component\Mailer\Bridge\Scaleway\Transport\ScalewayTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Exception\InvalidArgumentException; @@ -55,6 +56,7 @@ final class Transport MandrillTransportFactory::class, OhMySmtpTransportFactory::class, PostmarkTransportFactory::class, + ScalewayTransportFactory::class, SendgridTransportFactory::class, SendinblueTransportFactory::class, SesTransportFactory::class,