diff --git a/composer.json b/composer.json index 9ca7c65..6fcb663 100644 --- a/composer.json +++ b/composer.json @@ -1,40 +1,40 @@ -{ - "name": "synergitech/laravel-postal", - "description": "This library integrates Postal with the standard Laravel mail framework.", - "keywords": [ - "Laravel", - "Postal", - "Email" - ], - "homepage": "https://github.com/synergitech/laravel-postal", - "license": "MIT", - "require": { - "php": "^8.0", - "laravel/framework": "^9.0.1|^10.0", - "postal/postal": "^1.0.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.0", - "orchestra/testbench": "^7.0|^8.0", - "nunomaduro/larastan": "^2.0" - }, - "autoload": { - "psr-4": { - "SynergiTech\\Postal\\": "src", - "SynergiTech\\Postal\\Tests\\": "tests" - } - }, - "extra": { - "laravel": { - "providers": [ - "SynergiTech\\Postal\\PostalServiceProvider" - ] - } - }, - "scripts": { - "test": [ - "phpunit", - "phpstan --memory-limit=1G" - ] - } -} +{ + "name": "synergitech/laravel-postal", + "description": "This library integrates Postal with the standard Laravel mail framework.", + "keywords": [ + "Laravel", + "Postal", + "Email" + ], + "homepage": "https://github.com/synergitech/laravel-postal", + "license": "MIT", + "require": { + "php": "^8.0", + "laravel/framework": "^9.0.1|^10.0", + "postal/postal": "^2.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.0", + "orchestra/testbench": "^7.0|^8.0", + "nunomaduro/larastan": "^2.0" + }, + "autoload": { + "psr-4": { + "SynergiTech\\Postal\\": "src", + "SynergiTech\\Postal\\Tests\\": "tests" + } + }, + "extra": { + "laravel": { + "providers": [ + "SynergiTech\\Postal\\PostalServiceProvider" + ] + } + }, + "scripts": { + "test": [ + "phpunit", + "phpstan --memory-limit=1G" + ] + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 118b5e9..0c47944 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -35,11 +35,6 @@ parameters: count: 1 path: src/PostalNotificationChannel.php - - - message: "#^Access to an undefined property Postal\\\\SendResult\\:\\:\\$result\\.$#" - count: 1 - path: src/PostalTransport.php - - message: "#^Access to an undefined property object\\:\\:\\$body\\.$#" count: 2 @@ -95,3 +90,12 @@ parameters: count: 1 path: src/PostalTransport.php + - + message: "#^Parameter \\#1 \\$content of method Postal\\\\Send\\\\Message\\:\\:htmlBody\\(\\) expects string, resource\\|string given\\.$#" + count: 1 + path: src/PostalTransport.php + + - + message: "#^Parameter \\#1 \\$content of method Postal\\\\Send\\\\Message\\:\\:plainBody\\(\\) expects string, resource\\|string given\\.$#" + count: 1 + path: src/PostalTransport.php diff --git a/src/PostalNotificationChannel.php b/src/PostalNotificationChannel.php index dfc731c..8c2e59d 100644 --- a/src/PostalNotificationChannel.php +++ b/src/PostalNotificationChannel.php @@ -13,7 +13,7 @@ class PostalNotificationChannel extends MailChannel * * @param mixed $notifiable * @param \Illuminate\Notifications\Notification $notification - * @return void + * @return \Illuminate\Mail\SentMessage|null */ public function send($notifiable, Notification $notification) { diff --git a/src/PostalServiceProvider.php b/src/PostalServiceProvider.php index c06b62a..3140183 100644 --- a/src/PostalServiceProvider.php +++ b/src/PostalServiceProvider.php @@ -31,12 +31,19 @@ public function boot(): void } Mail::extend('postal', function (array $config = []) { - $config = config('postal', []); + $config = config('postal'); + if (! is_array($config)) { - $config = []; + throw new \RuntimeException('missing Postal configuration'); + } + if (! is_string($config['domain'])) { + throw new \RuntimeException('missing Postal domain configuration'); + } + if (! is_string($config['key'])) { + throw new \RuntimeException('missing Postal key configuration'); } - return new PostalTransport(new Client($config['domain'] ?? null, $config['key'] ?? null)); + return new PostalTransport(new Client($config['domain'], $config['key'])); }); } } diff --git a/src/PostalTransport.php b/src/PostalTransport.php index bb80bd6..d130844 100644 --- a/src/PostalTransport.php +++ b/src/PostalTransport.php @@ -2,12 +2,10 @@ namespace SynergiTech\Postal; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\DB; use Postal\Client; -use Postal\Error; -use Postal\SendMessage; -use Postal\SendResult; +use Postal\ApiException; +use Postal\Send\Message as SendMessage; +use Postal\Send\Result as SendResult; use Symfony\Component\Mailer\Exception\TransportException; use Symfony\Component\Mailer\SentMessage; use Symfony\Component\Mailer\Transport\AbstractTransport; @@ -37,8 +35,8 @@ protected function doSend(SentMessage $sentMessage): void $postalmessage = $this->symfonyToPostal($symfonyMessage); try { - $response = $postalmessage->send(); - } catch (Error $error) { + $response = $this->client->send->message($postalmessage); + } catch (ApiException $error) { throw new TransportException($error->getMessage(), $error->getCode(), $error); } @@ -46,7 +44,7 @@ protected function doSend(SentMessage $sentMessage): void // send known header back for laravel to match emails coming out of Postal // - doesn't seem we can replace Message-ID - $headers->addTextHeader('Postal-Message-ID', $response->result->message_id); + $headers->addTextHeader('Postal-Message-ID', $response->message_id); if (config('postal.enable.emaillogging') !== true) { return; @@ -61,7 +59,7 @@ protected function doSend(SentMessage $sentMessage): void if ($emailable_type != '' && $emailable_id != '') { $emailmodel = config('postal.models.email'); \DB::table((new $emailmodel)->getTable()) - ->where('postal_email_id', $response->result->message_id) + ->where('postal_email_id', $response->message_id) ->update([ 'emailable_type' => $emailable_type, 'emailable_id' => $emailable_id, @@ -107,13 +105,13 @@ private function symfonyToPostal(Email $symfonyMessage): SendMessage $postalMessage->htmlBody($symfonyMessage->getHtmlBody()); } - foreach ($symfonyMessage->getAttachments() as $symfonyPart) { + foreach ($symfonyMessage->getAttachments() as $index => $symfonyPart) { $filename = $symfonyPart ->getPreparedHeaders() ->getHeaderParameter('content-disposition', 'filename'); $postalMessage->attach( - $filename, + $filename ?? "attached_file_$index", $symfonyPart->getMediaType() . '/' . $symfonyPart->getMediaSubtype(), $symfonyPart->getBody() ); @@ -162,9 +160,9 @@ private function recordEmailsFromResponse(Email $symfonyMessage, SendResult $res $email->body = $symfonyMessage->getHtmlBody(); } - $email->postal_email_id = $response->result->message_id; - $email->postal_id = $message->id(); - $email->postal_token = $message->token(); + $email->postal_email_id = $response->message_id; + $email->postal_id = $message->id; + $email->postal_token = $message->token; $email->save(); } @@ -181,7 +179,7 @@ private function stringifyAddress(Address $address): string private function getNewSendMessage(): SendMessage { - return new SendMessage($this->client); + return new SendMessage(); } /** diff --git a/tests/FeatureTest.php b/tests/FeatureTest.php index 601fa3a..8bf241c 100644 --- a/tests/FeatureTest.php +++ b/tests/FeatureTest.php @@ -5,6 +5,8 @@ use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Notification; use Postal\Client; +use Postal\Send\Result; +use Postal\SendService; use Symfony\Component\Mailer\DelayedEnvelope; use SynergiTech\Postal\PostalNotificationChannel; use SynergiTech\Postal\PostalTransport; @@ -13,19 +15,23 @@ class FeatureTest extends TestCase { public function testSendingNotificationOnDemand() { - $result = new \stdClass; - $result->message_id = 'feature-test'; - $message = new \stdClass(); - $message->id = 'feature-test'; - $message->token = 'feature-test'; - $result->messages['feature-test@example.com'] = $message; - - $clientMock = $this->createMock(Client::class); - $clientMock - ->method('makeRequest') - ->willReturn($result); - - Mail::extend('postal', function (array $config = []) use ($clientMock) { + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $result = new Result([ + 'message_id' => 'feature-test', + 'messages' => ['feature-test@example.com' => [ + 'id' => 123, + 'token' => 'feature-test', + ]], + ]); + + $serviceMock->method('message') + ->willReturn($result); + + $clientMock->send = $serviceMock; + return new PostalTransport($clientMock); }); @@ -48,19 +54,23 @@ public function testSendingNotificationOnDemand() public function testSendingNotificationOnDemandWithAlias() { - $result = new \stdClass; - $result->message_id = 'feature-test'; - $message = new \stdClass(); - $message->id = 'feature-test'; - $message->token = 'feature-test'; - $result->messages['feature-test@example.com'] = $message; - - $clientMock = $this->createMock(Client::class); - $clientMock - ->method('makeRequest') - ->willReturn($result); - - Mail::extend('postal', function (array $config = []) use ($clientMock) { + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $result = new Result([ + 'message_id' => 'feature-test', + 'messages' => ['feature-test@example.com' => [ + 'id' => 123, + 'token' => 'feature-test', + ]], + ]); + + $serviceMock->method('message') + ->willReturn($result); + + $clientMock->send = $serviceMock; + return new PostalTransport($clientMock); }); @@ -83,19 +93,23 @@ public function testSendingNotificationOnDemandWithAlias() public function testSendingNotificationWithNotifiableTrait() { - $result = new \stdClass; - $result->message_id = 'feature-test'; - $message = new \stdClass(); - $message->id = 'feature-test'; - $message->token = 'feature-test'; - $result->messages['feature-test@example.com'] = $message; - - $clientMock = $this->createMock(Client::class); - $clientMock - ->method('makeRequest') - ->willReturn($result); - - Mail::extend('postal', function (array $config = []) use ($clientMock) { + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $result = new Result([ + 'message_id' => 'feature-test', + 'messages' => ['feature-test@example.com' => [ + 'id' => 123, + 'token' => 'feature-test', + ]], + ]); + + $serviceMock->method('message') + ->willReturn($result); + + $clientMock->send = $serviceMock; + return new PostalTransport($clientMock); }); diff --git a/tests/PostalServiceProviderTest.php b/tests/PostalServiceProviderTest.php index 156a90f..2222c0f 100644 --- a/tests/PostalServiceProviderTest.php +++ b/tests/PostalServiceProviderTest.php @@ -10,6 +10,10 @@ public function testBoot() { // assert the service provider did boot and extend the mail + config([ + 'postal' => ['domain' => 'example.com', 'key' => 'hunter2'], + ]); + $driver = app('mail.manager') ->createSymfonyTransport(config('mail.mailers.postal')); diff --git a/tests/PostalTransportTest.php b/tests/PostalTransportTest.php index d92804e..58e43e7 100644 --- a/tests/PostalTransportTest.php +++ b/tests/PostalTransportTest.php @@ -5,7 +5,10 @@ use Illuminate\Support\Facades\Mail; use Mockery\MockInterface; use Postal\Client; -use Postal\Error; +use Postal\ApiException; +use Postal\Send\Message; +use Postal\Send\Result; +use Postal\SendService; use Symfony\Component\Mailer\Exception\TransportException; use SynergiTech\Postal\PostalTransport; @@ -13,18 +16,18 @@ class PostalTransportTest extends TestCase { public function testSendPostalFailure(): void { - // requests requires a URL - config(['postal.domain' => 'http://example.com']); - - // the transport converts Postal\Error to TransportException + // the transport converts Postal\ApiException to TransportException $this->expectException(TransportException::class); - $clientMock = $this->createMock(Client::class); - $clientMock - ->method('makeRequest') - ->will($this->throwException(new Error())); + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $serviceMock->method('message') + ->will($this->throwException(new ApiException())); + + $clientMock->send = $serviceMock; - Mail::extend('postal', function (array $config = []) use ($clientMock) { return new PostalTransport($clientMock); }); @@ -33,20 +36,23 @@ public function testSendPostalFailure(): void public function testSendSuccess(): void { - // requests requires a URL - config(['postal.domain' => 'http://example.com']); + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $result = new Result([ + 'message_id' => 'test', + 'messages' => ['testsendsuccess@example.com' => [ + 'id' => 123, + 'token' => 'first', + ]], + ]); - $clientMock = $this->createMock(Client::class); - $result = new \stdClass; - $result->message_id = 'test'; - $message = new \stdClass(); - $message->id = 'first'; - $message->token = 'first'; - $result->messages['testsendsuccess@example.com'] = $message; + $serviceMock->method('message') + ->willReturn($result); - $clientMock->method('makeRequest')->willReturn((object)$result); + $clientMock->send = $serviceMock; - Mail::extend('postal', function (array $config = []) use ($clientMock) { return new PostalTransport($clientMock); }); @@ -64,19 +70,23 @@ public function testSendSuccess(): void public function testPostalCaseSensitivity(): void { - $result = new \stdClass; - $result->message_id = 'caseSensitivityTest'; - $message = new \stdClass(); - $message->id = 'caseSensitivityTest'; - $message->token = 'caseSensitivityTest'; - $result->messages['caseSensitivityTest@example.com'] = $message; - - $clientMock = $this->createMock(Client::class); - $clientMock - ->method('makeRequest') - ->willReturn($result); - - Mail::extend('postal', function (array $config = []) use ($clientMock) { + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->createMock(SendService::class); + + $result = new Result([ + 'message_id' => 'caseSensitivityTest', + 'messages' => ['caseSensitivityTest@example.com' => [ + 'id' => 123, + 'token' => 'caseSensitivityTest', + ]], + ]); + + $serviceMock->method('message') + ->willReturn($result); + + $clientMock->send = $serviceMock; + return new PostalTransport($clientMock); }); @@ -93,29 +103,28 @@ public function testPostalCaseSensitivity(): void public function testAttachments(): void { - // requests requires a URL - config(['postal.domain' => 'http://example.com']); - - $clientMock = $this->mock(Client::class, function (MockInterface $mock) { - $message = new \stdClass(); - $message->id = 'first'; - $message->token = 'first'; - - $result = new \stdClass; - $result->message_id = 'test'; - $result->messages['testsendsuccess@example.com'] = $message; - - $mock->shouldReceive('makeRequest') - ->withArgs(function ($controller, $action, $parameters) { - $this->assertCount(1, $parameters['attachments']); - $this->assertSame('test-attachment', $parameters['attachments'][0]['name']); - - return $controller == 'send' && $action == 'message'; + Mail::extend('postal', function (array $config = []) { + $clientMock = $this->createMock(Client::class); + $serviceMock = $this->mock(SendService::class); + + $result = new Result([ + 'message_id' => 'first', + 'messages' => ['testsendsuccess@example.com' => [ + 'id' => 123, + 'token' => 'first', + ]], + ]); + + $serviceMock->shouldReceive('message') + ->withArgs(function (Message $message) { + $this->assertCount(1, $message->attachments); + + return $message->attachments[0]['name'] == 'test-attachment'; }) - ->andReturn((object)$result); - }); + ->andReturn($result); + + $clientMock->send = $serviceMock; - Mail::extend('postal', function (array $config = []) use ($clientMock) { return new PostalTransport($clientMock); });