Skip to content
29 changes: 22 additions & 7 deletions src/Utopia/Messaging/Adapter/Email/Mailgun.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,36 @@ protected function process(EmailMessage $message): string

$domain = $this->isEU ? $euDomain : $usDomain;

$body = [
'to' => \implode(',', $message->getTo()),
'from' => "{$message->getFromName()}<{$message->getFromEmail()}>",
'subject' => $message->getSubject(),
'text' => $message->isHtml() ? null : $message->getContent(),
'html' => $message->isHtml() ? $message->getContent() : null,
];

if (! \is_null($message->getCC())) {
foreach ($message->getCC() as $cc) {
$body['cc'] = "{$body['cc']},{$cc['name']}<{$cc['email']}>";
}
}

if (! \is_null($message->getBCC())) {
foreach ($message->getBCC() as $bcc) {
$body['bcc'] = "{$body['bcc']},{$bcc['name']}<{$bcc['email']}>";
}
}

$response = new Response($this->getType());

$result = $this->request(
method: 'POST',
url: "https://$domain/v3/{$this->domain}/messages",
headers: [
'Authorization: Basic '.base64_encode('api:'.$this->apiKey),
'h:Reply-To: '."{$message->getReplyToName()}<{$message->getReplyToEmail()}>",
],
body: \http_build_query([
'to' => \implode(',', $message->getTo()),
'from' => $message->getFrom(),
'subject' => $message->getSubject(),
'text' => $message->isHtml() ? null : $message->getContent(),
'html' => $message->isHtml() ? $message->getContent() : null,
]),
body: \http_build_query($body),
);

$statusCode = $result['statusCode'];
Expand Down
4 changes: 2 additions & 2 deletions src/Utopia/Messaging/Adapter/Email/Mock.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ protected function process(EmailMessage $message): string
$mail->Subject = $message->getSubject();
$mail->Body = $message->getContent();
$mail->AltBody = \strip_tags($message->getContent());
$mail->setFrom($message->getFrom(), 'Utopia');
$mail->addReplyTo($message->getFrom(), 'Utopia');
$mail->setFrom($message->getFromEmail(), $message->getFromName());
$mail->addReplyTo($message->getReplyToEmail(), $message->getReplyToName());
$mail->isHTML($message->isHtml());

