8000 [Mailer] Add api transport for Mailjet · symfony/symfony@87cd915 · GitHub
[go: up one dir, main page]

Skip to content

Commit 87cd915

Browse files
committed
[Mailer] Add api transport for Mailjet
1 parent 51b3f67 commit 87cd915

File tree

5 files changed

+168
-11
lines changed

5 files changed

+168
-11
lines changed
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
CHANGELOG
22
=========
33

4-
5.2
4+
5.2.0
55
-----
66

77
* Added the bridge
8-
9-
* Configuration example :
10-
11-
```dotenv
12-
MAILER_DSN=mailjet+api://$MJ_API_PUBLIC_KEY:$MJ_API_PRIVATE_KEY@api.mailjet.com
13-
```

src/Symfony/Component/Mailer/Bridge/Mailjet/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
Mailjet Bridge
2-
===============
2+
==============
33

44
Provides Mailjet integration for Symfony Mailer.
55

6+
7+
8+
Configuration examples :
9+
---------
10+
11+
```dotenv
12+
# Api usage
13+
MAILER_DSN=mailjet+api://$PUBLIC_KEY:$PRIVATE_KEY@default
14+
# Smtp usage
15+
MAILER_DSN=mailjet+smtp://$PUBLIC_KEY:$PRIVATE_KEY@default
16+
```
17+
618
Resources
719
---------
820

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Mailer\Bridge\Mailjet\Transport;
13+
14+
use Psr\Log\LoggerInterface;
15+
use Symfony\Component\Mailer\Envelope;
16+
use Symfony\Component\Mailer\Exception\HttpTransportException;
17+
use Symfony\Component\Mailer\SentMessage;
18+
use Symfony\Component\Mailer\Transport\AbstractApiTransport;
19+
use Symfony\Component\Mime\Address;
20+
use Symfony\Component\Mime\Email;
21+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
22+
use Symfony\Contracts\HttpClient\HttpClientInterface;
23+
use Symfony\Contracts\HttpClient\ResponseInterface;
24+
25+
class MailjetApiTransport extends AbstractApiTransport
26+
{
27+
private const HOST = 'api.mailjet.com';
28+
private const API_VERSION = '3.1';
29+
30+
private $privateKey;
31+
private $publicKey;
32+
33+
public function __construct(string $publicKey, string $privateKey, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null, LoggerInterface $logger = null)
34+
{
35+
$this->publicKey = $publicKey;
36+
$this->privateKey = $privateKey;
37+
38+
parent::__construct($client, $dispatcher, $logger);
39+
}
40+
41+
public function __toString(): string
42+
{
43+
return sprintf('mailjet+api://%s', $this->getEndpoint());
44+
}
45+
46+
protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface
47+
{
48+
$response = $this->client->request('POST', sprintf('https://%s/v%s/send', $this->getEndpoint(), self::API_VERSION), [
49+
'headers' => [
50+
'Accept' => 'application/json',
51+
],
52+
'auth_basic' => $this->publicKey.':'.$this->privateKey,
53+
'json' => $this->getPayload($email, $envelope),
54+
]);
55+
56+
$result = $response->toArray(false);
57+
58+
if (200 !== $response->getStatusCode()) {
59+
if ('application/json' === $response->getHeaders(false)['content-type'][0]) {
60+
throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $result['Message'], $response->getStatusCode()), $response);
61+
}
62+
63+
throw new HttpTransportException(sprintf('Unable to send an email: %s (code %d).', $response->getContent(false), $response->getStatusCode()), $response);
64+
}
65+
66+
// The response needs to contains a 'Messages' key that is an array
67+
if (!array_key_exists('Messages', $result) || !is_array($result['Messages']) || 0 === count($result['Messages'])) {
68+
throw new HttpTransportException(sprintf('Unable to send an email: %s malformed api response', $response->getContent(false)), $response);
69+
}
70+
71+
$sentMessage->setMessageId($response->getHeaders(false)['x-mj-request-guid'][0]);
72+
73+
return $response;
74+
}
75+
76+
private function getPayload(Email $email, Envelope $envelope): array
77+
{
78+
$html = $email->getHtmlBody();
79+
if (null !== $html && \is_resource($html)) {
80+
if (stream_get_meta_data($html)['seekable'] ?? false) {
81+
rewind($html);
82+
}
83+
$html = stream_get_contents($html);
84+
}
85+
[$attachments, $inlines, $html] = $this->prepareAttachments($email, $html);
86+
87+
$message = [
88+
'From' => [
89+
'Email' => $envelope->getSender()->getAddress(),
90+
'Name' => $envelope->getSender()->getName(),
91+
],
92+
'To' => array_map(function (Address $recipient) {
93+
return [
94+
'Email' => $recipient->getAddress(),
95+
'Name' => $recipient->getName(),
96+
];
97+
}, $this->getRecipients($email, $envelope)),
98+
'Subject' => $email->getSubject(),
99+
'Attachments' => $attachments,
100+
'InlinedAttachments' => $inlines,
101+
];
102+
if ($emails = $email->getCc()) {
103+
$message['Cc'] = implode(',', $this->stringifyAddresses($emails));
104+
}
105+
if ($emails = $email->getBcc()) {
106+
$message['Bcc'] = implode(',', $this->stringifyAddresses($emails));
107+
}
108+
if ($email->getTextBody()) {
109+
$message['TextPart'] = $email->getTextBody();
110+
}
111+
if ($html) {
112+
$message['HTMLPart'] = $html;
113+
}
114+
115+
return [
116+
'Messages' => [$message],
117+
];
118+
}
119+
120+
private function prepareAttachments(Email $email, ?string $html): array
121+
{
122+
$attachments = $inlines = [];
123+
foreach ($email->getAttachments() as $attachment) {
124+
$headers = $attachment->getPreparedHeaders();
125+
$filename = $headers->getHeaderParameter('Content-Disposition', 'filename');
126+
$formattedAttachment = [
127+
'ContentType' => $attachment->getMediaType().'/'.$attachment->getMediaSubtype(),
128+
'Filename' => $filename,
129+
'Base64Content' => $attachment->bodyToString(),
130+
];
131+
if ('inline' === $headers->getHeaderBody('Content-Disposition')) {
132+
$inlines[] = $formattedAttachment;
133+
} else {
134+
$attachments[] = $formattedAttachment;
135+
}
136+
}
137+
138+
return [$attachments, $inlines, $html];
139+
}
140+
141+
private function getEndpoint(): ?string
142+
{
143+
return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : '');
144+
}
145+
}

