[Mailer] Support Amazon SES ConfigurationSetName
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
This commit is contained in:
parent
1f77f32d78
commit
a36fec3204
@ -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