Skip to content

Commit 9535a80

Browse files
driesvintsStyleCIBottaylorotwell
authored
[9.x] Re-implement original SesTransport (#40696)
* Re-implement original SesTransport * Apply fixes from StyleCI * wip * wip * wip * Update composer.json Co-authored-by: StyleCI Bot <[email protected]> Co-authored-by: Taylor Otwell <[email protected]>
1 parent 2540c1b commit 9535a80

File tree

7 files changed

+211
-20
lines changed

7 files changed

+211
-20
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
"ext-posix": "Required to use all features of the queue worker.",
141141
"ext-redis": "Required to use the Redis cache and queue drivers (^4.0|^5.0).",
142142
"ably/ably-php": "Required to use the Ably broadcast driver (^1.0).",
143-
"aws/aws-sdk-php": "Required to use the SQS queue driver and DynamoDb failed job storage (^3.198.1).",
143+
"aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage, and SES mail driver (^3.198.1).",
144144
"brianium/paratest": "Required to run tests in parallel (^6.0).",
145145
"doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.13.3|^3.1.4).",
146146
"filp/whoops": "Required for friendly error pages in development (^2.14.3).",
@@ -157,7 +157,6 @@
157157
"predis/predis": "Required to use the predis connector (^1.1.9).",
158158
"psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
159159
"pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^6.0|^7.0).",
160-
"symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).",
161160
"symfony/cache": "Required to PSR-6 cache bridge (^6.0).",
162161
"symfony/filesystem": "Required to enable support for relative symbolic links (^6.0).",
163162
"symfony/http-client": "Required to enable support for the Symfony API mail transports (^6.0).",

src/Illuminate/Mail/MailManager.php

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@
22

33
namespace Illuminate\Mail;
44

5+
use Aws\Ses\SesClient;
56
use Closure;
67
use Illuminate\Contracts\Mail\Factory as FactoryContract;
78
use Illuminate\Log\LogManager;
89
use Illuminate\Mail\Transport\ArrayTransport;
910
use Illuminate\Mail\Transport\LogTransport;
11+
use Illuminate\Mail\Transport\SesTransport;
1012
use Illuminate\Support\Arr;
1113
use Illuminate\Support\Str;
1214
use InvalidArgumentException;
1315
use Psr\Log\LoggerInterface;
14-
use Symfony\Component\Mailer\Bridge\Amazon\Transport\SesTransportFactory;
1516
use Symfony\Component\Mailer\Bridge\Mailgun\Transport\MailgunTransportFactory;
1617
use Symfony\Component\Mailer\Bridge\Postmark\Transport\PostmarkTransportFactory;
1718
use Symfony\Component\Mailer\Transport\Dsn;
@@ -234,20 +235,25 @@ protected function createSesTransport(array $config)
234235

235236
$config = Arr::except($config, ['transport']);
236237

237-
$factory = new SesTransportFactory();
238+
return new SesTransport(
239+
new SesClient($this->addSesCredentials($config)),
240+
$config['options'] ?? []
241+
);
242+
}
238243

