feature #37897 [Mailer] Support Amazon SES ConfigurationSetName (cvmiert)
This PR was merged into the 5.2-dev branch.
Discussion
----------
[Mailer] Support Amazon SES ConfigurationSetName
| Q | A
| ------------- | ---
| Branch? | master
| Bug fix? | no
| New feature? | yes
| Deprecations? | no
| Tickets | N/A
| License | MIT
| Doc PR | N/A
In Amazon SES a Configuration Set can be used to monitor email sending
events (delivery, bounces, complaints etc.). In order to use this
feature the ConfigurationSetName needs to be sent along with the email.
Setting the `X-SES-CONFIGURATION-SET` header should accomplish this for
all SES Transports now.
Ref: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/using-configuration-sets-in-email.html
Commits
-------
a36fec3204
[Mailer] Support Amazon SES ConfigurationSetName
This commit is contained in:
commit
f1d1514793
@ -69,6 +69,7 @@ class SesApiAsyncAwsTransportTest extends TestCase
|
|||||||
$this->assertSame('Hello There!', $content['Content']['Simple']['Body']['Text']['Data']);
|
$this->assertSame('Hello There!', $content['Content']['Simple']['Body']['Text']['Data']);
|
||||||
$this->assertSame('<b>Hello There!</b>', $content['Content']['Simple']['Body']['Html']['Data']);
|
$this->assertSame('<b>Hello There!</b>', $content['Content']['Simple']['Body']['Html']['Data']);
|
||||||
$this->assertSame(['replyto-1@example.com', 'replyto-2@example.com'], $content['ReplyToAddresses']);
|
$this->assertSame(['replyto-1@example.com', 'replyto-2@example.com'], $content['ReplyToAddresses']);
|
||||||
|
$this->assertSame('aws-configuration-set-name', $content['ConfigurationSetName']);
|
||||||
|
|
||||||
$json = '{"MessageId": "foobar"}';
|
$json = '{"MessageId": "foobar"}';
|
||||||
|
|
||||||
@ -87,6 +88,8 @@ class SesApiAsyncAwsTransportTest extends TestCase
|
|||||||
->html('<b>Hello There!</b>')
|
->html('<b>Hello There!</b>')
|
||||||
->replyTo(new Address('replyto-1@example.com'), new Address('replyto-2@example.com'));
|
->replyTo(new Address('replyto-1@example.com'), new Address('replyto-2@example.com'));
|
||||||
|
|
||||||
|
$mail->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'aws-configuration-set-name');
|
||||||
|
|
||||||
$message = $transport->send($mail);
|
$message = $transport->send($mail);
|
||||||
|
|
||||||
$this->assertSame('foobar', $message->getMessageId());
|
$this->assertSame('foobar', $message->getMessageId());
|
||||||
|
@ -68,6 +68,7 @@ class SesApiTransportTest extends TestCase
|
|||||||
$this->assertSame('Saif Eddin <saif.gmati@symfony.com>', $content['Destination_ToAddresses_member'][0]);
|
$this->assertSame('Saif Eddin <saif.gmati@symfony.com>', $content['Destination_ToAddresses_member'][0]);
|
||||||
$this->assertSame('Fabien <fabpot@symfony.com>', $content['Source']);
|
$this->assertSame('Fabien <fabpot@symfony.com>', $content['Source']);
|
||||||
$this->assertSame('Hello There!', $content['Message_Body_Text_Data']);
|
$this->assertSame('Hello There!', $content['Message_Body_Text_Data']);
|
||||||
|
$this->assertSame('aws-configuration-set-name', $content['ConfigurationSetName']);
|
||||||
|
|
||||||
$xml = '<SendEmailResponse xmlns="https://email.amazonaws.com/doc/2010-03-31/">
|
$xml = '<SendEmailResponse xmlns="https://email.amazonaws.com/doc/2010-03-31/">
|
||||||
<SendEmailResult>
|
<SendEmailResult>
|
||||||
@ -88,6 +89,53 @@ class SesApiTransportTest extends TestCase
|
|||||||
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
||||||
->text('Hello There!');
|
->text('Hello There!');
|
||||||
|
|
||||||
|
$mail->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'aws-configuration-set-name');
|
||||||
|
|
||||||
|
$message = $transport->send($mail);
|
||||||
|
|
||||||
|
$this->assertSame('foobar', $message->getMessageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSendWithAttachments()
|
||||||
|
{
|
||||||
|
$client = new MockHttpClient(function (string $method, string $url, array $options): ResponseInterface {
|
||||||
|
$this->assertSame('POST', $method);
|
||||||
|
$this->assertSame('https://email.eu-west-1.amazonaws.com:8984/', $url);
|
||||||
|
$this->assertStringContainsStringIgnoringCase('X-Amzn-Authorization: AWS3-HTTPS AWSAccessKeyId=ACCESS_KEY,Algorithm=HmacSHA256,Signature=', $options['headers'][0] ?? $options['request_headers'][0]);
|
||||||
|
|
||||||
|
parse_str($options['body'], $body);
|
||||||
|
$content = base64_decode($body['RawMessage_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);
|
||||||
|
$this->assertStringContainsString(base64_encode('attached data'), $content);
|
||||||
|
|
||||||
|
$this->assertSame('aws-configuration-set-name', $body['ConfigurationSetName']);
|
||||||
|
|
||||||
|
$xml = '<SendEmailResponse xmlns="https://email.amazonaws.com/doc/2010-03-31/">
|
||||||
|
<SendRawEmailResult>
|
||||||
|
<MessageId>foobar</MessageId>
|
||||||
|
</SendRawEmailResult>
|
||||||
|
</SendEmailResponse>';
|
||||||
|
|
||||||
|
return new MockResponse($xml, [
|
||||||
|
'http_code' => 200,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
$transport = new SesApiTransport('ACCESS_KEY', 'SECRET_KEY', null, $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!')
|
||||||
|
->attach('attached data');
|
||||||
|
|
||||||
|
$mail->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'aws-configuration-set-name');
|
||||||
|
|
||||||
$message = $transport->send($mail);
|
$message = $transport->send($mail);
|
||||||
|
|
||||||
$this->assertSame('foobar', $message->getMessageId());
|
$this->assertSame('foobar', $message->getMessageId());
|
||||||
|
@ -68,6 +68,7 @@ class SesHttpAsyncAwsTransportTest extends TestCase
|
|||||||
$this->assertStringContainsString('Saif Eddin <saif.gmati@symfony.com>', $content);
|
$this->assertStringContainsString('Saif Eddin <saif.gmati@symfony.com>', $content);
|
||||||
$this->assertStringContainsString('Fabien <fabpot@symfony.com>', $content);
|
$this->assertStringContainsString('Fabien <fabpot@symfony.com>', $content);
|
||||||
$this->assertStringContainsString('Hello There!', $content);
|
$this->assertStringContainsString('Hello There!', $content);
|
||||||
|
$this->assertSame('aws-configuration-set-name', $body['ConfigurationSetName']);
|
||||||
|
|
||||||
$json = '{"MessageId": "foobar"}';
|
$json = '{"MessageId": "foobar"}';
|
||||||
|
|
||||||
@ -84,6 +85,8 @@ class SesHttpAsyncAwsTransportTest extends TestCase
|
|||||||
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
||||||
->text('Hello There!');
|
->text('Hello There!');
|
||||||
|
|
||||||
|
$mail->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'aws-configuration-set-name');
|
||||||
|
|
||||||
$message = $transport->send($mail);
|
$message = $transport->send($mail);
|
||||||
|
|
||||||
$this->assertSame('foobar', $message->getMessageId());
|
$this->assertSame('foobar', $message->getMessageId());
|
||||||
|
@ -70,6 +70,8 @@ class SesHttpTransportTest extends TestCase
|
|||||||
$this->assertStringContainsString('Fabien <fabpot@symfony.com>', $content);
|
$this->assertStringContainsString('Fabien <fabpot@symfony.com>', $content);
|
||||||
$this->assertStringContainsString('Hello There!', $content);
|
$this->assertStringContainsString('Hello There!', $content);
|
||||||
|
|
||||||
|
$this->assertSame('aws-configuration-set-name', $body['ConfigurationSetName']);
|
||||||
|
|
||||||
$xml = '<SendEmailResponse xmlns="https://email.amazonaws.com/doc/2010-03-31/">
|
$xml = '<SendEmailResponse xmlns="https://email.amazonaws.com/doc/2010-03-31/">
|
||||||
<SendRawEmailResult>
|
<SendRawEmailResult>
|
||||||
<MessageId>foobar</MessageId>
|
<MessageId>foobar</MessageId>
|
||||||
@ -89,6 +91,8 @@ class SesHttpTransportTest extends TestCase
|
|||||||
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
->from(new Address('fabpot@symfony.com', 'Fabien'))
|
||||||
->text('Hello There!');
|
->text('Hello There!');
|
||||||
|
|
||||||
|
$mail->getHeaders()->addTextHeader('X-SES-CONFIGURATION-SET', 'aws-configuration-set-name');
|
||||||
|
|
||||||
$message = $transport->send($mail);
|
$message = $transport->send($mail);
|
||||||
|
|
||||||
$this->assertSame('foobar', $message->getMessageId());
|
$this->assertSame('foobar', $message->getMessageId());
|
||||||
|
@ -89,6 +89,9 @@ class SesApiAsyncAwsTransport extends SesHttpAsyncAwsTransport
|
|||||||
if ($emails = $email->getReplyTo()) {
|
if ($emails = $email->getReplyTo()) {
|
||||||
$request['ReplyToAddresses'] = $this->stringifyAddresses($emails);
|
$request['ReplyToAddresses'] = $this->stringifyAddresses($emails);
|
||||||
}
|
}
|
||||||
|
if ($header = $email->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
|
||||||
|
$request['ConfigurationSetName'] = $header->getBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
return new SendEmailRequest($request);
|
return new SendEmailRequest($request);
|
||||||
}
|
}
|
||||||
|
@ -90,10 +90,16 @@ class SesApiTransport extends AbstractApiTransport
|
|||||||
private function getPayload(Email $email, Envelope $envelope): array
|
private function getPayload(Email $email, Envelope $envelope): array
|
||||||
{
|
{
|
||||||
if ($email->getAttachments()) {
|
if ($email->getAttachments()) {
|
||||||
return [
|
$payload = [
|
||||||
'Action' => 'SendRawEmail',
|
'Action' => 'SendRawEmail',
|
||||||
'RawMessage.Data' => base64_encode($email->toString()),
|
'RawMessage.Data' => base64_encode($email->toString()),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($header = $email->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
|
||||||
|
$payload['ConfigurationSetName'] = $header->getBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
$payload = [
|
$payload = [
|
||||||
@ -118,6 +124,9 @@ class SesApiTransport extends AbstractApiTransport
|
|||||||
if ($email->getReplyTo()) {
|
if ($email->getReplyTo()) {
|
||||||
$payload['ReplyToAddresses.member'] = $this->stringifyAddresses($email->getReplyTo());
|
$payload['ReplyToAddresses.member'] = $this->stringifyAddresses($email->getReplyTo());
|
||||||
}
|
}
|
||||||
|
if ($header = $email->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
|
||||||
|
$payload['ConfigurationSetName'] = $header->getBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
return $payload;
|
return $payload;
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ use Psr\Log\LoggerInterface;
|
|||||||
use Symfony\Component\Mailer\Exception\HttpTransportException;
|
use Symfony\Component\Mailer\Exception\HttpTransportException;
|
||||||
use Symfony\Component\Mailer\SentMessage;
|
use Symfony\Component\Mailer\SentMessage;
|
||||||
use Symfony\Component\Mailer\Transport\AbstractTransport;
|
use Symfony\Component\Mailer\Transport\AbstractTransport;
|
||||||
|
use Symfony\Component\Mime\Message;
|
||||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +68,7 @@ class SesHttpAsyncAwsTransport extends AbstractTransport
|
|||||||
|
|
||||||
protected function getRequest(SentMessage $message): SendEmailRequest
|
protected function getRequest(SentMessage $message): SendEmailRequest
|
||||||
{
|
{
|
||||||
return new SendEmailRequest([
|
$request = [
|
||||||
'Destination' => new Destination([
|
'Destination' => new Destination([
|
||||||
'ToAddresses' => $this->stringifyAddresses($message->getEnvelope()->getRecipients()),
|
'ToAddresses' => $this->stringifyAddresses($message->getEnvelope()->getRecipients()),
|
||||||
]),
|
]),
|
||||||
@ -76,6 +77,13 @@ class SesHttpAsyncAwsTransport extends AbstractTransport
|
|||||||
'Data' => $message->toString(),
|
'Data' => $message->toString(),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (($message->getOriginalMessage() instanceof Message)
|
||||||
|
&& $configurationSetHeader = $message->getOriginalMessage()->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
|
||||||
|
$request['ConfigurationSetName'] = $configurationSetHeader->getBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SendEmailRequest($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ use Psr\Log\LoggerInterface;
|
|||||||
use Symfony\Component\Mailer\Exception\HttpTransportException;
|
use Symfony\Component\Mailer\Exception\HttpTransportException;
|
||||||
use Symfony\Component\Mailer\SentMessage;
|
use Symfony\Component\Mailer\SentMessage;
|
||||||
use Symfony\Component\Mailer\Transport\AbstractHttpTransport;
|
use Symfony\Component\Mailer\Transport\AbstractHttpTransport;
|
||||||
|
use Symfony\Component\Mime\Message;
|
||||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||||
@ -54,7 +55,7 @@ class SesHttpTransport extends AbstractHttpTransport
|
|||||||
$date = gmdate('D, d M Y H:i:s e');
|
$date = gmdate('D, d M Y H:i:s e');
|
||||||
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
|
$auth = sprintf('AWS3-HTTPS AWSAccessKeyId=%s,Algorithm=HmacSHA256,Signature=%s', $this->accessKey, $this->getSignature($date));
|
||||||
|
|
||||||
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), [
|
$request = [
|
||||||
'headers' => [
|
'headers' => [
|
||||||
'X-Amzn-Authorization' => $auth,
|
'X-Amzn-Authorization' => $auth,
|
||||||
'Date' => $date,
|
'Date' => $date,
|
||||||
@ -63,7 +64,14 @@ class SesHttpTransport extends AbstractHttpTransport
|
|||||||
'Action' => 'SendRawEmail',
|
'Action' => 'SendRawEmail',
|
||||||
'RawMessage.Data' => base64_encode($message->toString()),
|
'RawMessage.Data' => base64_encode($message->toString()),
|
||||||
],
|
],
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (($message->getOriginalMessage() instanceof Message)
|
||||||
|
&& $configurationSetHeader = $message->getOriginalMessage()->getHeaders()->get('X-SES-CONFIGURATION-SET')) {
|
||||||
|
$request['body']['ConfigurationSetName'] = $configurationSetHeader->getBodyAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = $this->client->request('POST', 'https://'.$this->getEndpoint(), $request);
|
||||||
|
|
||||||
$result = new \SimpleXMLElement($response->getContent(false));
|
$result = new \SimpleXMLElement($response->getContent(false));
|
||||||
if (200 !== $response->getStatusCode()) {
|
if (200 !== $response->getStatusCode()) {
|
||||||
|
Reference in New Issue
Block a user