src/Symfony/Component/Mailer/Bridge/Mailjet/Transport/MailjetTransportFactory.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Mailer\Transport\AbstractTransportFactory;
1616
use Symfony\Component\Mailer\Transport\Dsn;
1717
use Symfony\Component\Mailer\Transport\TransportInterface;
18+
use function in_array;
1819

1920
class MailjetTransportFactory extends AbstractTransportFactory
2021
{
@@ -23,8 +24,13 @@ public function create(Dsn $dsn): TransportInterface
2324
$scheme = $dsn->getScheme();
2425
$user = $this->getUser($dsn);
2526
$password = $this->getPassword($dsn);
27+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
2628

27-
if ('mailjet+smtp' === $scheme || 'mailjet+smtps' === $scheme || 'mailjet' === $scheme) {
29+
if ('maijlet+api' === $scheme) {
30+
return (new MailjetApiTransport($user, $password, $this->client, $this->dispatcher, $this->logger))->setHost($host);
31+
}
32+
33+
if (in_array($scheme, ['mailjet+smtp', 'mailjet+smtps', 'mailjet'])) {
2834
return new MailjetSmtpTransport($user, $password, $this->dispatcher, $this->logger);
2935
}
3036

@@ -33,6 +39,6 @@ public function create(Dsn $dsn): TransportInterface
3339

3440
protected function getSupportedSchemes(): array
3541
{
36-
return ['mailjet', 'mailjet+smtp', 'mailjet+smtps'];
42+
return ['mailjet', 'mailjet+api', 'mailjet+smtp', 'mailjet+smtps'];
3743
}
3844
}

src/Symfony/Component/Mailer/Bridge/Mailjet/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"minimum-stability": "dev",
3434
"extra": {
3535
"branch-alias": {
36-
"dev-master": "5.1-dev"
36+
"dev-master": "5.2-dev"
3737
}
3838
}
3939
}

0 commit comments

Comments
 (0)
0