239-
if (! isset($config['session_token']) && isset($config['token'])) {
240-
$config['session_token'] = $config['token'];
244+
/**
245+
* Add the SES credentials to the configuration array.
246+
*
247+
* @param array $config
248+
* @return array
249+
*/
250+
protected function addSesCredentials(array $config)
251+
{
252+
if (! empty($config['key']) && ! empty($config['secret'])) {
253+
$config['credentials'] = Arr::only($config, ['key', 'secret', 'token']);
241254
}
242255

243-
return $factory->create(new Dsn(
244-
'ses+api',
245-
'default',
246-
$config['key'] ?? null,
247-
$config['secret'] ?? null,
248-
$config['port'] ?? null,
249-
$config
250-
));
256+
return $config;
251257
}
252258

253259
/**

src/Illuminate/Mail/Transport/ArrayTransport.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public function __construct()
2929

3030
/**
3131
* {@inheritdoc}
32-
*
33-
* @return int
3432
*/
3533
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
3634
{

src/Illuminate/Mail/Transport/LogTransport.php

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ public function __construct(LoggerInterface $logger)
3030

3131
/**
3232
* {@inheritdoc}
33-
*
34-
* @return int
3533
*/
3634
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
3735
{
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
namespace Illuminate\Mail\Transport;
4+
5+
use Aws\Exception\AwsException;
6+
use Aws\Ses\SesClient;
7+
use Exception;
8+
use Symfony\Component\Mailer\SentMessage;
9+
use Symfony\Component\Mailer\Transport\AbstractTransport;
10+
use Symfony\Component\Mailer\Transport\TransportInterface;
11+
12+
class SesTransport extends AbstractTransport implements TransportInterface
13+
{
14+
/**
15+
* The Amazon SES instance.
16+
*
17+
* @var \Aws\Ses\SesClient
18+
*/
19+
protected $ses;
20+
21+
/**
22+
* The Amazon SES transmission options.
23+
*
24+
* @var array
25+
*/
26+
protected $options = [];
27+
28+
/**
29+
* Create a new SES transport instance.
30+
*
31+
* @param \Aws\Ses\SesClient $ses
32+
* @param array $options
33+
* @return void
34+
*/
35+
public function __construct(SesClient $ses, $options = [])
36+
{
37+
$this->ses = $ses;
38+
$this->options = $options;
39+
40+
parent::__construct();
41+
}
42+
43+
/**
44+
* {@inheritDoc}
45+
*/
46+
protected function doSend(SentMessage $message): void
47+
{
48+
try {
49+
$this->ses->sendRawEmail(
50+
array_merge(
51+
$this->options, [
52+
'Source' => $message->getEnvelope()->getSender()->toString(),
53+
'RawMessage' => [
54+
'Data' => $message->toString(),
55+
],
56+
]
57+
)
58+
);
59+
} catch (AwsException $e) {
60+
throw new Exception('Request to AWS SES API failed.', $e->getCode(), $e);
61+
}
62+
}
63+
64+
/**
65+
* Get the string representation of the transport.
66+
*
67+
* @return string
68+
*/
69+
public function __toString(): string
70+
{
71+
return 'ses';
72+
}
73+
74+
/**
75+
* Get the Amazon SES client for the SesTransport instance.
76+
*
77+
* @return \Aws\Ses\SesClient
78+
*/
79+
public function ses()
80+
{
81+
return $this->ses;
82+
}
83+
84+
/**
85+
* Get the transmission options being used by the transport.
86+
*
87+
* @return array
88+
*/
89+
public function getOptions()
90+
{
91+
return $this->options;
92+
}
93+
94+
/**
95+
* Set the transmission options being used by the transport.
96+
*
97+
* @param array $options
98+
* @return array
99+
*/
100+
public function setOptions(array $options)
101+
{
102+
return $this->options = $options;
103+
}
104+
}

src/Illuminate/Mail/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
}
3838
},
3939
"suggest": {
40-
"symfony/amazon-mailer": "Required to enable support for the SES mail transport (^6.0).",
40+
"aws/aws-sdk-php": "Required to use the SES mail driver (^3.198.1).",
4141
"symfony/http-client": "Required to use the Symfony API mail transports (^6.0).",
4242
"symfony/mailgun-mailer": "Required to enable support for the Mailgun mail transport (^6.0).",
4343
"symfony/postmark-mailer": "Required to enable support for the Postmark mail transport (^6.0)."

tests/Mail/MailSesTransportTest.php

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,115 @@
22

33
namespace Illuminate\Tests\Mail;
44

5+
use Aws\Ses\SesClient;
56
use Illuminate\Config\Repository;
67
use Illuminate\Container\Container;
78
use Illuminate\Mail\MailManager;
9+
use Illuminate\Mail\Transport\SesTransport;
10+
use Illuminate\View\Factory;
11+
use Mockery as m;
812
use PHPUnit\Framework\TestCase;
13+
use Symfony\Component\Mime\Email;
914

1015
class MailSesTransportTest extends TestCase
1116
{
17+
protected function tearDown(): void
18+
{
19+
m::close();
20+
21+
parent::tearDown();
22+
}
23+
1224
public function testGetTransport()
1325
{
1426
$container = new Container;
1527

1628
$container->singleton('config', function () {
1729
return new Repository([
1830
'services.ses' => [
31+
'key' => 'foo',
32+
'secret' => 'bar',
1933
'region' => 'us-east-1',
2034
],
2135
]);
2236
});
2337

2438
$manager = new MailManager($container);
2539

40+
/** @var \Illuminate\Mail\Transport\SesTransport $transport */
2641
$transport = $manager->createSymfonyTransport(['transport' => 'ses']);
2742

28-
$this->assertSame('ses+api://random_key@us-east-1', $transport->__toString());
43+
$ses = $transport->ses();
44+
45+
$this->assertSame('us-east-1', $ses->getRegion());
46+
47+
$this->assertSame('ses', (string) $transport);
48+
}
49+
50+
public function testSend()
51+
{
52+
$message = new Email();
53+
$message->subject('Foo subject');
54+
$message->text('Bar body');
55+
$message->sender('[email protected]');
56+
$message->to('[email protected]');
57+
$message->bcc('[email protected]');
58+
59+
$client = m::mock(SesClient::class);
60+
$client->shouldReceive('sendRawEmail')->once();
61+
62+
(new SesTransport($client))->send($message);
63+
}
64+
65+
public function testSesLocalConfiguration()
66+
{
67+
$container = new Container;
68+
69+
$container->singleton('config', function () {
70+
return new Repository([
71+
'mail' => [
72+
'mailers' => [
73+
'ses' => [
74+
'transport' => 'ses',
75+
'region' => 'eu-west-1',
76+
'options' => [
77+
'ConfigurationSetName' => 'Laravel',
78+
'Tags' => [
79+
['Name' => 'Laravel', 'Value' => 'Framework'],
80+
],
81+
],
82+
],
83+
],
84+
],
85+
'services' => [
86+
'ses' => [
87+
'region' => 'us-east-1',
88+
],
89+
],
90+
]);
91+
});
92+
93+
$container->instance('view', $this->createMock(Factory::class));
94+
95+
$container->bind('events', function () {
96+
return null;
97+
});
98+
99+
$manager = new MailManager($container);
100+
101+
/** @var \Illuminate\Mail\Mailer $mailer */
102+
$mailer = $manager->mailer('ses');
103+
104+
/** @var \Illuminate\Mail\Transport\SesTransport $transport */
105+
$transport = $mailer->getSymfonyTransport();
106+
107+
$this->assertSame('eu-west-1', $transport->ses()->getRegion());
108+
109+
$this->assertSame([
110+
'ConfigurationSetName' => 'Laravel',
111+
'Tags' => [
112+
['Name' => 'Laravel', 'Value' => 'Framework'],
113+
],
114+
], $transport->getOptions());
29115
}
30116
}

0 commit comments

Comments
 (0)