From c07432006dd1bb446387ed706eaa183ca251142c Mon Sep 17 00:00:00 2001 From: korridor <26689068+korridor@users.noreply.github.com> Date: Thu, 5 Jan 2023 21:48:35 -0500 Subject: [PATCH] Refactored code after first test --- .php-cs-fixer.php | 2 +- composer.json | 6 +- readme.md | 65 +++++++++++++- src/Transport/MessageStreamHeader.php | 13 --- src/Transport/ScalewayApiTransport.php | 65 ++++---------- src/Transport/ScalewaySmtpTransport.php | 47 ++++++---- src/Transport/ScalewayTransportFactory.php | 24 ++---- tests/Transport/ScalewayApiTransportTest.php | 85 +++---------------- tests/Transport/ScalewaySmtpTransportTest.php | 8 +- .../ScalewayTransportFactoryTest.php | 67 +++++++-------- 10 files changed, 173 insertions(+), 209 deletions(-) delete mode 100644 src/Transport/MessageStreamHeader.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index cc2902c..0a6168d 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -3,7 +3,7 @@ return (new PhpCsFixer\Config()) ->setRiskyAllowed(false) ->setRules([ - '@PSR2' => true, + '@PSR12' => true, ]) ->setUsingCache(true) ->setFinder( diff --git a/composer.json b/composer.json index 1dd7f6f..7f39d17 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "korridor/symfony-scaleway-tem-mailer", "type": "symfony-mailer-bridge", "description": "Symfony Scaleway transaction email (TEM) Mailer Bridge", - "keywords": [], + "keywords": ["symfony", "scaleway", "symfony-mailer", "scaleway-api"], "homepage": "https://github.com/korridor/symfony-scaleway-tem-mailer", "license": "MIT", "authors": [ @@ -29,12 +29,12 @@ }, "autoload": { "psr-4": { - "Symfony\\Component\\Mailer\\Bridge\\Scaleway\\": "src/" + "Korridor\\SymfonyScalewayTemMailer\\": "src/" } }, "autoload-dev": { "psr-4": { - "Symfony\\Component\\Mailer\\Bridge\\Scaleway\\Tests\\": "tests/" + "Korridor\\SymfonyScalewayTemMailer\\Tests\\": "tests/" } }, "minimum-stability": "dev" diff --git a/readme.md b/readme.md index c69fdd7..547d81a 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,9 @@ # Symfony Scaleway TEM mailer +[![Latest Version on Packagist](https://img.shields.io/packagist/v/korridor/symfony-scaleway-tem-mailer?style=flat-square)](https://packagist.org/packages/korridor/symfony-scaleway-tem-mailer) +[![License](https://img.shields.io/packagist/l/korridor/symfony-scaleway-tem-mailer?style=flat-square)](license.md) +[![Supported PHP versions](https://img.shields.io/packagist/php-v/korridor/symfony-scaleway-tem-mailer?style=flat-square)](https://packagist.org/packages/korridor/symfony-scaleway-tem-mailer) + ## Installation You can install the package via composer with following command: @@ -8,11 +12,63 @@ You can install the package via composer with following command: composer require korridor/symfony-scaleway-tem-mailer ``` +### Requirements + +This package is tested for the following Laravel and PHP versions: + +- 9.* (PHP 8.1) + ## Usage examples ### Laravel -TODO +Add the following code to the `AppServiceProvider`: + +```php +use Korridor\SymfonyScalewayTemMailer\Transport\ScalewayApiTransport; +use Korridor\SymfonyScalewayTemMailer\Transport\ScalewaySmtpTransport; + + // .. + + /** + * Bootstrap any application services. + * + * @return void + */ + public function boot(): void + { + // ... + Mail::extend('scaleway-api', function (array $config = []) { + return new ScalewayApiTransport($config['token'], $config['region'], $config['project_id']); + }); + Mail::extend('scaleway-smtp', function (array $config = []) { + return new ScalewaySmtpTransport($config['token'], $config['region'], $config['project_id']); + }); + } + +``` + +Now add the following lines to the `config/mail.php` file in the `mailers` array: + +```php +'scaleway' => [ + 'transport' => 'scaleway-api', + 'region' => env('MAIL_SCALEWAY_REGION', 'fr-par'), + 'token' => env('MAIL_SCALEWAY_TOKEN'), + 'project_id' => env('MAIL_SCALEWAY_PROJECT_ID'), +], +``` + +If you want to use the SMTP integration instead use following lines: + +```php +'scaleway' => [ + 'transport' => 'scaleway-smtp', + 'region' => env('MAIL_SCALEWAY_REGION', 'fr-par'), + 'token' => env('MAIL_SCALEWAY_TOKEN'), + 'project_id' => env('MAIL_SCALEWAY_PROJECT_ID'), +], +``` ## Contributing @@ -30,13 +86,18 @@ docker-compose run workspace bash ### Testing The `composer test` command runs all tests with [phpunit](https://phpunit.de/). -The `composer test-coverage` command runs all tests with phpunit and creates a coverage report into the `coverage` folder. +The `composer test-coverage` command runs all tests with phpunit and creates a coverage report into the `coverage` +folder. ### Codeformatting/Linting The `composer fix` command formats the code with [php-cs-fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer). The `composer lint` command checks the code with [phpcs](https://github.com/squizlabs/PHP_CodeSniffer). +## Credits + +The structure of the repository is inspired by the project [symfony/postmark-mailer](https://github.com/symfony/postmark-mailer). + ## License This package is licensed under the MIT License (MIT). Please see [license file](license.md) for more information. diff --git a/src/Transport/MessageStreamHeader.php b/src/Transport/MessageStreamHeader.php deleted file mode 100644 index 4388a88..0000000 --- a/src/Transport/MessageStreamHeader.php +++ /dev/null @@ -1,13 +0,0 @@ -token = $token; $this->region = $region; + $this->projectId = $projectId; parent::__construct($client, $dispatcher, $logger); } public function __toString(): string { - return sprintf('scaleway+api://%s', $this->getEndpoint()).($this->messageStream ? '?message_stream='.$this->messageStream : ''); + return sprintf('scaleway+api://%s', $this->getEndpoint()); } protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $envelope): ResponseInterface @@ -63,10 +58,10 @@ protected function doSendApi(SentMessage $sentMessage, Email $email, Envelope $e } if (200 !== $statusCode) { - throw new HttpTransportException('Unable to send an email: '.$result['Message'].sprintf(' (code %d).', $result['ErrorCode']), $response); + throw new HttpTransportException('Unable to send an email: '.$result['message'].' Details: '.print_r($result, true), $response); } - $sentMessage->setMessageId($result['MessageID']); + $sentMessage->setMessageId($result['id']); // TODO: what is message_id return $response; } @@ -81,6 +76,7 @@ private function getPayload(Email $email, Envelope $envelope): array 'subject' => $email->getSubject(), 'text' => $email->getTextBody(), 'html' => $email->getHtmlBody(), + 'project_id' => $this->projectId, 'attachments' => $this->getAttachments($email), ]; @@ -90,36 +86,7 @@ private function getPayload(Email $email, Envelope $envelope): array continue; } - if ($header instanceof TagHeader) { - if (isset($payload['Tag'])) { - throw new TransportException('Scaleway only allows a single tag per email.'); - } - - $payload['Tag'] = $header->getValue(); - - continue; - } - - if ($header instanceof MetadataHeader) { - $payload['Metadata'][$header->getKey()] = $header->getValue(); - - continue; - } - - if ($header instanceof MessageStreamHeader) { - $payload['MessageStream'] = $header->getValue(); - - continue; - } - - $payload['Headers'][] = [ - 'Name' => $header->getName(), - 'Value' => $header->getBodyAsString(), - ]; - } - - if (null !== $this->messageStream && !isset($payload['MessageStream'])) { - $payload['MessageStream'] = $this->messageStream; + // TODO: ? } return $payload; @@ -171,23 +138,27 @@ private function getAttachments(Email $email): array return $attachments; } + /** + * @return string|null + */ private function getEndpoint(): ?string { return ($this->host ?: self::HOST).($this->port ? ':'.$this->port : ''); } + /** + * @return string + */ private function getRegion(): string { return $this->region; } /** - * @return $this + * @return string */ - public function setMessageStream(string $messageStream): static + public function getProjectId(): string { - $this->messageStream = $messageStream; - - return $this; + return $this->projectId; } } diff --git a/src/Transport/ScalewaySmtpTransport.php b/src/Transport/ScalewaySmtpTransport.php index 3a8b70a..983c56a 100644 --- a/src/Transport/ScalewaySmtpTransport.php +++ b/src/Transport/ScalewaySmtpTransport.php @@ -1,33 +1,34 @@ token = $token; + $this->region = $region; + $this->projectId = $projectId; parent::__construct(self::HOSTNAME, 587, false, $dispatcher, $logger); - $this->setUsername($id); - $this->setPassword($id); + $this->setUsername($projectId); + $this->setPassword($token); } public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage @@ -47,12 +48,26 @@ private function addScalewayHeaders(Message $message): void } /** - * @return $this + * @return string */ - public function setMessageStream(string $messageStream): static + public function getToken(): string { - $this->messageStream = $messageStream; + return $this->token; + } - return $this; + /** + * @return string + */ + public function getRegion(): string + { + return $this->region; + } + + /** + * @return string + */ + public function getProjectId(): string + { + return $this->projectId; } } diff --git a/src/Transport/ScalewayTransportFactory.php b/src/Transport/ScalewayTransportFactory.php index 07805cc..9c56d2e 100644 --- a/src/Transport/ScalewayTransportFactory.php +++ b/src/Transport/ScalewayTransportFactory.php @@ -1,42 +1,34 @@ - */ final class ScalewayTransportFactory extends AbstractTransportFactory { public function create(Dsn $dsn): TransportInterface { $transport = null; $scheme = $dsn->getScheme(); - $user = $this->getUser($dsn); + $projectId = $this->getUser($dsn); + $token = $this->getPassword($dsn); + $region = $dsn->getOption('region', 'fr-par'); if ('scaleway+api' === $scheme) { $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - $region = 'fr-par'; // TODO - $transport = (new ScalewayApiTransport($user, $region, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port); + $transport = (new ScalewayApiTransport($token, $region, $projectId, $this->client, $this->dispatcher, $this->logger))->setHost($host)->setPort($port); } - if ('scaleway+smtp' === $scheme || 'scaleway+smtps' === $scheme || 'scaleway' === $scheme) { - $transport = new ScalewaySmtpTransport($user, $this->dispatcher, $this->logger); + if ('scaleway+smtp' === $scheme || 'scaleway+smtps' === $scheme) { + $transport = new ScalewaySmtpTransport($token, $region, $projectId, $this->dispatcher, $this->logger); } if (null !== $transport) { - $messageStream = $dsn->getOption('message_stream'); - - if (null !== $messageStream) { - $transport->setMessageStream($messageStream); - } - return $transport; } @@ -45,6 +37,6 @@ public function create(Dsn $dsn): TransportInterface protected function getSupportedSchemes(): array { - return ['scaleway', 'scaleway+api', 'scaleway+smtp', 'scaleway+smtps']; + return ['scaleway+api', 'scaleway+smtp', 'scaleway+smtps']; } } diff --git a/tests/Transport/ScalewayApiTransportTest.php b/tests/Transport/ScalewayApiTransportTest.php index 4c5e98d..8458a81 100644 --- a/tests/Transport/ScalewayApiTransportTest.php +++ b/tests/Transport/ScalewayApiTransportTest.php @@ -1,12 +1,11 @@ setHost('example.com'), + (new ScalewayApiTransport('token', 'par-fr', 'project-id'))->setHost('example.com'), 'scaleway+api://example.com', ], [ - (new ScalewayApiTransport('KEY', 'par-fr'))->setHost('example.com')->setPort(99), + (new ScalewayApiTransport('token', 'par-fr', 'project-id'))->setHost('example.com')->setPort(99), 'scaleway+api://example.com:99', ], ]; } - public function testCustomHeader(): void - { - // Arrange - $email = new Email(); - $email->getHeaders()->addTextHeader('foo', 'bar'); - $envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]); - $transport = new ScalewayApiTransport('ACCESS_KEY', 'par-fr'); - $method = new \ReflectionMethod(ScalewayApiTransport::class, 'getPayload'); - - // Act - $payload = $method->invoke($transport, $email, $envelope); - - // Assert - $this->assertArrayHasKey('Headers', $payload); - $this->assertCount(1, $payload['Headers']); - $this->assertEquals(['Name' => 'foo', 'Value' => 'bar'], $payload['Headers'][0]); - } - public function testSend(): void { // Arrange @@ -77,12 +58,12 @@ public function testSend(): void $this->assertSame('Hello!', $body['subject']); $this->assertSame('Hello There!', $body['text']); - return new MockResponse(json_encode(['MessageID' => 'foobar']), [ + return new MockResponse(json_encode(['id' => 'foo-bar']), [ 'http_code' => 200, ]); }); - $transport = new ScalewayApiTransport('KEY', 'par-fr', $client); + $transport = new ScalewayApiTransport('token', 'par-fr', 'project-id', $client); $mail = new Email(); $mail->subject('Hello!') @@ -96,21 +77,23 @@ public function testSend(): void $message = $transport->send($mail); // Assert - $this->assertSame('foobar', $message->getMessageId()); + $this->assertSame('foo-bar', $message->getMessageId()); } public function testSendThrowsForErrorResponse(): void { // Arrange $client = new MockHttpClient(static function (string $method, string $url, array $options): ResponseInterface { - return new MockResponse(json_encode(['Message' => 'i\'m a teapot', 'ErrorCode' => 418]), [ + return new MockResponse(json_encode([ + 'message' => 'i\'m a teapot', + ]), [ 'http_code' => 418, 'response_headers' => [ 'content-type' => 'application/json', ], ]); }); - $transport = new ScalewayApiTransport('KEY', 'par-fr', $client); + $transport = new ScalewayApiTransport('token', 'par-fr', 'project-id', $client); $transport->setPort(8984); $mail = new Email(); @@ -120,49 +103,7 @@ public function testSendThrowsForErrorResponse(): void ->text('Hello There!'); $this->expectException(HttpTransportException::class); - $this->expectExceptionMessage('Unable to send an email: i\'m a teapot (code 418).'); + $this->expectExceptionMessage('Unable to send an email: i\'m a teapot'); $transport->send($mail); } - - public function testTagAndMetadataAndMessageStreamHeaders(): void - { - // Arrange - $email = new Email(); - $email->getHeaders()->add(new TagHeader('password-reset')); - $email->getHeaders()->add(new MetadataHeader('Color', 'blue')); - $email->getHeaders()->add(new MetadataHeader('Client-ID', '12345')); - $email->getHeaders()->add(new MessageStreamHeader('broadcasts')); - $envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]); - - $transport = new ScalewayApiTransport('ACCESS_KEY', 'par-fr'); - $method = new \ReflectionMethod(ScalewayApiTransport::class, 'getPayload'); - - // Act - $payload = $method->invoke($transport, $email, $envelope); - - // Assert - $this->assertArrayNotHasKey('Headers', $payload); - $this->assertArrayHasKey('Tag', $payload); - $this->assertArrayHasKey('Metadata', $payload); - $this->assertArrayHasKey('MessageStream', $payload); - - $this->assertSame('password-reset', $payload['Tag']); - $this->assertSame(['Color' => 'blue', 'Client-ID' => '12345'], $payload['Metadata']); - $this->assertSame('broadcasts', $payload['MessageStream']); - } - - public function testMultipleTagsAreNotAllowed(): void - { - $email = new Email(); - $email->getHeaders()->add(new TagHeader('tag1')); - $email->getHeaders()->add(new TagHeader('tag2')); - $envelope = new Envelope(new Address('alice@system.com'), [new Address('bob@system.com')]); - - $transport = new ScalewayApiTransport('ACCESS_KEY', 'par-fr'); - $method = new \ReflectionMethod(ScalewayApiTransport::class, 'getPayload'); - - $this->expectException(TransportException::class); - - $method->invoke($transport, $email, $envelope); - } } diff --git a/tests/Transport/ScalewaySmtpTransportTest.php b/tests/Transport/ScalewaySmtpTransportTest.php index d7ae9f2..e136d44 100644 --- a/tests/Transport/ScalewaySmtpTransportTest.php +++ b/tests/Transport/ScalewaySmtpTransportTest.php @@ -1,9 +1,9 @@ getHeaders()->addTextHeader('foo', 'bar'); - $transport = new ScalewaySmtpTransport('ACCESS_KEY'); + $transport = new ScalewaySmtpTransport('token', 'fr-par', 'project-id'); $method = new \ReflectionMethod(ScalewaySmtpTransport::class, 'addScalewayHeaders'); // Act @@ -29,7 +29,7 @@ public function testTagAndMetadataAndMessageStreamHeaders(): void // Arrange $email = new Email(); $email->getHeaders()->addTextHeader('foo', 'bar'); - $transport = new ScalewaySmtpTransport('ACCESS_KEY'); + $transport = new ScalewaySmtpTransport('token', 'fr-par', 'project-id'); $method = new \ReflectionMethod(ScalewaySmtpTransport::class, 'addScalewayHeaders'); // Act diff --git a/tests/Transport/ScalewayTransportFactoryTest.php b/tests/Transport/ScalewayTransportFactoryTest.php index b12779d..78285a4 100644 --- a/tests/Transport/ScalewayTransportFactoryTest.php +++ b/tests/Transport/ScalewayTransportFactoryTest.php @@ -1,10 +1,10 @@ 'fr-par' + ]), true, ]; yield [ - new Dsn('scaleway', 'default'), + new Dsn('scaleway+smtp', 'default', 'project-id', 'token', null, [ + 'region' => 'fr-par' + ]), true, ]; yield [ - new Dsn('scaleway+smtp', 'default'), + new Dsn('scaleway+smtps', 'default', 'project-id', 'token', null, [ + 'region' => 'fr-par' + ]), true, ]; yield [ - new Dsn('scaleway+smtps', 'default'), - true, - ]; - - yield [ - new Dsn('scaleway+smtp', 'example.com'), + new Dsn('scaleway+smtp', 'example.com', 'project-id', 'token', null, [ + 'region' => 'fr-par' + ]), true, ]; } @@ -50,46 +53,40 @@ public function createProvider(): iterable $logger = $this->getLogger(); yield [ - new Dsn('scaleway+api', 'default', self::USER), - new ScalewayApiTransport(self::USER, 'fr-par', $this->getClient(), $dispatcher, $logger), - ]; - - yield [ - new Dsn('scaleway+api', 'example.com', self::USER, '', 8080), - (new ScalewayApiTransport(self::USER, 'fr-par', $this->getClient(), $dispatcher, $logger))->setHost('example.com')->setPort(8080), - ]; - - yield [ - new Dsn('scaleway+api', 'example.com', self::USER, '', 8080, ['message_stream' => 'broadcasts']), - (new ScalewayApiTransport(self::USER, 'fr-par', $this->getClient(), $dispatcher, $logger))->setHost('example.com')->setPort(8080)->setMessageStream('broadcasts'), + new Dsn('scaleway+api', 'default', self::USER, self::PASSWORD), + new ScalewayApiTransport(self::PASSWORD, 'fr-par', self::USER, $this->getClient(), $dispatcher, $logger), ]; yield [ - new Dsn('scaleway', 'default', self::USER), - new ScalewaySmtpTransport(self::USER, $dispatcher, $logger), + new Dsn('scaleway+api', 'example.com', self::USER, self::PASSWORD, 8080), + (new ScalewayApiTransport(self::PASSWORD, 'fr-par', self::USER, $this->getClient(), $dispatcher, $logger))->setHost('example.com')->setPort(8080), ]; yield [ - new Dsn('scaleway+smtp', 'default', self::USER), - new ScalewaySmtpTransport(self::USER, $dispatcher, $logger), + new Dsn('scaleway+api', 'default', self::USER, self::PASSWORD, null, [ + 'region' => 'nl-ams' + ]), + new ScalewayApiTransport(self::PASSWORD, 'nl-ams', self::USER, $this->getClient(), $dispatcher, $logger), ]; yield [ - new Dsn('scaleway+smtps', 'default', self::USER), - new ScalewaySmtpTransport(self::USER, $dispatcher, $logger), + new Dsn('scaleway+smtp', 'default', self::USER, self::PASSWORD), + new ScalewaySmtpTransport(self::PASSWORD, 'fr-par', self::USER, $dispatcher, $logger), ]; yield [ - new Dsn('scaleway+smtps', 'default', self::USER, null, null, ['message_stream' => 'broadcasts']), - (new ScalewaySmtpTransport(self::USER, $dispatcher, $logger))->setMessageStream('broadcasts'), + new Dsn('scaleway+smtp', 'default', self::USER, self::PASSWORD, null, [ + 'region' => 'nl-ams' + ]), + new ScalewaySmtpTransport(self::PASSWORD, 'nl-ams', self::USER, $dispatcher, $logger), ]; } public function unsupportedSchemeProvider(): iterable { yield [ - new Dsn('scaleway+foo', 'default', self::USER), - 'The "scaleway+foo" scheme is not supported; supported schemes for mailer "scaleway" are: "scaleway", "scaleway+api", "scaleway+smtp", "scaleway+smtps".', + new Dsn('scaleway', 'default', self::USER, self::PASSWORD), + 'The "scaleway" scheme is not supported; supported schemes for mailer "scaleway" are: "scaleway+api", "scaleway+smtp", "scaleway+smtps".', ]; }