Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Mailer] Use AsyncAws to handle SES requests #35992

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 2 UPGRADE-5.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Mailer
------

* Deprecated passing Mailgun headers without their "h:" prefix.
* Deprecated the `SesApiTransport` class. It has been replaced by SesApiAsyncAwsTransport Run `composer require async-aws/ses` to use the new classes.
* Deprecated the `SesHttpTransport` class. It has been replaced by SesHttpAsyncAwsTransport Run `composer require async-aws/ses` to use the new classes.

Messenger
---------
Expand Down
7 changes: 7 additions & 0 deletions 7 UPGRADE-6.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ HttpKernel
* Made `WarmableInterface::warmUp()` return a list of classes or files to preload on PHP 7.4+
* Removed support for `service:action` syntax to reference controllers. Use `serviceOrFqcn::method` instead.


Mailer
------

* Removed the `SesApiTransport` class. Use `SesApiAsyncAwsTransport` instead.
* Removed the `SesHttpTransport` class. Use `SesHttpAsyncAwsTransport` instead.

Messenger
---------

Expand Down
1 change: 1 addition & 0 deletions 1 composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
"require-dev": {
"amphp/http-client": "^4.2",
"amphp/http-tunnel": "^1.0",
"async-aws/ses": "^1.0",
"cache/integration-tests": "dev-master",
"doctrine/annotations": "~1.0",
"doctrine/cache": "~1.6",
Expand Down
5 changes: 5 additions & 0 deletions 5 src/Symfony/Component/Mailer/Bridge/Amazon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

5.1.0
-----

* Added `async-aws/ses` to communicate with AWS API.

4.4.0
-----

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Mailer\Bridge\Amazon\Tests\Transport;

use AsyncAws\Core\Configuration;
use AsyncAws\Core\Credentials\NullProvider;
use AsyncAws\Ses\SesClient;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiAsyncAwsTransport;
use Symfony\Component\Mailer\Exception\HttpTransportException;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Contracts\HttpClient\ResponseInterface;

class SesApiAsyncAwsTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(SesApiAsyncAwsTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}

public function getTransportData()
{
return [
[
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY']))),
'ses+api://ACCESS_KEY@us-east-1',
],
[
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'region' => 'us-west-1']))),
'ses+api://ACCESS_KEY@us-west-1',
],
[
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'endpoint' => 'https://example.com']))),
'ses+api://ACCESS_KEY@example.com',
],
[
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'endpoint' => 'https://example.com:99']))),
'ses+api://ACCESS_KEY@example.com:99',
],
];
}

public function testSend()
{
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
$this->assertSame('POST', $method);
$this->assertSame('https://email.us-east-1.amazonaws.com/v2/email/outbound-emails', $url);

$content = json_decode($options['body'], true);

$this->assertSame('Hello!', $content['Content']['Simple']['Subject']['Data']);
$this->assertSame('Saif Eddin <saif.gmati@symfony.com>', $content['Destination']['ToAddresses'][0]);
$this->assertSame('Fabien <fabpot@symfony.com>', $content['FromEmailAddress']);
$this->assertSame('Hello There!', $content['Content']['Simple']['Body']['Text']['Data']);
$this->assertSame('<b>Hello There!</b>', $content['Content']['Simple']['Body']['Html']['Data']);

$json = '{"MessageId": "foobar"}';

return new MockResponse($json, [
'http_code' => 200,
]);
});

$transport = new SesApiAsyncAwsTransport(new SesClient(Configuration::create([]), new NullProvider(), $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!')
->html('<b>Hello There!</b>');

$message = $transport->send($mail);

$this->assertSame('foobar', $message->getMessageId());
}

public function testSendThrowsForErrorResponse()
{
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
$xml = "<SendEmailResponse xmlns=\"https://email.amazonaws.com/doc/2010-03-31/\">
<Error>
<Message>i'm a teapot</Message>
<Code>418</Code>
</Error>
</SendEmailResponse>";

return new MockResponse($xml, [
'http_code' => 418,
]);
});