foreach ($message->getTo() as $to) {
Expand Down
43 changes: 34 additions & 9 deletions src/Utopia/Messaging/Adapter/Email/Sendgrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,34 @@ public function getMaxMessagesPerRequest(): int
*/
protected function process(EmailMessage $message): string
{
$personalizations = [
[
'to' => \array_map(
fn ($to) => ['email' => $to],
$message->getTo()
),
'subject' => $message->getSubject(),
],
];

if (! \is_null($message->getCC())) {
foreach ($message->getCC() as $cc) {
$personalizations[0]['cc'][] = [
'name' => $cc['name'],
'email' => $cc['email'],
];
}
}

if (! \is_null($message->getBCC())) {
foreach ($message->getBCC() as $bcc) {
$personalizations[0]['bcc'][] = [
'name' => $bcc['name'],
'email' => $bcc['email'],
];
}
}

$response = new Response($this->getType());
$result = $this->request(
method: 'POST',
Expand All @@ -46,17 +74,14 @@ protected function process(EmailMessage $message): string
'Content-Type: application/json',
],
body: \json_encode([
'personalizations' => [
[
'to' => \array_map(
fn ($to) => ['email' => $to],
$message->getTo()
),
'subject' => $message->getSubject(),
],
'personalizations' => $personalizations,
'reply_to' => [
'name' => $message->getReplyToName(),
'email' => $message->getReplyToEmail(),
],
'from' => [
'email' => $message->getFrom(),
'name' => $message->getFromName(),
'email' => $message->getFromEmail(),
],
'content' => [
[
Expand Down
74 changes: 70 additions & 4 deletions src/Utopia/Messaging/Messages/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,53 @@ class Email implements Message
* @param array<string> $to The recipients of the email.
* @param string $subject The subject of the email.
* @param string $content The content of the email.
* @param string|null $from The sender of the email.
* @param string $fromName The name of the sender.
* @param string $fromEmail The email address of the sender.
* @param array<array<string,string>>|null $cc . The CC recipients of the email. Each recipient should be an array containing a "name" and an "email" key.
* @param array<array<string,string>>|null $bcc . The BCC recipients of the email. Each recipient should be an array containing a "name" and an "email" key.
* @param string|null $replyToName The name of the reply to.
* @param string|null $replyToEmail The email address of the reply to.
* @param array<string, mixed>|null $attachments The attachments of the email.
* @param bool $html Whether the message is HTML or not.
*
* @throws \InvalidArgumentException
*/
public function __construct(
private array $to,
private string $subject,
private string $content,
private ?string $from = null,
private string $fromName,
private string $fromEmail,
private ?string $replyToName = null,
private ?string $replyToEmail = null,
private ?array $cc = null,
private ?array $bcc = null,
private ?array $attachments = null,
private bool $html = false
) {
if (\is_null($this->replyToName)) {
$this->replyToName = $this->fromName;
}

if (\is_null($this->replyToEmail)) {
$this->replyToEmail = $this->fromEmail;
}

if (! \is_null($this->cc)) {
foreach ($this->cc as $recipient) {
if (! isset($recipient['name']) || ! isset($recipient['email'])) {
throw new \InvalidArgumentException('Each recipient in cc must have a name and email');
}
}
}

if (! \is_null($this->bcc)) {
foreach ($this->bcc as $recipient) {
if (! isset($recipient['name']) || ! isset($recipient['email'])) {
throw new \InvalidArgumentException('Each recipient in bcc must have a name and email');
}
}
}
}

/**
Expand All @@ -42,9 +77,40 @@ public function getContent(): string
return $this->content;
}

public function getFrom(): ?string
public function getFromName(): string
{
return $this->fromName;
}

public function getFromEmail(): string
{
return $this->fromEmail;
}

public function getReplyToName(): string
{
return $this->replyToName;
}

public function getReplyToEmail(): string
{
return $this->replyToEmail;
}

/**
* @return array<array<string, string>>|null
*/
public function getCC(): ?array
{
return $this->cc;
}

/**
* @return array<array<string, string>>|null
*/
public function getBCC(): ?array
{
return $this->from;
return $this->bcc;
}

/**
Expand Down
12 changes: 12 additions & 0 deletions src/Utopia/Messaging/Response.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,16 @@ public function toArray(): array
'results' => $this->results,
];
}

/**
* @param array<array<string, string>> $results
*/
public function fromArray(array $results): self
{
$response = new self($this->type);
$response->deliveredTo = $this->deliveredTo;
$response->results = $this->results;

return $response;
}
}
8 changes: 5 additions & 3 deletions tests/Messaging/Adapter/Email/EmailTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ public function testSendEmail(): void
$to = 'tester@localhost.test';
$subject = 'Test Subject';
$content = 'Test Content';
$from = 'sender@localhost.test';
$fromName = 'Test Sender';
$fromEmail = 'sender@localhost.test';

$message = new Email(
to: [$to],
subject: $subject,
content: $content,
from: $from
fromName: $fromName,
fromEmail: $fromEmail,
);

$response = \json_decode($sender->send($message), true);
Expand All @@ -30,7 +32,7 @@ public function testSendEmail(): void

$this->assertResponse($response);
$this->assertEquals($to, $lastEmail['to'][0]['address']);
$this->assertEquals($from, $lastEmail['from'][0]['address']);
$this->assertEquals($fromEmail, $lastEmail['from'][0]['address']);
$this->assertEquals($subject, $lastEmail['subject']);
$this->assertEquals($content, \trim($lastEmail['text']));
}
Expand Down
5 changes: 3 additions & 2 deletions tests/Messaging/Adapter/Email/MailgunTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,14 @@ public function testSendEmail(): void
$to = \getenv('TEST_EMAIL');
$subject = 'Test Subject';
$content = 'Test Content';
$from = 'sender@'.$domain;
$fromEmail = 'sender@'.$domain;

$message = new Email(
to: [$to],
subject: $subject,
content: $content,
from: $from,
fromName: 'Test Sender',
fromEmail: $fromEmail,
);

$response = \json_decode($sender->send($message), true);
Expand Down
5 changes: 3 additions & 2 deletions tests/Messaging/Adapter/Email/SendgridTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ public function testSendEmail(): void
$to = \getenv('TEST_EMAIL');
$subject = 'Test Subject';
$content = 'Test Content';
$from = \getenv('TEST_FROM_EMAIL');
$fromEmail = \getenv('TEST_FROM_EMAIL');

$message = new Email(
to: [$to],
subject: $subject,
content: $content,
from: $from,
fromName: 'prateek',
fromEmail: $fromEmail,
);

$response = \json_decode($sender->send($message), true);
Expand Down