From 836a20350bede027c28cd727099fa30441488854 Mon Sep 17 00:00:00 2001 From: drixs6o9 Date: Wed, 23 Sep 2020 13:58:57 +0200 Subject: [PATCH] [Mailer] Added Sendinblue bridge --- .../FrameworkExtension.php | 2 + .../Resources/config/mailer_transports.php | 5 + .../Mailer/Bridge/Sendinblue/.gitattributes | 4 + .../Mailer/Bridge/Sendinblue/.gitignore | 3 + .../Mailer/Bridge/Sendinblue/CHANGELOG.md | 7 + .../Mailer/Bridge/Sendinblue/LICENSE | 19 ++ .../Mailer/Bridge/Sendinblue/README.md | 53 ++++++ .../Transport/SendinblueApiTransportTest.php | 142 ++++++++++++++ .../SendinblueTransportFactoryTest.php | 81 ++++++++ .../Transport/SendinblueApiTransport.php | 175 ++++++++++++++++++ .../Transport/SendinblueSmtpTransport.php | 30 +++ .../Transport/SendinblueTransportFactory.php | 50 +++++ .../Mailer/Bridge/Sendinblue/composer.json | 37 ++++ .../Mailer/Bridge/Sendinblue/phpunit.xml.dist | 31 ++++ .../Exception/UnsupportedSchemeException.php | 4 + src/Symfony/Component/Mailer/Transport.php | 2 + 16 files changed, 645 insertions(+) create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitattributes create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitignore create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/CHANGELOG.md create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/README.md create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueApiTransportTest.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueTransportFactoryTest.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueApiTransport.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueSmtpTransport.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueTransportFactory.php create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/composer.json create mode 100644 src/Symfony/Component/Mailer/Bridge/Sendinblue/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index e0c3dd1b9f..d865a990b3 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -83,6 +83,7 @@ use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; +use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Mailer; use Symfony\Component\Messenger\Bridge\AmazonSqs\Transport\AmazonSqsTransportFactory; use Symfony\Component\Messenger\Bridge\Amqp\Transport\AmqpTransportFactory; @@ -2128,6 +2129,7 @@ class FrameworkExtension extends Extension MailgunTransportFactory::class => 'mailer.transport_factory.mailgun', PostmarkTransportFactory::class => 'mailer.transport_factory.postmark', SendgridTransportFactory::class => 'mailer.transport_factory.sendgrid', + SendinblueTransportFactory::class => 'mailer.transport_factory.sendinblue', ]; foreach ($classToServices as $class => $service) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php index 408d2f47f0..3c62996fdb 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/mailer_transports.php @@ -17,6 +17,7 @@ use Symfony\Component\Mailer\Bridge\Mailchimp\Transport\MandrillTransportFactory use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; +use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Transport\AbstractTransportFactory; use Symfony\Component\Mailer\Transport\NativeTransportFactory; use Symfony\Component\Mailer\Transport\NullTransportFactory; @@ -65,6 +66,10 @@ return static function (ContainerConfigurator $container) { ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.sendinblue', SendinblueTransportFactory::class) + ->parent('mailer.transport_factory.abstract') + ->tag('mailer.transport_factory') + ->set('mailer.transport_factory.smtp', EsmtpTransportFactory::class) ->parent('mailer.transport_factory.abstract') ->tag('mailer.transport_factory', ['priority' => -100]) diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitattributes b/src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitattributes new file mode 100644 index 0000000000..84c7add058 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/.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/Sendinblue/.gitignore b/src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitignore new file mode 100644 index 0000000000..c49a5d8df5 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/CHANGELOG.md b/src/Symfony/Component/Mailer/Bridge/Sendinblue/CHANGELOG.md new file mode 100644 index 0000000000..0d994e934e --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.2.0 +----- + + * Added the bridge diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE new file mode 100644 index 0000000000..4bf0fef4ff --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-2020 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/Sendinblue/README.md b/src/Symfony/Component/Mailer/Bridge/Sendinblue/README.md new file mode 100644 index 0000000000..808fd06b73 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/README.md @@ -0,0 +1,53 @@ +Sendinblue Bridge +================= + +Provides Sendinblue integration for Symfony Mailer. + + +Configuration example: + +```env +# API +MAILER_DSN=sendinblue+api://$SENDINBLUE_API_KEY@default + +# SMTP +MAILER_DSN=sendinblue+smtp://$SENDINBLUE_USERNAME:$SENDINBLUE_PASSWORD@default +``` + +With API, you can use custom headers. + +```php +$params = ['param1' => 'foo', 'param2' => 'bar']; +$json = json_encode(['"custom_header_1' => 'custom_value_1']); + +$email = new Email(); +$email + ->getHeaders() + ->add(new MetadataHeader('custom', $json)) + ->add(new TagHeader('TagInHeaders1')) + ->add(new TagHeader('TagInHeaders2')) + ->addTextHeader('sender.ip', '1.2.3.4') + ->addTextHeader('templateId', 1) + ->addParameterizedHeader('params', 'params', $params) + ->addTextHeader('foo', 'bar') +; +``` + +This example allow you to set : + +* templateId +* params +* tags +* headers + * sender.ip + * X-Mailin-Custom + +For more informations, you can refer to [Sendinblue API documentation](https://developers.sendinblue.com/reference#sendtransacemail). + +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/Sendinblue/Tests/Transport/SendinblueApiTransportTest.php b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueApiTransportTest.php new file mode 100644 index 0000000000..dfb8457c96 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueApiTransportTest.php @@ -0,0 +1,142 @@ +assertSame($expected, (string) $transport); + } + + public function getTransportData() + { + yield [ + new SendinblueApiTransport('ACCESS_KEY'), + 'sendinblue+api://api.sendinblue.com', + ]; + + yield [ + (new SendinblueApiTransport('ACCESS_KEY'))->setHost('example.com'), + 'sendinblue+api://example.com', + ]; + + yield [ + (new SendinblueApiTransport('ACCESS_KEY'))->setHost('example.com')->setPort(99), + 'sendinblue+api://example.com:99', + ]; + } + + public function testCustomHeader() + { + $params = ['param1' => 'foo', 'param2' => 'bar']; + $json = json_encode(['"custom_header_1' => 'custom_value_1']); + + $email = new Email(); + $email->getHeaders() + ->add(new MetadataHeader('custom', $json)) + ->add(new TagHeader('TagInHeaders')) + ->addTextHeader('templateId', 1) + ->addParameterizedHeader('params', 'params', $params) + ->addTextHeader('foo', 'bar') + ; + $envelope = new Envelope(new Address('alice@system.com', 'Alice'), [new Address('bob@system.com', 'Bob')]); + + $transport = new SendinblueApiTransport('ACCESS_KEY'); + $method = new \ReflectionMethod(SendinblueApiTransport::class, 'getPayload'); + $method->setAccessible(true); + $payload = $method->invoke($transport, $email, $envelope); + + $this->assertArrayHasKey('X-Mailin-Custom', $payload['headers']); + $this->assertEquals($json, $payload['headers']['X-Mailin-Custom']); + + $this->assertArrayHasKey('tags', $payload); + $this->assertEquals('TagInHeaders', current($payload['tags'])); + $this->assertArrayHasKey('templateId', $payload); + $this->assertEquals(1, $payload['templateId']); + $this->assertArrayHasKey('params', $payload); + $this->assertEquals('foo', $payload['params']['param1']); + $this->assertEquals('bar', $payload['params']['param2']); + $this->assertArrayHasKey('foo', $payload['headers']); + $this->assertEquals('bar', $payload['headers']['foo']); + } + + public function testSendThrowsForErrorResponse() + { + $client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface { + $this->assertSame('POST', $method); + $this->assertSame('https://api.sendinblue.com:8984/v3/smtp/email', $url); + $this->assertStringContainsString('Accept: */*', $options['headers'][2] ?? $options['request_headers'][1]); + + return new MockResponse(json_encode(['message' => 'i\'m a teapot']), [ + 'http_code' => 418, + 'response_headers' => [ + 'content-type' => 'application/json', + ], + ]); + }); + + $transport = new SendinblueApiTransport('ACCESS_KEY', $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!') + ; + + $this->expectException(HttpTransportException::class); + $this->expectExceptionMessage('Unable to send an email: i\'m a teapot (code 418).'); + $transport->send($mail); + } + + public function testSend() + { + $client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface { + $this->assertSame('POST', $method); + $this->assertSame('https://api.sendinblue.com:8984/v3/smtp/email', $url); + $this->assertStringContainsString('Accept: */*', $options['headers'][2] ?? $options['request_headers'][1]); + + return new MockResponse(json_encode(['messageId' => 'foobar']), [ + 'http_code' => 201, + ]); + }); + + $transport = new SendinblueApiTransport('ACCESS_KEY', $client); + $transport->setPort(8984); + + $dataPart = new DataPart('body'); + $mail = new Email(); + $mail->subject('Hello!') + ->to(new Address('saif.gmati@symfony.com', 'Saif Eddin')) + ->from(new Address('fabpot@symfony.com', 'Fabien')) + ->text('Hello here!') + ->html('Hello there!') + ->addCc('foo@bar.fr') + ->addBcc('foo@bar.fr') + ->addReplyTo('foo@bar.fr') + ->attachPart($dataPart) + ; + + $message = $transport->send($mail); + + $this->assertSame('foobar', $message->getMessageId()); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueTransportFactoryTest.php b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueTransportFactoryTest.php new file mode 100644 index 0000000000..fc7e9d2b83 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Tests/Transport/SendinblueTransportFactoryTest.php @@ -0,0 +1,81 @@ +getDispatcher(), $this->getClient(), $this->getLogger()); + } + + public function supportsProvider(): iterable + { + yield [ + new Dsn('sendinblue', 'default'), + true, + ]; + + yield [ + new Dsn('sendinblue+smtp', 'default'), + true, + ]; + + yield [ + new Dsn('sendinblue+smtp', 'example.com'), + true, + ]; + + yield [ + new Dsn('sendinblue+api', 'default'), + true, + ]; + } + + public function createProvider(): iterable + { + yield [ + new Dsn('sendinblue', 'default', self::USER, self::PASSWORD), + new SendinblueSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()), + ]; + + yield [ + new Dsn('sendinblue+smtp', 'default', self::USER, self::PASSWORD), + new SendinblueSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()), + ]; + + yield [ + new Dsn('sendinblue+smtp', 'default', self::USER, self::PASSWORD, 465), + new SendinblueSmtpTransport(self::USER, self::PASSWORD, $this->getDispatcher(), $this->getLogger()), + ]; + + yield [ + new Dsn('sendinblue+api', 'default', self::USER), + new SendinblueApiTransport(self::USER, $this->getClient(), $this->getDispatcher(), $this->getLogger()), + ]; + } + + public function unsupportedSchemeProvider(): iterable + { + yield [ + new Dsn('sendinblue+foo', 'default', self::USER, self::PASSWORD), + 'The "sendinblue+foo" scheme is not supported; supported schemes for mailer "sendinblue" are: "sendinblue", "sendinblue+smtp", "sendinblue+api".', + ]; + } + + public function incompleteDsnProvider(): iterable + { + yield [new Dsn('sendinblue+smtp', 'default', self::USER)]; + + yield [new Dsn('sendinblue+smtp', 'default', null, self::PASSWORD)]; + + yield [new Dsn('sendinblue+api', 'default')]; + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueApiTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueApiTransport.php new file mode 100644 index 0000000000..7a8c975d60 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueApiTransport.php @@ -0,0 +1,175 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Sendinblue\Transport; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Mailer\Envelope; +use Symfony\Component\Mailer\Exception\HttpTransportException; +use Symfony\Component\Mailer\Header\MetadataHeader; +use Symfony\Component\Mailer\Header\TagHeader; +use Symfony\Component\Mailer\SentMessage; +use Symfony\Component\Mailer\Transport\AbstractApiTransport; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; +use Symfony\Component\Mime\Header\Headers; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; +use Symfony\Contracts\HttpClient\ResponseInterface; + +/** + * @author Yann LUCAS + */ +final class SendinblueApiTransport extends AbstractApiTransport +{ + private $key; + + public function __construct(string $key, ?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) + { + $this->key = $key; + + parent::__construct($client, $dispatcher, $logger); + } + + public function __toString(): string + { + return sprintf('sendinblue+api://%s', $this->getEndpoint()); + } + + protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface + { + $response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/v3/smtp/email', [ + 'json' => $this->getPayload($email, $envelope), + 'headers' => [ + 'api-key' => $this->key, + ], + ]); + + $result = $response->toArray(false); + if (201 !== $response->getStatusCode()) { + throw new HttpTransportException('Unable to send an email: '.$result['message'].sprintf(' (code %d).', $response->getStatusCode()), $response); + } + + $sentMessage->setMessageId($result['messageId']); + + return $response; + } + + protected function stringifyAddresses(array $addresses): array + { + $stringifiedAddresses = []; + foreach ($addresses as $address) { + $stringifiedAddresses[] = $this->stringifyAddress($address); + } + + return $stringifiedAddresses; + } + + private function getPayload(Email $email, Envelope $envelope): array + { + $payload = [ + 'sender' => $this->stringifyAddress($envelope->getSender()), + 'to' => $this->stringifyAddresses($this->getRecipients($email, $envelope)), + 'subject' => $email->getSubject(), + ]; + if ($attachements = $this->prepareAttachments($email)) { + $payload['attachment'] = $attachements; + } + if ($emails = $email->getReplyTo()) { + $payload['replyTo'] = current($this->stringifyAddresses($emails)); + } + if ($emails = $email->getCc()) { + $payload['cc'] = $this->stringifyAddresses($emails); + } + if ($emails = $email->getBcc()) { + $payload['bcc'] = $this->stringifyAddresses($emails); + } + if ($email->getTextBody()) { + $payload['textContent'] = $email->getTextBody(); + } + if ($email->getHtmlBody()) { + $payload['htmlContent'] = $email->getHtmlBody(); + } + if ($headersAndTags = $this->prepareHeadersAndTags($email->getHeaders())) { + $payload = array_merge($payload, $headersAndTags); + } + + return $payload; + } + + private function prepareAttachments(Email $email): array + { + $attachments = []; + foreach ($email->getAttachments() as $attachment) { + $headers = $attachment->getPreparedHeaders(); + $filename = $headers->getHeaderParameter('Content-Disposition', 'filename'); + + $att = [ + 'content' => str_replace("\r\n", '', $attachment->bodyToString()), + 'name' => $filename, + ]; + + $attachments[] = $att; + } + + return $attachments; + } + + private function prepareHeadersAndTags(Headers $headers): array + { + $headersAndTags = []; + $headersToBypass = ['from', 'to', 'cc', 'bcc', 'subject', 'reply-to', 'content-type', 'accept', 'api-key']; + foreach ($headers->all() as $name => $header) { + if (\in_array($name, $headersToBypass, true)) { + continue; + } + if ($header instanceof TagHeader) { + $headersAndTags['tags'][] = $header->getValue(); + + continue; + } + if ($header instanceof MetadataHeader) { + $headersAndTags['headers']['X-Mailin-'.ucfirst(strtolower($header->getKey()))] = $header->getValue(); + + continue; + } + if ('templateid' === $name) { + $headersAndTags[$header->getName()] = (int) $header->getValue(); + + continue; + } + if ('params' === $name) { + $headersAndTags[$header->getName()] = $header->getParameters(); + + continue; + } + $headersAndTags['headers'][$name] = $header->getBodyAsString(); + } + + return $headersAndTags; + } + + private function stringifyAddress(Address $address): array + { + $stringifiedAddress = ['email' => $address->getAddress()]; + + if ($address->getName()) { + $stringifiedAddress['name'] = $address->getName(); + } + + return $stringifiedAddress; + } + + private function getEndpoint(): ?string + { + return ($this->host ?: 'api.sendinblue.com').($this->port ? ':'.$this->port : ''); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueSmtpTransport.php b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueSmtpTransport.php new file mode 100644 index 0000000000..7b2eace02b --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueSmtpTransport.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Sendinblue\Transport; + +use Psr\Log\LoggerInterface; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * @author Yann LUCAS + */ +final class SendinblueSmtpTransport extends EsmtpTransport +{ + public function __construct(string $username, string $password, ?EventDispatcherInterface $dispatcher = null, ?LoggerInterface $logger = null) + { + parent::__construct('smtp-relay.sendinblue.com', 465, true, $dispatcher, $logger); + + $this->setUsername($username); + $this->setPassword($password); + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueTransportFactory.php b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueTransportFactory.php new file mode 100644 index 0000000000..400c25f194 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/Transport/SendinblueTransportFactory.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Mailer\Bridge\Sendinblue\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; + +/** + * @author Yann LUCAS + */ +final class SendinblueTransportFactory extends AbstractTransportFactory +{ + public function create(Dsn $dsn): TransportInterface + { + if (!\in_array($dsn->getScheme(), $this->getSupportedSchemes(), true)) { + throw new UnsupportedSchemeException($dsn, 'sendinblue', $this->getSupportedSchemes()); + } + + switch ($dsn->getScheme()) { + default: + case 'sendinblue': + case 'sendinblue+smtp': + $transport = SendinblueSmtpTransport::class; + break; + case 'sendinblue+api': + return (new SendinblueApiTransport($this->getUser($dsn), $this->client, $this->dispatcher, $this->logger)) + ->setHost('default' === $dsn->getHost() ? null : $dsn->getHost()) + ->setPort($dsn->getPort()) + ; + } + + return new $transport($this->getUser($dsn), $this->getPassword($dsn), $this->dispatcher, $this->logger); + } + + protected function getSupportedSchemes(): array + { + return ['sendinblue', 'sendinblue+smtp', 'sendinblue+api']; + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/composer.json b/src/Symfony/Component/Mailer/Bridge/Sendinblue/composer.json new file mode 100644 index 0000000000..c6bdc454d4 --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/composer.json @@ -0,0 +1,37 @@ +{ + "name": "symfony/sendinblue-mailer", + "type": "symfony-bridge", + "description": "Symfony Sendinblue 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": ">=7.2.5", + "symfony/mailer": "^5.1" + }, + "require-dev": { + "symfony/http-client": "^4.4|^5.0" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Mailer\\Bridge\\Sendinblue\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-master": "5.2-dev" + } + } +} diff --git a/src/Symfony/Component/Mailer/Bridge/Sendinblue/phpunit.xml.dist b/src/Symfony/Component/Mailer/Bridge/Sendinblue/phpunit.xml.dist new file mode 100644 index 0000000000..ae281a09ec --- /dev/null +++ b/src/Symfony/Component/Mailer/Bridge/Sendinblue/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 bcf7f415ee..97f6bf581a 100644 --- a/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Mailer/Exception/UnsupportedSchemeException.php @@ -48,6 +48,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\Mailjet\Transport\MailjetTransportFactory::class, 'package' => 'symfony/mailjet-mailer', ], + 'sendinblue' => [ + 'class' => Bridge\Sendinblue\Transport\SendinblueTransportFactory::class, + 'package' => 'symfony/sendinblue-mailer', + ], ]; public function __construct(Dsn $dsn, string $name = null, array $supported = []) diff --git a/src/Symfony/Component/Mailer/Transport.php b/src/Symfony/Component/Mailer/Transport.php index 0c52282729..760486a8ad 100644 --- a/src/Symfony/Component/Mailer/Transport.php +++ b/src/Symfony/Component/Mailer/Transport.php @@ -19,6 +19,7 @@ use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory; use Symfony\Component\Mailer\Bridge\Mailjet\Transport\MailjetTransportFactory; use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory; use Symfony\Component\Mailer\Bridge\Sendgrid\Transport\SendgridTransportFactory; +use Symfony\Component\Mailer\Bridge\Sendinblue\Transport\SendinblueTransportFactory; use Symfony\Component\Mailer\Exception\InvalidArgumentException; use Symfony\Component\Mailer\Exception\UnsupportedSchemeException; use Symfony\Component\Mailer\Transport\Dsn; @@ -48,6 +49,7 @@ class Transport PostmarkTransportFactory::class, SendgridTransportFactory::class, MailjetTransportFactory::class, + SendinblueTransportFactory::class, ]; private $factories;