From 0cf2bd3ca24e833815dbd4d00bd346ee2622814c Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 09:05:55 +0530 Subject: [PATCH 01/10] feat: added bcc only emails to smtp --- .env.dev | 1 + .github/workflows/test.yml | 1 + docker-compose.yml | 1 + src/Utopia/Messaging/Adapter/Email/SMTP.php | 12 ++++++- tests/Messaging/Adapter/Email/SMTPTest.php | 40 +++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/.env.dev b/.env.dev index 36923f8..3ee1abb 100644 --- a/.env.dev +++ b/.env.dev @@ -1,3 +1,4 @@ +SMTP_DEFAULT_RECIPIENT= MAILGUN_API_KEY= MAILGUN_DOMAIN= SENDGRID_API_KEY= diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9df4bf4..52fc6bf 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,7 @@ jobs: - run: git checkout HEAD^2 - name: Run Tests env: + SMTP_DEFAULT_RECIPIENT: ${{ secrets.SMTP_DEFAULT_RECIPIENT }} MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} diff --git a/docker-compose.yml b/docker-compose.yml index 1756221..613f10b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,7 @@ services: - ./tests:/usr/local/src/tests - ./phpunit.xml:/usr/local/src/phpunit.xml environment: + - SMTP_DEFAULT_RECIPIENT - MAILGUN_API_KEY - MAILGUN_DOMAIN - SENDGRID_API_KEY diff --git a/src/Utopia/Messaging/Adapter/Email/SMTP.php b/src/Utopia/Messaging/Adapter/Email/SMTP.php index fc569a4..48a24ff 100644 --- a/src/Utopia/Messaging/Adapter/Email/SMTP.php +++ b/src/Utopia/Messaging/Adapter/Email/SMTP.php @@ -19,6 +19,7 @@ class SMTP extends EmailAdapter * @param string $smtpSecure SMTP Secure prefix. Can be '', 'ssl' or 'tls' * @param bool $smtpAutoTLS Enable/disable SMTP AutoTLS feature. Defaults to false. * @param string $xMailer The value to use for the X-Mailer header. + * @param string $defaultRecipient The default recipient to use for the email. */ public function __construct( private string $host, @@ -27,7 +28,8 @@ public function __construct( private string $password = '', private string $smtpSecure = '', private bool $smtpAutoTLS = false, - private string $xMailer = '' + private string $xMailer = '', + private string $defaultRecipient = '' ) { if (!\in_array($this->smtpSecure, ['', 'ssl', 'tls'])) { throw new \InvalidArgumentException('Invalid SMTP secure prefix. Must be "", "ssl" or "tls"'); @@ -72,6 +74,14 @@ protected function process(EmailMessage $message): array $mail->AltBody = \strip_tags($mail->AltBody); $mail->AltBody = \trim($mail->AltBody); + if (empty($message->getTo())) { + if (empty($message->getBCC()) || empty($this->defaultRecipient)) { + throw new \Exception('Email requires either "to" recipients or both BCC and a default recipient configurations'); + } + + $mail->addAddress($this->defaultRecipient); + } + foreach ($message->getTo() as $to) { $mail->addAddress($to); } diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index f9aeeeb..45273e0 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -78,4 +78,44 @@ public function testSendEmailWithAttachment(): void $this->assertEquals($subject, $lastEmail['subject']); $this->assertEquals($content, \trim($lastEmail['text'])); } + + public function testSendEmailOnlyBCC(): void + { + $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?? 'placeholder@localhost.test'; + $sender = new SMTP( + host: 'maildev', + port: 1025, + defaultRecipient: $defaultRecipient, + ); + + $subject = 'Test Subject'; + $content = 'Test Content'; + $fromName = 'Test Sender'; + $fromEmail = 'sender@localhost.test'; + $bcc = [ + [ + 'email' => 'tester@localhost.test', + 'name' => 'Test Recipient', + ], + ]; + + $message = new Email( + to: [], + subject: $subject, + content: $content, + fromName: $fromName, + fromEmail: $fromEmail, + bcc: $bcc, + ); + + $response = $sender->send($message); + + $lastEmail = $this->getLastEmail(); + + $this->assertResponse($response); + $this->assertEquals($defaultRecipient, $lastEmail['to'][0]['address']); + $this->assertEquals($fromEmail, $lastEmail['from'][0]['address']); + $this->assertEquals($subject, $lastEmail['subject']); + $this->assertEquals($content, \trim($lastEmail['text'])); + } } From cb96f41b2c7bc978829ed6ae98512990f0e607b8 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 09:14:26 +0530 Subject: [PATCH 02/10] chore: fix analyze --- tests/Messaging/Adapter/Email/SMTPTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 45273e0..32b57ef 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -81,7 +81,7 @@ public function testSendEmailWithAttachment(): void public function testSendEmailOnlyBCC(): void { - $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?? 'placeholder@localhost.test'; + $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'placeholder@localhost.test'; $sender = new SMTP( host: 'maildev', port: 1025, From bafb8b8ee1966d8c4a2b42f593d76bfa5e50a2c1 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 09:22:42 +0530 Subject: [PATCH 03/10] chore: fix tests --- tests/Messaging/Adapter/Email/SMTPTest.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 32b57ef..4f7dd91 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -110,12 +110,8 @@ public function testSendEmailOnlyBCC(): void $response = $sender->send($message); - $lastEmail = $this->getLastEmail(); - - $this->assertResponse($response); - $this->assertEquals($defaultRecipient, $lastEmail['to'][0]['address']); - $this->assertEquals($fromEmail, $lastEmail['from'][0]['address']); - $this->assertEquals($subject, $lastEmail['subject']); - $this->assertEquals($content, \trim($lastEmail['text'])); + $this->assertEquals(0, $response['deliveredTo'], \var_export($response, true)); + $this->assertEquals('email', $response['type'], \var_export($response, true)); + $this->assertEquals('', $response['results'][0]['error'], \var_export($response, true)); } } From cec2956de9c3e59cc1f41e43b4f393630941b7ed Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 10:25:25 +0530 Subject: [PATCH 04/10] chore: fix default recipient --- tests/Messaging/Adapter/Email/SMTPTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 4f7dd91..51883c0 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -81,7 +81,7 @@ public function testSendEmailWithAttachment(): void public function testSendEmailOnlyBCC(): void { - $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'placeholder@localhost.test'; + $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'tester@localhost.test'; $sender = new SMTP( host: 'maildev', port: 1025, From c42b0d30b75423e1396ffef41c0f434d0105e039 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 11:00:22 +0530 Subject: [PATCH 05/10] chore: update tests --- src/Utopia/Messaging/Adapter/Email/SMTP.php | 10 ++++++++++ tests/Messaging/Adapter/Email/SMTPTest.php | 20 ++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Utopia/Messaging/Adapter/Email/SMTP.php b/src/Utopia/Messaging/Adapter/Email/SMTP.php index 48a24ff..2c4bed1 100644 --- a/src/Utopia/Messaging/Adapter/Email/SMTP.php +++ b/src/Utopia/Messaging/Adapter/Email/SMTP.php @@ -122,6 +122,10 @@ protected function process(EmailMessage $message): array if ($sent) { $response->setDeliveredTo(\count($message->getTo())); + + if (empty($message->getTo())) { + $response->setDeliveredTo(\count($message->getBCC())); + } } foreach ($message->getTo() as $to) { @@ -132,6 +136,12 @@ protected function process(EmailMessage $message): array $response->addResult($to, $sent ? '' : $error); } + if (empty($message->getTo())) { + foreach ($message->getBCC() as $bcc) { + $response->addResult($bcc['email'], $sent ? '' : 'Unknown error'); + } + } + return $response->toArray(); } } diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 51883c0..377f3ac 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -28,6 +28,12 @@ public function testSendEmail(): void content: $content, fromName: $fromName, fromEmail: $fromEmail, + bcc: [ + [ + 'email' => 'tester2@localhost.test', + 'name' => 'Test Recipient 2', + ], + ], ); $response = $sender->send($message); @@ -54,7 +60,6 @@ public function testSendEmailWithAttachment(): void $fromName = 'Test Sender'; $fromEmail = 'sender@localhost.test'; - $message = new Email( to: [$to], subject: $subject, @@ -94,8 +99,8 @@ public function testSendEmailOnlyBCC(): void $fromEmail = 'sender@localhost.test'; $bcc = [ [ - 'email' => 'tester@localhost.test', - 'name' => 'Test Recipient', + 'email' => 'tester2@localhost.test', + 'name' => 'Test Recipient 2', ], ]; @@ -110,8 +115,11 @@ public function testSendEmailOnlyBCC(): void $response = $sender->send($message); - $this->assertEquals(0, $response['deliveredTo'], \var_export($response, true)); - $this->assertEquals('email', $response['type'], \var_export($response, true)); - $this->assertEquals('', $response['results'][0]['error'], \var_export($response, true)); + $lastEmail = $this->getLastEmail(); + + $this->assertResponse($response); + $this->assertEquals($fromEmail, $lastEmail['from'][0]['address']); + $this->assertEquals($subject, $lastEmail['subject']); + $this->assertEquals($content, \trim($lastEmail['text'])); } } From 01736ac71f7b4e142f4372460b6e683e7b492d1a Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 11:01:14 +0530 Subject: [PATCH 06/10] chore: fix tests --- tests/Messaging/Adapter/Email/SMTPTest.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 377f3ac..77650f1 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -27,13 +27,7 @@ public function testSendEmail(): void subject: $subject, content: $content, fromName: $fromName, - fromEmail: $fromEmail, - bcc: [ - [ - 'email' => 'tester2@localhost.test', - 'name' => 'Test Recipient 2', - ], - ], + fromEmail: $fromEmail ); $response = $sender->send($message); From 540192c00addacfad22076544e64b17397f00afc Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Mon, 17 Feb 2025 11:01:30 +0530 Subject: [PATCH 07/10] chore: fix tests --- tests/Messaging/Adapter/Email/SMTPTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 77650f1..7bd8eda 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -27,7 +27,7 @@ public function testSendEmail(): void subject: $subject, content: $content, fromName: $fromName, - fromEmail: $fromEmail + fromEmail: $fromEmail, ); $response = $sender->send($message); From d5f59ff2c1d9ae04d6aef1a2709e3d89719a7e2b Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 18 Feb 2025 13:46:28 +0530 Subject: [PATCH 08/10] chore: aggregated to, cc and bcc delivered to --- src/Utopia/Messaging/Adapter/Email/SMTP.php | 31 ++++++++++++--------- src/Utopia/Messaging/Messages/Email.php | 9 +++++- tests/Messaging/Adapter/Email/SMTPTest.php | 4 +-- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/Utopia/Messaging/Adapter/Email/SMTP.php b/src/Utopia/Messaging/Adapter/Email/SMTP.php index 2c4bed1..dd1edf7 100644 --- a/src/Utopia/Messaging/Adapter/Email/SMTP.php +++ b/src/Utopia/Messaging/Adapter/Email/SMTP.php @@ -19,7 +19,6 @@ class SMTP extends EmailAdapter * @param string $smtpSecure SMTP Secure prefix. Can be '', 'ssl' or 'tls' * @param bool $smtpAutoTLS Enable/disable SMTP AutoTLS feature. Defaults to false. * @param string $xMailer The value to use for the X-Mailer header. - * @param string $defaultRecipient The default recipient to use for the email. */ public function __construct( private string $host, @@ -29,7 +28,6 @@ public function __construct( private string $smtpSecure = '', private bool $smtpAutoTLS = false, private string $xMailer = '', - private string $defaultRecipient = '' ) { if (!\in_array($this->smtpSecure, ['', 'ssl', 'tls'])) { throw new \InvalidArgumentException('Invalid SMTP secure prefix. Must be "", "ssl" or "tls"'); @@ -75,11 +73,11 @@ protected function process(EmailMessage $message): array $mail->AltBody = \trim($mail->AltBody); if (empty($message->getTo())) { - if (empty($message->getBCC()) || empty($this->defaultRecipient)) { + if (empty($message->getBCC()) && empty($message->getDefaultRecipient())) { throw new \Exception('Email requires either "to" recipients or both BCC and a default recipient configurations'); } - $mail->addAddress($this->defaultRecipient); + $mail->addAddress($message->getDefaultRecipient()); } foreach ($message->getTo() as $to) { @@ -121,11 +119,8 @@ protected function process(EmailMessage $message): array $sent = $mail->send(); if ($sent) { - $response->setDeliveredTo(\count($message->getTo())); - - if (empty($message->getTo())) { - $response->setDeliveredTo(\count($message->getBCC())); - } + $totalDelivered = \count($message->getTo() ?? []) + \count($message->getCC() ?? []) + \count($message->getBCC() ?? []); + $response->setDeliveredTo($totalDelivered); } foreach ($message->getTo() as $to) { @@ -136,10 +131,20 @@ protected function process(EmailMessage $message): array $response->addResult($to, $sent ? '' : $error); } - if (empty($message->getTo())) { - foreach ($message->getBCC() as $bcc) { - $response->addResult($bcc['email'], $sent ? '' : 'Unknown error'); - } + foreach ($message->getCC() as $cc) { + $error = empty($mail->ErrorInfo) + ? 'Unknown error' + : $mail->ErrorInfo; + + $response->addResult($cc['email'], $sent ? '' : $error); + } + + foreach ($message->getBCC() as $bcc) { + $error = empty($mail->ErrorInfo) + ? 'Unknown error' + : $mail->ErrorInfo; + + $response->addResult($bcc['email'], $sent ? '' : $error); } return $response->toArray(); diff --git a/src/Utopia/Messaging/Messages/Email.php b/src/Utopia/Messaging/Messages/Email.php index c53b540..9d359b9 100644 --- a/src/Utopia/Messaging/Messages/Email.php +++ b/src/Utopia/Messaging/Messages/Email.php @@ -19,6 +19,7 @@ class Email implements Message * @param string|null $replyToEmail The email address of the reply to. * @param array|null $attachments The attachments of the email. * @param bool $html Whether the message is HTML or not. + * @param string|null $defaultRecipient The default recipient of the email. * * @throws \InvalidArgumentException */ @@ -33,7 +34,8 @@ public function __construct( private ?array $cc = null, private ?array $bcc = null, private ?array $attachments = null, - private bool $html = false + private bool $html = false, + private ?string $defaultRecipient = null ) { if (\is_null($this->replyToName)) { $this->replyToName = $this->fromName; @@ -126,4 +128,9 @@ public function isHtml(): bool { return $this->html; } + + public function getDefaultRecipient(): ?string + { + return $this->defaultRecipient; + } } diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 7bd8eda..8e2d0d1 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -80,11 +80,9 @@ public function testSendEmailWithAttachment(): void public function testSendEmailOnlyBCC(): void { - $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'tester@localhost.test'; $sender = new SMTP( host: 'maildev', port: 1025, - defaultRecipient: $defaultRecipient, ); $subject = 'Test Subject'; @@ -97,6 +95,7 @@ public function testSendEmailOnlyBCC(): void 'name' => 'Test Recipient 2', ], ]; + $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'tester@localhost.test'; $message = new Email( to: [], @@ -105,6 +104,7 @@ public function testSendEmailOnlyBCC(): void fromName: $fromName, fromEmail: $fromEmail, bcc: $bcc, + defaultRecipient: $defaultRecipient, ); $response = $sender->send($message); From 892c94000e955bf9d512d116867882d8ec32daf6 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 18 Feb 2025 13:50:27 +0530 Subject: [PATCH 09/10] chore: fix test --- src/Utopia/Messaging/Adapter/Email/SMTP.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utopia/Messaging/Adapter/Email/SMTP.php b/src/Utopia/Messaging/Adapter/Email/SMTP.php index dd1edf7..b400024 100644 --- a/src/Utopia/Messaging/Adapter/Email/SMTP.php +++ b/src/Utopia/Messaging/Adapter/Email/SMTP.php @@ -119,7 +119,7 @@ protected function process(EmailMessage $message): array $sent = $mail->send(); if ($sent) { - $totalDelivered = \count($message->getTo() ?? []) + \count($message->getCC() ?? []) + \count($message->getBCC() ?? []); + $totalDelivered = \count($message->getTo()) + \count($message->getCC() ?: []) + \count($message->getBCC() ?: []); $response->setDeliveredTo($totalDelivered); } From 3694fcf28e0dd16c8d0eddbe355aa940e58b3522 Mon Sep 17 00:00:00 2001 From: ChiragAgg5k Date: Tue, 18 Feb 2025 13:56:50 +0530 Subject: [PATCH 10/10] chore: removed env --- .env.dev | 1 - .github/workflows/test.yml | 1 - docker-compose.yml | 1 - tests/Messaging/Adapter/Email/SMTPTest.php | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.env.dev b/.env.dev index 3ee1abb..36923f8 100644 --- a/.env.dev +++ b/.env.dev @@ -1,4 +1,3 @@ -SMTP_DEFAULT_RECIPIENT= MAILGUN_API_KEY= MAILGUN_DOMAIN= SENDGRID_API_KEY= diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 52fc6bf..9df4bf4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,6 @@ jobs: - run: git checkout HEAD^2 - name: Run Tests env: - SMTP_DEFAULT_RECIPIENT: ${{ secrets.SMTP_DEFAULT_RECIPIENT }} MAILGUN_API_KEY: ${{ secrets.MAILGUN_API_KEY }} MAILGUN_DOMAIN: ${{ secrets.MAILGUN_DOMAIN }} SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }} diff --git a/docker-compose.yml b/docker-compose.yml index 613f10b..1756221 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,6 @@ services: - ./tests:/usr/local/src/tests - ./phpunit.xml:/usr/local/src/phpunit.xml environment: - - SMTP_DEFAULT_RECIPIENT - MAILGUN_API_KEY - MAILGUN_DOMAIN - SENDGRID_API_KEY diff --git a/tests/Messaging/Adapter/Email/SMTPTest.php b/tests/Messaging/Adapter/Email/SMTPTest.php index 8e2d0d1..ea0ec0e 100644 --- a/tests/Messaging/Adapter/Email/SMTPTest.php +++ b/tests/Messaging/Adapter/Email/SMTPTest.php @@ -85,6 +85,7 @@ public function testSendEmailOnlyBCC(): void port: 1025, ); + $defaultRecipient = 'tester@localhost.test'; $subject = 'Test Subject'; $content = 'Test Content'; $fromName = 'Test Sender'; @@ -95,7 +96,6 @@ public function testSendEmailOnlyBCC(): void 'name' => 'Test Recipient 2', ], ]; - $defaultRecipient = \getenv('SMTP_DEFAULT_RECIPIENT') ?: 'tester@localhost.test'; $message = new Email( to: [],