From 0fb66b4b557199d4c0c7e796bc4dee374cc5de25 Mon Sep 17 00:00:00 2001 From: Icaro Ferreira Date: Mon, 8 Sep 2025 12:06:13 -0300 Subject: [PATCH 1/4] feat: Change result response --- src/SendResult.php | 11 +++++++++++ src/Wrapper/AmazonSesWrapper.php | 20 ++++++++++++++++---- src/Wrapper/MailWrapperInterface.php | 3 ++- src/Wrapper/MailgunApiWrapper.php | 13 ++++++++----- src/Wrapper/PHPMailerWrapper.php | 11 ++++++----- src/Wrapper/SendMailWrapper.php | 9 +++++---- tests/MailgunWrapperTest.php | 7 ++++--- 7 files changed, 52 insertions(+), 22 deletions(-) create mode 100644 src/SendResult.php diff --git a/src/SendResult.php b/src/SendResult.php new file mode 100644 index 0000000..87c1b0a --- /dev/null +++ b/src/SendResult.php @@ -0,0 +1,11 @@ +validate($envelope); @@ -61,6 +62,17 @@ public function send(Envelope $envelope): bool ] ); - return true; + $messageId = null; + $messageLines = explode("\n", str_replace(["\r\n", "\r"], "\n", $message)); + + foreach ($messageLines as $line) { + if (stripos($line, 'Message-ID:') === 0) { + $value = trim(substr($line, strlen('Message-ID:'))); + $messageId = trim($value, "<> \t\r\n"); + break; + } + } + + return new SendResult(true, $messageId); } } diff --git a/src/Wrapper/MailWrapperInterface.php b/src/Wrapper/MailWrapperInterface.php index 3803881..9648439 100644 --- a/src/Wrapper/MailWrapperInterface.php +++ b/src/Wrapper/MailWrapperInterface.php @@ -3,10 +3,11 @@ namespace ByJG\Mail\Wrapper; use ByJG\Mail\Envelope; +use ByJG\Mail\SendResult; interface MailWrapperInterface { public static function schema(): array; - public function send(Envelope $envelope): bool; + public function send(Envelope $envelope): SendResult; } diff --git a/src/Wrapper/MailgunApiWrapper.php b/src/Wrapper/MailgunApiWrapper.php index 8e87cd0..cdea6f2 100644 --- a/src/Wrapper/MailgunApiWrapper.php +++ b/src/Wrapper/MailgunApiWrapper.php @@ -5,6 +5,7 @@ use ByJG\Mail\Envelope; use ByJG\Mail\Exception\InvalidEMailException; use ByJG\Mail\Exception\MailApiException; +use ByJG\Mail\SendResult; use ByJG\WebRequest\Exception\MessageException; use ByJG\WebRequest\Exception\NetworkException; use ByJG\WebRequest\Exception\RequestException; @@ -62,15 +63,15 @@ public function getRequestObject(): RequestInterface * malgun://api:APIKEY@DOMAINNAME * * @param Envelope $envelope - * @return bool + * @return SendResult + * @throws ClientExceptionInterface * @throws InvalidEMailException * @throws MailApiException * @throws MessageException - * @throws RequestException * @throws NetworkException - * @throws ClientExceptionInterface + * @throws RequestException */ - public function send(Envelope $envelope): bool + public function send(Envelope $envelope): SendResult { $this->validate($envelope); @@ -118,7 +119,9 @@ public function send(Envelope $envelope): bool throw new MailApiException('Mailgun: ' . $resultJson['message']); } - return true; + $messageId = $resultJson['id']; + + return new SendResult(true, $messageId); } private function getApiUri() diff --git a/src/Wrapper/PHPMailerWrapper.php b/src/Wrapper/PHPMailerWrapper.php index cae884b..43072f1 100644 --- a/src/Wrapper/PHPMailerWrapper.php +++ b/src/Wrapper/PHPMailerWrapper.php @@ -6,6 +6,7 @@ use ByJG\Mail\Exception\InvalidEMailException; use ByJG\Mail\Exception\MailApiException; use ByJG\Mail\Override\PHPMailerOverride; +use ByJG\Mail\SendResult; use ByJG\Mail\Util; use InvalidArgumentException; use PHPMailer\PHPMailer\Exception; @@ -100,12 +101,12 @@ protected function prepareMailer(Envelope $envelope): PHPMailerOverride /** * @param Envelope $envelope - * @return bool - * @throws MailApiException - * @throws InvalidEMailException + * @return SendResult * @throws Exception + * @throws InvalidEMailException + * @throws MailApiException */ - public function send(Envelope $envelope): bool + public function send(Envelope $envelope): SendResult { $this->validate($envelope); @@ -124,6 +125,6 @@ public function send(Envelope $envelope): bool throw new MailApiException($mail->ErrorInfo); } - return true; + return new SendResult(true, $mail->getLastMessageID()); } } diff --git a/src/Wrapper/SendMailWrapper.php b/src/Wrapper/SendMailWrapper.php index c205f78..ac75ede 100644 --- a/src/Wrapper/SendMailWrapper.php +++ b/src/Wrapper/SendMailWrapper.php @@ -5,6 +5,7 @@ use ByJG\Mail\Envelope; use ByJG\Mail\Exception\InvalidEMailException; use ByJG\Mail\Exception\InvalidMessageFormatException; +use ByJG\Mail\SendResult; use PHPMailer\PHPMailer\Exception; /** @@ -24,12 +25,12 @@ public static function schema(): array /** * @param Envelope $envelope - * @return bool + * @return SendResult + * @throws Exception * @throws InvalidEMailException * @throws InvalidMessageFormatException - * @throws Exception */ - public function send(Envelope $envelope): bool + public function send(Envelope $envelope): SendResult { $this->validate($envelope); @@ -47,6 +48,6 @@ public function send(Envelope $envelope): bool mail($toEmail, $envelope->getSubject(), $messageParts['body'], $messageParts['header']); } - return true; + return new SendResult(true, $mail->getLastMessageID()); } } diff --git a/tests/MailgunWrapperTest.php b/tests/MailgunWrapperTest.php index 17c8b50..4ec916d 100644 --- a/tests/MailgunWrapperTest.php +++ b/tests/MailgunWrapperTest.php @@ -5,6 +5,7 @@ use ByJG\Mail\Envelope; use ByJG\Mail\Exception\InvalidEMailException; use ByJG\Mail\Exception\MailApiException; +use ByJG\Mail\SendResult; use ByJG\Mail\Wrapper\MailgunApiWrapper; use ByJG\WebRequest\Exception\MessageException; use ByJG\WebRequest\Exception\NetworkException; @@ -21,15 +22,15 @@ class MailgunWrapperTest extends BaseWrapperTest /** * @param Envelope $envelope * @param MockClient $mock - * @return bool + * @return SendResult + * @throws ClientExceptionInterface * @throws InvalidEMailException * @throws MailApiException * @throws MessageException * @throws NetworkException * @throws RequestException - * @throws ClientExceptionInterface */ - public function doMockedRequest(Envelope $envelope, MockClient $mock): bool + public function doMockedRequest(Envelope $envelope, MockClient $mock): SendResult { $object = new MailgunApiWrapper(new Uri('mailgun://YOUR_API_KEY@YOUR_DOMAIN'), $mock); return $object->send($envelope); From 670e29209181ea04bc01433ddfa3e8dd356feda2 Mon Sep 17 00:00:00 2001 From: Icaro Ferreira Date: Mon, 8 Sep 2025 20:09:51 -0300 Subject: [PATCH 2/4] feat: Add tests and refactor AWS Wrapper --- src/SendResult.php | 4 +- src/Wrapper/AmazonSesWrapper.php | 15 +------- tests/AmazonSesWrapperTest.php | 66 +++++++++++++++++++++++++++----- tests/MailgunWrapperTest.php | 48 +++++++++++++++++++++++ tests/MockSender.php | 6 +++ tests/PHPMailerWrapperTest.php | 24 +++++++----- 6 files changed, 129 insertions(+), 34 deletions(-) diff --git a/src/SendResult.php b/src/SendResult.php index 87c1b0a..3d0ba2f 100644 --- a/src/SendResult.php +++ b/src/SendResult.php @@ -5,7 +5,7 @@ class SendResult { public function __construct( - public bool $success, - public ?string $id = null, + public readonly bool $success, + public readonly ?string $id = null, ) {} } \ No newline at end of file diff --git a/src/Wrapper/AmazonSesWrapper.php b/src/Wrapper/AmazonSesWrapper.php index 3ec8694..ac27bac 100644 --- a/src/Wrapper/AmazonSesWrapper.php +++ b/src/Wrapper/AmazonSesWrapper.php @@ -54,7 +54,7 @@ public function send(Envelope $envelope): SendResult $ses = $this->getSesClient(); - $ses->sendRawEmail( + $result = $ses->sendRawEmail( [ 'RawMessage' => [ 'Data' => $message, @@ -62,17 +62,6 @@ public function send(Envelope $envelope): SendResult ] ); - $messageId = null; - $messageLines = explode("\n", str_replace(["\r\n", "\r"], "\n", $message)); - - foreach ($messageLines as $line) { - if (stripos($line, 'Message-ID:') === 0) { - $value = trim(substr($line, strlen('Message-ID:'))); - $messageId = trim($value, "<> \t\r\n"); - break; - } - } - - return new SendResult(true, $messageId); + return new SendResult(true, $result->get('MessageId')); } } diff --git a/tests/AmazonSesWrapperTest.php b/tests/AmazonSesWrapperTest.php index ccdc504..e449c61 100644 --- a/tests/AmazonSesWrapperTest.php +++ b/tests/AmazonSesWrapperTest.php @@ -3,16 +3,23 @@ namespace Tests; use Aws\Credentials\Credentials; +use ByJG\Mail\Exception\InvalidEMailException; +use ByJG\Mail\Exception\InvalidMessageFormatException; +use ByJG\Mail\SendResult; use ByJG\Mail\Wrapper\AmazonSesWrapper; use ByJG\Util\Uri; +use PHPMailer\PHPMailer\Exception; class AmazonSesWrapperTest extends BaseWrapperTest { /** * @param $envelope - * @return MockSender + * @return array + * @throws InvalidEMailException + * @throws InvalidMessageFormatException + * @throws Exception */ - public function doMockedRequest($envelope) + public function doMockedRequest($envelope): array { $object = $this->getMockBuilder(AmazonSesWrapper::class) ->onlyMethods(['getSesClient']) @@ -24,9 +31,9 @@ public function doMockedRequest($envelope) ->method('getSesClient') ->will($this->returnValue($mock)); - $object->send($envelope); + $result = $object->send($envelope); - return $mock; + return [$mock, $result]; } public function testGetSesClient() @@ -46,9 +53,14 @@ public function testGetSesClient() $this->assertEquals('2010-12-01', $sesClient->getApi()->getApiVersion()); } - protected function send($envelope, $rawEmail) + /** + * @throws Exception + * @throws InvalidMessageFormatException + * @throws InvalidEMailException + */ + protected function send($envelope, $rawEmail): SendResult { - $mock = $this->doMockedRequest($envelope); + [$mock, $result] = $this->doMockedRequest($envelope); $mimeMessage = $this->fixVariableFields(file_get_contents(__DIR__ . '/resources/' . $rawEmail . '.eml')); $mock->result['RawMessage']['Data'] = $this->fixVariableFields($mock->result['RawMessage']['Data']); @@ -59,29 +71,63 @@ protected function send($envelope, $rawEmail) ]; $this->assertEquals($expected, $mock->result); + + return $result; } + /** + * @throws Exception + * @throws InvalidMessageFormatException + * @throws InvalidEMailException + */ public function testBasicEnvelope() { $envelope = $this->getBasicEnvelope(); - $this->send($envelope, 'basicenvelope'); + $result = $this->send($envelope, 'basicenvelope'); + + $this->assertTrue($result->success); + $this->assertEquals('EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000', $result->id); } + /** + * @throws Exception + * @throws InvalidMessageFormatException + * @throws InvalidEMailException + */ public function testFullEnvelope() { $envelope = $this->getFullEnvelope(); - $this->send($envelope, 'fullenvelope'); + $result = $this->send($envelope, 'fullenvelope'); + + $this->assertTrue($result->success); + $this->assertEquals('EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000', $result->id); } + /** + * @throws Exception + * @throws InvalidMessageFormatException + * @throws InvalidEMailException + */ public function testAttachmentEnvelope() { $envelope = $this->getAttachmentEnvelope(); - $this->send($envelope, 'attachmentenvelope'); + $result = $this->send($envelope, 'attachmentenvelope'); + + $this->assertTrue($result->success); + $this->assertEquals('EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000', $result->id); } + /** + * @throws Exception + * @throws InvalidMessageFormatException + * @throws InvalidEMailException + */ public function testEmbedImageEnvelope() { $envelope = $this->getEmbedImageEnvelope(); - $this->send($envelope, 'embedenvelope'); + $result = $this->send($envelope, 'embedenvelope'); + + $this->assertTrue($result->success); + $this->assertEquals('EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000', $result->id); } } diff --git a/tests/MailgunWrapperTest.php b/tests/MailgunWrapperTest.php index 4ec916d..9054140 100644 --- a/tests/MailgunWrapperTest.php +++ b/tests/MailgunWrapperTest.php @@ -36,6 +36,10 @@ public function doMockedRequest(Envelope $envelope, MockClient $mock): SendResul return $object->send($envelope); } + /** + * @throws RequestException + * @throws MessageException + */ public function testGetRequest() { $wrapper = new MailgunApiWrapper(new Uri('mailgun://YOUR_API_KEY@YOUR_DOMAIN')); @@ -44,6 +48,14 @@ public function testGetRequest() $this->assertEquals("api:YOUR_API_KEY", $request->getUri()->getUserInfo()); } + /** + * @throws MailApiException + * @throws RequestException + * @throws NetworkException + * @throws ClientExceptionInterface + * @throws InvalidEMailException + * @throws MessageException + */ public function testBasicEnvelope() { $expectedResponse = new Response(200); @@ -55,8 +67,19 @@ public function testBasicEnvelope() $result = $this->doMockedRequest($envelope, $mock); $expected = $this->fixRequestBody(file_get_contents(__DIR__ . "/resources/basicenvelope-request.txt")); $this->assertEquals($expected, $this->fixRequestBody($mock->getRequestedObject()->getBody()->getContents())); + + $this->assertTrue($result->success); + $this->assertEquals('12345', $result->id); } + /** + * @throws MailApiException + * @throws RequestException + * @throws NetworkException + * @throws InvalidEMailException + * @throws ClientExceptionInterface + * @throws MessageException + */ public function testFullEnvelope() { $expectedResponse = new Response(200); @@ -68,8 +91,19 @@ public function testFullEnvelope() $result = $this->doMockedRequest($envelope, $mock); $expected = $this->fixRequestBody(file_get_contents(__DIR__ . "/resources/fullenvelope-request.txt")); $this->assertEquals($expected, $this->fixRequestBody($mock->getRequestedObject()->getBody()->getContents())); + + $this->assertTrue($result->success); + $this->assertEquals('12345', $result->id); } + /** + * @throws MailApiException + * @throws RequestException + * @throws NetworkException + * @throws ClientExceptionInterface + * @throws InvalidEMailException + * @throws MessageException + */ public function testAttachmentEnvelope() { $expectedResponse = new Response(200); @@ -81,8 +115,19 @@ public function testAttachmentEnvelope() $result = $this->doMockedRequest($envelope, $mock); $expected = $this->fixRequestBody(file_get_contents(__DIR__ . "/resources/attachmentenvelope-request.txt")); $this->assertEquals($expected, $this->fixRequestBody($mock->getRequestedObject()->getBody()->getContents())); + + $this->assertTrue($result->success); + $this->assertEquals('12345', $result->id); } + /** + * @throws MailApiException + * @throws NetworkException + * @throws RequestException + * @throws InvalidEMailException + * @throws ClientExceptionInterface + * @throws MessageException + */ public function testEmbedImageEnvelope() { $expectedResponse = new Response(200); @@ -94,5 +139,8 @@ public function testEmbedImageEnvelope() $result = $this->doMockedRequest($envelope, $mock); $expected = $this->fixRequestBody(file_get_contents(__DIR__ . "/resources/embedenvelope-request.txt")); $this->assertEquals($expected, $this->fixRequestBody($mock->getRequestedObject()->getBody()->getContents())); + + $this->assertTrue($result->success); + $this->assertEquals('12345', $result->id); } } diff --git a/tests/MockSender.php b/tests/MockSender.php index 926b2a0..d361ca4 100644 --- a/tests/MockSender.php +++ b/tests/MockSender.php @@ -2,6 +2,8 @@ namespace Tests; +use Aws\Result; + class MockSender { public $result; @@ -10,6 +12,10 @@ class MockSender public function sendRawEmail($raw) { $this->result = $raw; + + return new Result([ + 'MessageId' => 'EXAMPLEf3f73d99b-c63fb06f-d263-41f8-a0fb-d0dc67d56c07-000000', + ]); } // Mailgun Wrapper diff --git a/tests/PHPMailerWrapperTest.php b/tests/PHPMailerWrapperTest.php index 7a05bed..d0c3e0a 100644 --- a/tests/PHPMailerWrapperTest.php +++ b/tests/PHPMailerWrapperTest.php @@ -13,21 +13,25 @@ class PHPMailerWrapperTest extends BaseWrapperTest { /** * @param $envelope - * @return PHPMailerOverride + * @return array + * @throws Exception * @throws InvalidEMailException * @throws MailApiException - * @throws Exception */ - public function doMockedRequest($envelope): PHPMailerOverride + public function doMockedRequest($envelope): array { $mock = $this->getMockBuilder(PHPMailerOverride::class) - ->onlyMethods(['send']) + ->onlyMethods(['send', 'getLastMessageID']) ->setConstructorArgs([true]) ->getMock(); + $mock->expects($this->once()) ->method('send') - ->will($this->returnValue(true)); + ->willReturn(true); + $mock->expects($this->once()) + ->method('getLastMessageID') + ->willReturn('mocked-message-id'); $object = $this->getMockBuilder(PHPMailerWrapper::class) ->onlyMethods(['getMailer']) @@ -36,20 +40,22 @@ public function doMockedRequest($envelope): PHPMailerOverride $object->expects($this->once()) ->method('getMailer') - ->will($this->returnValue($mock)); + ->willReturn($mock); - $object->send($envelope); + $sendResult = $object->send($envelope); - return $mock; + return [$mock, $sendResult]; } protected function send($envelope, $rawEmail) { - $mock = $this->doMockedRequest($envelope); + [$mock, $sendResult] = $this->doMockedRequest($envelope); $expected = $this->fixVariableFields(file_get_contents(__DIR__ . '/resources/' . $rawEmail . '.eml')); $result = $this->fixVariableFields($mock->getFullMessageEnvelope()); $this->assertEquals($expected, $result); + $this->assertTrue($sendResult->success); + $this->assertEquals('mocked-message-id', $sendResult->id); } public function testBasicEnvelope() From 29c4b96436802f6d7a7d835157e8a44388c5a6e1 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 9 Sep 2025 08:13:28 -0400 Subject: [PATCH 3/4] Update phpunit.yml --- .github/workflows/phpunit.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index a781440..97f8b52 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -32,5 +32,6 @@ jobs: with: folder: php project: ${{ github.event.repository.name }} - secrets: inherit + secrets: + DOC_TOKEN: ${{ secrets.DOC_TOKEN }} From dfb7aa9c38c2b2866006736ffad754aa38c14e19 Mon Sep 17 00:00:00 2001 From: Joao Gilberto Magalhaes Date: Tue, 9 Sep 2025 08:16:56 -0400 Subject: [PATCH 4/4] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 49e563b..429d85f 100644 --- a/README.md +++ b/README.md @@ -104,13 +104,13 @@ scheme://username:password/smtpserver:port The options are: -| Part | Description | -|:-----------|:--------------------| +| Part | Description | +|:-----------|:--------------------------------------------------------------------------------------------------------| | scheme | The email scheme: smtp, ssl, tls, mandrill and ses. Note that mandrill and ses use your own private api | -| username | The username | -| password | The password | -| smtpserver | The SMTP Host | -| port | The SMTP Port | +| username | The username | +| password | The password | +| smtpserver | The SMTP Host | +| port | The SMTP Port | The protocols available are: @@ -205,7 +205,7 @@ class MyWrapper extends \ByJG\Mail\Wrapper\BaseWrapper return ['mywrapper']; } - public function send(Envelope $envelope) + public function send(Envelope $envelope): \ByJG\Mail\SendResult { // Do how to send the email using your library }