$transport = new SesApiAsyncAwsTransport(new SesClient(Configuration::create([]), new NullProvider(), $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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
use Symfony\Component\Mime\Email;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* @group legacy
*/
class SesApiTransportTest extends TestCase
{
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Mailer\Bridge\Amazon\Tests\Transport;

use AsyncAws\Core\Configuration;
use AsyncAws\Core\Credentials\NullProvider;
use AsyncAws\Ses\SesClient;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpClient\MockHttpClient;
use Symfony\Component\HttpClient\Response\MockResponse;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpAsyncAwsTransport;
use Symfony\Component\Mailer\Exception\HttpTransportException;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Contracts\HttpClient\ResponseInterface;

class SesHttpAsyncAwsTransportTest extends TestCase
{
/**
* @dataProvider getTransportData
*/
public function testToString(SesHttpAsyncAwsTransport $transport, string $expected)
{
$this->assertSame($expected, (string) $transport);
}

public function getTransportData()
{
return [
[
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY']))),
'ses+https://ACCESS_KEY@us-east-1',
],
[
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'region' => 'us-west-1']))),
'ses+https://ACCESS_KEY@us-west-1',
],
[
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'endpoint' => 'https://example.com']))),
'ses+https://ACCESS_KEY@example.com',
],
[
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => 'ACCESS_KEY', 'accessKeySecret' => 'SECRET_KEY', 'endpoint' => 'https://example.com:99']))),
'ses+https://ACCESS_KEY@example.com:99',
],
];
}

public function testSend()
{
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
$this->assertSame('POST', $method);
$this->assertSame('https://email.us-east-1.amazonaws.com/v2/email/outbound-emails', $url);

$body = json_decode($options['body'], true);
$content = base64_decode($body['Content']['Raw']['Data']);

$this->assertStringContainsString('Hello!', $content);
$this->assertStringContainsString('Saif Eddin <saif.gmati@symfony.com>', $content);
$this->assertStringContainsString('Fabien <fabpot@symfony.com>', $content);
$this->assertStringContainsString('Hello There!', $content);

$json = '{"MessageId": "foobar"}';

return new MockResponse($json, [
'http_code' => 200,
]);
});

$transport = new SesHttpAsyncAwsTransport(new SesClient(Configuration::create([]), new NullProvider(), $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!');

$message = $transport->send($mail);

$this->assertSame('foobar', $message->getMessageId());
}

public function testSendThrowsForErrorResponse()
{
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
$xml = "<SendEmailResponse xmlns=\"https://email.amazonaws.com/doc/2010-03-31/\">
<Error>
<Message>i'm a teapot</Message>
<Code>418</Code>
</Error>
</SendEmailResponse>";

return new MockResponse($xml, [
'http_code' => 418,
]);
});

$transport = new SesHttpAsyncAwsTransport(new SesClient(Configuration::create([]), new NullProvider(), $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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
use Symfony\Component\Mime\Email;
use Symfony\Contracts\HttpClient\ResponseInterface;

/**
* @group legacy
*/
class SesHttpTransportTest extends TestCase
{
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

namespace Symfony\Component\Mailer\Bridge\Amazon\Tests\Transport;

use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiTransport;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpTransport;
use AsyncAws\Core\Configuration;
use AsyncAws\Ses\SesClient;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesApiAsyncAwsTransport;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesHttpAsyncAwsTransport;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesSmtpTransport;
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
use Symfony\Component\Mailer\Test\TransportFactoryTestCase;
Expand Down Expand Up @@ -67,37 +69,37 @@ public function createProvider(): iterable

yield [
new Dsn('ses+api', 'default', self::USER, self::PASSWORD),
new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-1']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesApiTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
new Dsn('ses+api', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-2']),
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-2']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses+api', 'example.com', self::USER, self::PASSWORD, 8080),
(new SesApiTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
new SesApiAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-1', 'endpoint' => 'https://example.com:8080']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses+https', 'default', self::USER, self::PASSWORD),
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-1']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses', 'default', self::USER, self::PASSWORD),
new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger),
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-1']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses+https', 'example.com', self::USER, self::PASSWORD, 8080),
(new SesHttpTransport(self::USER, self::PASSWORD, null, $client, $dispatcher, $logger))->setHost('example.com')->setPort(8080),
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-1', 'endpoint' => 'https://example.com:8080']), null, $client, $logger), $dispatcher, $logger),
];

yield [
new Dsn('ses+https', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-1']),
new SesHttpTransport(self::USER, self::PASSWORD, 'eu-west-1', $client, $dispatcher, $logger),
new Dsn('ses+https', 'default', self::USER, self::PASSWORD, null, ['region' => 'eu-west-2']),
new SesHttpAsyncAwsTransport(new SesClient(Configuration::create(['accessKeyId' => self::USER, 'accessKeySecret' => self::PASSWORD, 'region' => 'eu-west-2']), null, $client, $logger), $dispatcher, $logger),
];

yield [
Expand Down Expand Up @@ -127,7 +129,5 @@ public function unsupportedSchemeProvider(): iterable
public function incompleteDsnProvider(): iterable
{
yield [new Dsn('ses+smtp', 'default', self::USER)];

yield [new Dsn('ses+smtp', 'default', null, self::PASSWORD)];
}
}
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.