Skip to content

Commit c844dcb

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

File tree

5 files changed

+167
-11
lines changed

5 files changed

+167
-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: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ public function create(Dsn $dsn): TransportInterface
2323
$scheme = $dsn->getScheme();
2424
$user = $this->getUser($dsn);
2525
$password = $this->getPassword($dsn);
26+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
2627

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

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

3439
protected function getSupportedSchemes(): array
3540
{
36-
return ['mailjet', 'mailjet+smtp', 'mailjet+smtps'];
41+
return ['mailjet', 'mailjet+api', 'mailjet+smtp', 'mailjet+smtps'];
3742
}
3843
}

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy