From a5d78787e613b852b7af55d0dcf6915d8ea27705 Mon Sep 17 00:00:00 2001 From: Oskar Stark Date: Thu, 10 Dec 2020 14:26:39 +0100 Subject: [PATCH] [Notifier] Rework/streamline bridges (5.2) --- .../Discord/DiscordTransportFactory.php | 11 +-- .../Notifier/Bridge/Discord/README.md | 3 +- .../Tests/DiscordTransportFactoryTest.php | 54 ++++++++---- .../Discord/Tests/DiscordTransportTest.php | 29 ++++--- .../Notifier/Bridge/Discord/composer.json | 1 - .../Bridge/Esendex/EsendexTransport.php | 1 + .../Esendex/EsendexTransportFactory.php | 22 +++-- .../Notifier/Bridge/Esendex/README.md | 9 +- .../Tests/EsendexTransportFactoryTest.php | 85 +++++++++++++++++++ .../Esendex/Tests/EsendexTransportTest.php | 22 +++-- .../Bridge/Firebase/FirebaseTransport.php | 6 +- .../Firebase/Tests/FirebaseTransportTest.php | 2 +- .../Tests/FreeMobileTransportTest.php | 42 +++++---- .../Bridge/GoogleChat/GoogleChatOptions.php | 1 + .../Bridge/GoogleChat/GoogleChatTransport.php | 1 + .../GoogleChat/GoogleChatTransportFactory.php | 23 +++-- .../Notifier/Bridge/GoogleChat/README.md | 5 +- .../Tests/GoogleChatTransportFactoryTest.php | 38 +++++---- .../Tests/GoogleChatTransportTest.php | 27 +++--- .../Infobip/InfobipTransportFactory.php | 11 +-- .../Notifier/Bridge/Infobip/README.md | 1 - .../Tests/InfobipTransportFactoryTest.php | 52 +++++++----- .../Infobip/Tests/InfobipTransportTest.php | 16 ++-- .../Bridge/LinkedIn/LinkedInTransport.php | 3 +- .../LinkedIn/LinkedInTransportFactory.php | 11 +-- .../Notifier/Bridge/LinkedIn/README.md | 3 +- .../Tests/LinkedInTransportFactoryTest.php | 42 +++++---- .../LinkedIn/Tests/LinkedInTransportTest.php | 71 ++++++++-------- .../Bridge/Mattermost/MattermostTransport.php | 6 +- .../Tests/MattermostTransportFactoryTest.php | 1 + .../Notifier/Bridge/Mobyt/MobytOptions.php | 2 +- .../Notifier/Bridge/Mobyt/MobytTransport.php | 8 +- .../Bridge/Mobyt/MobytTransportFactory.php | 17 ++-- .../Component/Notifier/Bridge/Mobyt/README.md | 3 +- .../Bridge/Mobyt/Tests/MobytOptionsTest.php | 43 +++++----- .../Notifier/Bridge/Nexmo/NexmoTransport.php | 6 +- .../Bridge/Nexmo/Tests/NexmoTransportTest.php | 2 +- .../Bridge/OvhCloud/OvhCloudTransport.php | 6 +- .../OvhCloud/Tests/OvhCloudTransportTest.php | 2 +- .../Bridge/RocketChat/RocketChatTransport.php | 6 +- .../Tests/RocketChatTransportFactoryTest.php | 1 + .../Notifier/Bridge/Sendinblue/README.md | 7 +- .../Bridge/Sendinblue/SendinblueTransport.php | 6 +- .../Sendinblue/SendinblueTransportFactory.php | 19 +++-- .../Tests/SendinblueTransportFactoryTest.php | 59 +++++++++---- .../Tests/SendinblueTransportTest.php | 16 ++-- .../Notifier/Bridge/Sinch/SinchTransport.php | 6 +- .../Bridge/Sinch/Tests/SinchTransportTest.php | 2 +- .../Component/Notifier/Bridge/Slack/README.md | 32 +++---- .../Notifier/Bridge/Slack/SlackTransport.php | 1 + .../Tests/Block/SlackSectionBlockTest.php | 4 +- .../Slack/Tests/SlackTransportFactoryTest.php | 39 ++++++--- .../Bridge/Slack/Tests/SlackTransportTest.php | 8 +- .../Notifier/Bridge/Smsapi/README.md | 7 +- .../Bridge/Smsapi/SmsapiTransport.php | 1 + .../Bridge/Smsapi/SmsapiTransportFactory.php | 22 +++-- .../Tests/SmsapiTransportFactoryTest.php | 84 ++++++++++++++++++ .../Smsapi/Tests/SmsapiTransportTest.php | 50 +++++++++++ .../Notifier/Bridge/Smsapi/phpunit.xml.dist | 31 +++++++ .../Bridge/Telegram/TelegramTransport.php | 11 ++- .../Tests/TelegramTransportFactoryTest.php | 2 + .../Twilio/Tests/TwilioTransportTest.php | 2 +- .../Bridge/Twilio/TwilioTransport.php | 6 +- .../Component/Notifier/Bridge/Zulip/README.md | 7 +- .../Zulip/Tests/ZulipTransportFactoryTest.php | 84 ++++++++++++++++++ .../Bridge/Zulip/Tests/ZulipTransportTest.php | 50 +++++++++++ .../Notifier/Bridge/Zulip/ZulipTransport.php | 6 +- .../Bridge/Zulip/ZulipTransportFactory.php | 22 +++-- .../Notifier/Bridge/Zulip/phpunit.xml.dist | 31 +++++++ 69 files changed, 927 insertions(+), 383 deletions(-) create mode 100644 src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Smsapi/phpunit.xml.dist create mode 100644 src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Zulip/phpunit.xml.dist diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/DiscordTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Discord/DiscordTransportFactory.php index 3a8afa2551..a192832f56 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/DiscordTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Discord/DiscordTransportFactory.php @@ -30,6 +30,11 @@ final class DiscordTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('discord' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'discord', $this->getSupportedSchemes()); + } + $token = $this->getUser($dsn); $webhookId = $dsn->getOption('webhook_id'); @@ -40,11 +45,7 @@ final class DiscordTransportFactory extends AbstractTransportFactory $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - if ('discord' === $scheme) { - return (new DiscordTransport($token, $webhookId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'discord', $this->getSupportedSchemes()); + return (new DiscordTransport($token, $webhookId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/README.md b/src/Symfony/Component/Notifier/Bridge/Discord/README.md index 67a176d7e0..243904c276 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Discord/README.md @@ -1,13 +1,12 @@ Discord Notifier ================ -Provides Discord integration for Symfony Notifier. +Provides [Discord](https://discord.com) integration for Symfony Notifier. DSN example ----------- ``` -// .env file DISCORD_DSN=discord://TOKEN@default?webhook_id=ID ``` diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportFactoryTest.php index 09508b714f..40c8f4d4aa 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportFactoryTest.php @@ -21,46 +21,64 @@ final class DiscordTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = new DiscordTransportFactory(); + $factory = $this->createFactory(); - $host = 'testHost'; - $webhookId = 'testChannel'; + $transport = $factory->create(Dsn::fromString('discord://token@host.test?webhook_id=testWebhookId')); - $transport = $factory->create(Dsn::fromString(sprintf('discord://%s@%s/?webhook_id=%s', 'token', $host, $webhookId))); - - $this->assertSame(sprintf('discord://%s?webhook_id=%s', $host, $webhookId), (string) $transport); + $this->assertSame('discord://host.test?webhook_id=testWebhookId', (string) $transport); } - public function testCreateWithNoWebhookIdThrowsMalformed() + public function testCreateWithMissingOptionWebhookIdThrowsIncompleteDsnException() { - $factory = new DiscordTransportFactory(); + $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); $factory->create(Dsn::fromString('discord://token@host')); } - public function testCreateWithNoTokenThrowsMalformed() + public function testCreateWithNoTokenThrowsIncompleteDsnException() { - $factory = new DiscordTransportFactory(); + $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); - $factory->create(Dsn::fromString(sprintf('discord://%s/?webhook_id=%s', 'testHost', 'testChannel'))); + $factory->create(Dsn::fromString('discord://host.test?webhook_id=testWebhookId')); } - public function testSupportsDiscordScheme() + public function testSupportsReturnsTrueWithSupportedScheme() { - $factory = new DiscordTransportFactory(); + $factory = $this->createFactory(); - $this->assertTrue($factory->supports(Dsn::fromString('discord://host/?webhook_id=testChannel'))); - $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host/?webhook_id=testChannel'))); + $this->assertTrue($factory->supports(Dsn::fromString('discord://host?webhook_id=testWebhookId'))); } - public function testNonDiscordSchemeThrows() + public function testSupportsReturnsFalseWithUnsupportedScheme() { - $factory = new DiscordTransportFactory(); + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host?webhook_id=testWebhookId'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); $this->expectException(UnsupportedSchemeException::class); - $factory->create(Dsn::fromString('somethingElse://token@host/?webhook_id=testChannel')); + $factory->create(Dsn::fromString('somethingElse://token@host?webhook_id=testWebhookId')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "webhook_id" option + $factory->create(Dsn::fromString('somethingElse://token@host')); + } + + private function createFactory(): DiscordTransportFactory + { + return new DiscordTransportFactory(); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportTest.php index ead3027580..9d4d0ba005 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Discord/Tests/DiscordTransportTest.php @@ -25,33 +25,31 @@ final class DiscordTransportTest extends TestCase { public function testToStringContainsProperties() { - $webhookId = 'testChannel'; + $transport = $this->createTransport(); - $transport = new DiscordTransport('testToken', $webhookId, $this->createMock(HttpClientInterface::class)); - $transport->setHost('testHost'); - - $this->assertSame(sprintf('discord://%s?webhook_id=%s', 'testHost', $webhookId), (string) $transport); + $this->assertSame('discord://host.test?webhook_id=testWebhookId', (string) $transport); } public function testSupportsChatMessage() { - $transport = new DiscordTransport('testToken', 'testChannel', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new ChatMessage('testChatMessage'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonChatMessageThrows() + public function testSendNonChatMessageThrowsLogicException() { + $transport = $this->createTransport(); + $this->expectException(LogicException::class); - $transport = new DiscordTransport('testToken', 'testChannel', $this->createMock(HttpClientInterface::class)); $transport->send($this->createMock(MessageInterface::class)); } public function testSendChatMessageWithMoreThan2000CharsThrowsLogicException() { - $transport = new DiscordTransport('testToken', 'testChannel', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $this->expectException(LogicException::class); $this->expectExceptionMessage('The subject length of a Discord message must not exceed 2000 characters.'); @@ -61,9 +59,6 @@ final class DiscordTransportTest extends TestCase public function testSendWithErrorResponseThrows() { - $this->expectException(TransportException::class); - $this->expectExceptionMessageMatches('/testDescription.+testErrorCode/'); - $response = $this->createMock(ResponseInterface::class); $response->expects($this->exactly(2)) ->method('getStatusCode') @@ -76,8 +71,16 @@ final class DiscordTransportTest extends TestCase return $response; }); - $transport = new DiscordTransport('testToken', 'testChannel', $client); + $transport = $this->createTransport($client); + + $this->expectException(TransportException::class); + $this->expectExceptionMessageMatches('/testDescription.+testErrorCode/'); $transport->send(new ChatMessage('testMessage')); } + + private function createTransport(?HttpClientInterface $client = null): DiscordTransport + { + return (new DiscordTransport('testToken', 'testWebhookId', $client ?? $this->createMock(HttpClientInterface::class)))->setHost('host.test'); + } } diff --git a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json index a47bc90ac9..934f1f5886 100644 --- a/src/Symfony/Component/Notifier/Bridge/Discord/composer.json +++ b/src/Symfony/Component/Notifier/Bridge/Discord/composer.json @@ -30,5 +30,4 @@ ] }, "minimum-stability": "dev" - } diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransport.php b/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransport.php index a216a16f98..812c58906e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransport.php @@ -62,6 +62,7 @@ final class EsendexTransport extends AbstractTransport 'to' => $message->getPhone(), 'body' => $message->getSubject(), ]; + if (null !== $this->from) { $messageData['from'] = $this->from; } diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransportFactory.php index f526f5a385..2edf0788b3 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/EsendexTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Notifier\Bridge\Esendex; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; use Symfony\Component\Notifier\Transport\AbstractTransportFactory; use Symfony\Component\Notifier\Transport\Dsn; @@ -27,17 +28,28 @@ final class EsendexTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('esendex' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'esendex', $this->getSupportedSchemes()); + } + $token = $this->getUser($dsn).':'.$this->getPassword($dsn); $accountReference = $dsn->getOption('accountreference'); + + if (!$accountReference) { + throw new IncompleteDsnException('Missing accountreference.', $dsn->getOriginalDsn()); + } + $from = $dsn->getOption('from'); + + if (!$from) { + throw new IncompleteDsnException('Missing from.', $dsn->getOriginalDsn()); + } + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - if ('esendex' === $scheme) { - return (new EsendexTransport($token, $accountReference, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'esendex', $this->getSupportedSchemes()); + return (new EsendexTransport($token, $accountReference, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/README.md b/src/Symfony/Component/Notifier/Bridge/Esendex/README.md index fd1f142ed7..22b93a22ba 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/README.md @@ -1,21 +1,20 @@ Esendex Notifier ================ -Provides Esendex integration for Symfony Notifier. +Provides [Esendex](https://esendex.com) integration for Symfony Notifier. DSN example ----------- ``` -// .env file -ESENDEX_DSN='esendex://EMAIL:PASSWORD@default?accountreference=ACCOUNT_REFERENCE&from=FROM' +ESENDEX_DSN=esendex://EMAIL:PASSWORD@default?accountreference=ACCOUNT_REFERENCE&from=FROM ``` where: - `EMAIL` is your Esendex account email - `PASSWORD` is the Esendex API password - - `ACCOUNT_REFERENCE` is the Esendex account reference that the messages should be sent from. - - `FROM` is the alphanumeric originator for the message to appear to originate from. + - `ACCOUNT_REFERENCE` is the Esendex account reference that the messages should be sent from + - `FROM` is the alphanumeric originator for the message to appear to originate from See Esendex documentation at https://developers.esendex.com/api-reference#smsapis diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportFactoryTest.php new file mode 100644 index 0000000000..469a23c985 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportFactoryTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Esendex\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\Dsn; + +final class EsendexTransportFactoryTest extends TestCase +{ + public function testCreateWithDsn() + { + $factory = $this->createFactory(); + + $transport = $factory->create(Dsn::fromString('esendex://email:password@host.test?accountreference=testAccountreference&from=testFrom')); + + $this->assertSame('esendex://host.test', (string) $transport); + } + + public function testCreateWithMissingOptionAccountreferenceThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('esendex://email:password@host?from=FROM')); + } + + public function testCreateWithMissingOptionFromThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('esendex://email:password@host?accountreference=ACCOUNTREFERENCE')); + } + + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertTrue($factory->supports(Dsn::fromString('esendex://email:password@host?accountreference=ACCOUNTREFERENCE&from=FROM'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://email:password@host?accountreference=ACCOUNTREFERENCE&from=FROM'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + $factory->create(Dsn::fromString('somethingElse://email:password@host?accountreference=REFERENCE&from=FROM')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "from" option + $factory->create(Dsn::fromString('somethingElse://email:password@host?accountreference=REFERENCE')); + } + + private function createFactory(): EsendexTransportFactory + { + return new EsendexTransportFactory(); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportTest.php index 667ed59d6c..295b5e6102 100644 --- a/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Esendex/Tests/EsendexTransportTest.php @@ -25,23 +25,22 @@ final class EsendexTransportTest extends TestCase { public function testToString() { - $transport = new EsendexTransport('testToken', 'accountReference', 'from', $this->createMock(HttpClientInterface::class)); - $transport->setHost('testHost'); + $transport = $this->createTransport(); - $this->assertSame(sprintf('esendex://%s', 'testHost'), (string) $transport); + $this->assertSame('esendex://host.test', (string) $transport); } public function testSupportsSmsMessage() { - $transport = new EsendexTransport('testToken', 'accountReference', 'from', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new SmsMessage('phone', 'testSmsMessage'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrows() + public function testSendNonSmsMessageThrowsLogicException() { - $transport = new EsendexTransport('testToken', 'accountReference', 'from', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $this->expectException(LogicException::class); $transport->send($this->createMock(MessageInterface::class)); @@ -58,10 +57,11 @@ final class EsendexTransportTest extends TestCase return $response; }); - $transport = new EsendexTransport('testToken', 'accountReference', 'from', $client); + $transport = $this->createTransport($client); $this->expectException(TransportException::class); $this->expectExceptionMessage('Unable to send the SMS: error 500.'); + $transport->send(new SmsMessage('phone', 'testMessage')); } @@ -79,10 +79,16 @@ final class EsendexTransportTest extends TestCase return $response; }); - $transport = new EsendexTransport('testToken', 'accountReference', 'from', $client); + $transport = $this->createTransport($client); $this->expectException(TransportException::class); $this->expectExceptionMessage('Unable to send the SMS: error 500. Details from Esendex: accountreference_invalid: "Invalid Account Reference EX0000000".'); + $transport->send(new SmsMessage('phone', 'testMessage')); } + + private function createTransport(?HttpClientInterface $client = null): EsendexTransport + { + return (new EsendexTransport('testToken', 'testAccountReference', 'testFrom', $client ?: $this->createMock(HttpClientInterface::class)))->setHost('host.test'); + } } diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php b/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php index 1c1684d53a..12593b1256 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/FirebaseTransport.php @@ -88,9 +88,9 @@ final class FirebaseTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['results'][0]['message_id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['results'][0]['message_id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php index 8f377cdd88..1aea969dd5 100644 --- a/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Firebase/Tests/FirebaseTransportTest.php @@ -38,7 +38,7 @@ final class FirebaseTransportTest extends TestCase $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { $transport = $this->createTransport(); diff --git a/src/Symfony/Component/Notifier/Bridge/FreeMobile/Tests/FreeMobileTransportTest.php b/src/Symfony/Component/Notifier/Bridge/FreeMobile/Tests/FreeMobileTransportTest.php index 7f8d648154..d88cd11eea 100644 --- a/src/Symfony/Component/Notifier/Bridge/FreeMobile/Tests/FreeMobileTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/FreeMobile/Tests/FreeMobileTransportTest.php @@ -22,45 +22,53 @@ final class FreeMobileTransportTest extends TestCase { public function testToStringContainsProperties() { - $transport = $this->getTransport('0611223344'); + $transport = $this->createTransport('0611223344'); $this->assertSame('freemobile://host.test?phone=0611223344', (string) $transport); } - public function testSupportsMessageInterface() + /** + * @dataProvider supportsProvider + */ + public function testSupportsMessageInterface(bool $expected, string $configuredPhoneNumber, MessageInterface $message) { - $transport = $this->getTransport('0611223344'); + $transport = $this->createTransport($configuredPhoneNumber); - $this->assertTrue($transport->supports(new SmsMessage('0611223344', 'Hello!'))); - $this->assertTrue($transport->supports(new SmsMessage('+33611223344', 'Hello!'))); - $this->assertFalse($transport->supports(new SmsMessage('0699887766', 'Hello!'))); - $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); - - $transport = $this->getTransport('+33611223344'); - - $this->assertTrue($transport->supports(new SmsMessage('0611223344', 'Hello!'))); - $this->assertTrue($transport->supports(new SmsMessage('+33611223344', 'Hello!'))); + $this->assertSame($expected, $transport->supports($message)); } - public function testSendNonSmsMessageThrowsException() + /** + * @return iterable + */ + public function supportsProvider(): iterable { - $transport = $this->getTransport('0611223344'); + yield [true, '0611223344', new SmsMessage('0611223344', 'Hello!')]; + yield [true, '0611223344', new SmsMessage('+33611223344', 'Hello!')]; + yield [false, '0611223344', new SmsMessage('0699887766', 'Hello!')]; + yield [false, '0611223344', $this->createMock(MessageInterface::class)]; + yield [true, '+33611223344', new SmsMessage('0611223344', 'Hello!')]; + yield [true, '+33611223344', new SmsMessage('+33611223344', 'Hello!')]; + } + + public function testSendNonSmsMessageThrowsLogicException() + { + $transport = $this->createTransport('0611223344'); $this->expectException(LogicException::class); $transport->send($this->createMock(MessageInterface::class)); } - public function testSendSmsMessageButInvalidPhoneThrowsException() + public function testSendSmsMessageButInvalidPhoneThrowsLogicException() { - $transport = $this->getTransport('0611223344'); + $transport = $this->createTransport('0611223344'); $this->expectException(LogicException::class); $transport->send(new SmsMessage('0699887766', 'Hello!')); } - private function getTransport(string $phone): FreeMobileTransport + private function createTransport(string $phone): FreeMobileTransport { return (new FreeMobileTransport('login', 'pass', $phone, $this->createMock(HttpClientInterface::class)))->setHost('host.test'); } diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatOptions.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatOptions.php index a7b2b81771..407728ab49 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatOptions.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatOptions.php @@ -40,6 +40,7 @@ final class GoogleChatOptions implements MessageOptionsInterface if ($notification->getContent()) { $text .= "\r\n".$notification->getContent(); } + if ($exception = $notification->getExceptionAsString()) { $text .= "\r\n".'```'.$notification->getExceptionAsString().'```'; } diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php index 543b2a0a4c..fbe933cd56 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransport.php @@ -89,6 +89,7 @@ final class GoogleChatTransport extends AbstractTransport if (!$message instanceof ChatMessage) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" (instance of "%s" given).', __CLASS__, ChatMessage::class, \get_class($message))); } + if ($message->getOptions() && !$message->getOptions() instanceof GoogleChatOptions) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" for options.', __CLASS__, GoogleChatOptions::class)); } diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransportFactory.php index 039b3f5e7a..94cbc786f2 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/GoogleChatTransportFactory.php @@ -32,21 +32,18 @@ final class GoogleChatTransportFactory extends AbstractTransportFactory { $scheme = $dsn->getScheme(); - if ('googlechat' === $scheme) { - $space = explode('/', $dsn->getPath())[1]; - $accessKey = $this->getUser($dsn); - $accessToken = $this->getPassword($dsn); - $threadKey = $dsn->getOption('threadKey'); - $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); - $port = $dsn->getPort(); - - return (new GoogleChatTransport($space, $accessKey, $accessToken, $this->client, $this->dispatcher)) - ->setThreadKey($threadKey) - ->setHost($host) - ->setPort($port); + if ('googlechat' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'googlechat', $this->getSupportedSchemes()); } - throw new UnsupportedSchemeException($dsn, 'googlechat', $this->getSupportedSchemes()); + $space = explode('/', $dsn->getPath())[1]; + $accessKey = $this->getUser($dsn); + $accessToken = $this->getPassword($dsn); + $threadKey = $dsn->getOption('threadKey'); + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new GoogleChatTransport($space, $accessKey, $accessToken, $this->client, $this->dispatcher))->setThreadKey($threadKey)->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md b/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md index d1d85ebc2d..8a4b55c7e8 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/README.md @@ -7,15 +7,14 @@ DSN example ----------- ``` -// .env file -INFOBIP_DSN=googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?threadKey=THREAD_KEY +GOOGLE_CHAT_DSN=googlechat://ACCESS_KEY:ACCESS_TOKEN@default/SPACE?threadKey=THREAD_KEY ``` where: - `ACCESS_KEY` is your Google Chat access key - `ACCESS_TOKEN` is your Google Chat access token - `SPACE` is the Google Chat space - - `THREAD_KEY` is the the Google Chat message thread to group messages into a single thread + - `THREAD_KEY` is the the Google Chat message thread to group messages into a single thread (optional) Resources --------- diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportFactoryTest.php index 641277a5f2..01a8d263f2 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportFactoryTest.php @@ -21,47 +21,55 @@ final class GoogleChatTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = new GoogleChatTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'googlechat://abcde-fghij:kl_mnopqrstwxyz%3D@chat.googleapis.com/AAAAA_YYYYY'; - $transport = $factory->create(Dsn::fromString($dsn)); + $transport = $factory->create(Dsn::fromString('googlechat://abcde-fghij:kl_mnopqrstwxyz%3D@chat.googleapis.com/AAAAA_YYYYY')); $this->assertSame('googlechat://chat.googleapis.com/AAAAA_YYYYY', (string) $transport); } public function testCreateWithThreadKeyInDsn() { - $factory = new GoogleChatTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'googlechat://abcde-fghij:kl_mnopqrstwxyz%3D@chat.googleapis.com/AAAAA_YYYYY?threadKey=abcdefg'; - $transport = $factory->create(Dsn::fromString($dsn)); + $transport = $factory->create(Dsn::fromString('googlechat://abcde-fghij:kl_mnopqrstwxyz%3D@chat.googleapis.com/AAAAA_YYYYY?threadKey=abcdefg')); $this->assertSame('googlechat://chat.googleapis.com/AAAAA_YYYYY?threadKey=abcdefg', (string) $transport); } public function testCreateRequiresCredentials() { - $this->expectException(IncompleteDsnException::class); - $factory = new GoogleChatTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'googlechat://chat.googleapis.com/v1/spaces/AAAAA_YYYYY/messages'; - $factory->create(Dsn::fromString($dsn)); + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('googlechat://chat.googleapis.com/v1/spaces/AAAAA_YYYYY/messages')); } - public function testSupportsGoogleChatScheme() + public function testSupportsReturnsTrueWithSupportedScheme() { - $factory = new GoogleChatTransportFactory(); + $factory = $this->createFactory(); $this->assertTrue($factory->supports(Dsn::fromString('googlechat://host/path'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host/path'))); } - public function testNonGoogleChatSchemeThrows() + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() { - $factory = new GoogleChatTransportFactory(); + $factory = $this->createFactory(); $this->expectException(UnsupportedSchemeException::class); - $factory->create(Dsn::fromString('somethingElse://host/path')); } + + private function createFactory(): GoogleChatTransportFactory + { + return new GoogleChatTransportFactory(); + } } diff --git a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php index 02f829be67..f3d63d6506 100644 --- a/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/GoogleChat/Tests/GoogleChatTransportTest.php @@ -28,7 +28,7 @@ class GoogleChatTransportTest extends TestCase { public function testToStringContainsProperties() { - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $transport->setHost(null); $this->assertSame('googlechat://chat.googleapis.com/My-Space', (string) $transport); @@ -36,17 +36,17 @@ class GoogleChatTransportTest extends TestCase public function testSupportsChatMessage() { - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $this->createMock(HttpClientInterface::class)); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new ChatMessage('testChatMessage'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonChatMessageThrows() + public function testSendNonChatMessageThrowsLogicException() { - $this->expectException(LogicException::class); + $transport = $this->createTransport(); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $this->createMock(HttpClientInterface::class)); + $this->expectException(LogicException::class); $transport->send($this->createMock(MessageInterface::class)); } @@ -69,7 +69,7 @@ class GoogleChatTransportTest extends TestCase return $response; }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $sentMessage = $transport->send(new ChatMessage('testMessage')); @@ -93,7 +93,7 @@ class GoogleChatTransportTest extends TestCase return $response; }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $sentMessage = $transport->send(new ChatMessage('testMessage')); @@ -124,7 +124,7 @@ class GoogleChatTransportTest extends TestCase return $response; }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $transport->setThreadKey('My-Thread'); $sentMessage = $transport->send(new ChatMessage('testMessage')); @@ -157,7 +157,7 @@ class GoogleChatTransportTest extends TestCase return $response; }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $sentMessage = $transport->send($chatMessage); @@ -173,7 +173,7 @@ class GoogleChatTransportTest extends TestCase return $this->createMock(ResponseInterface::class); }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $transport->send(new ChatMessage('testMessage', $this->createMock(MessageOptionsInterface::class))); } @@ -202,10 +202,15 @@ class GoogleChatTransportTest extends TestCase return $response; }); - $transport = new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client); + $transport = $this->createTransport($client); $sentMessage = $transport->send(new ChatMessage('testMessage')); $this->assertSame('spaces/My-Space/messages/abcdefg.hijklmno', $sentMessage->getMessageId()); } + + private function createTransport(?HttpClientInterface $client = null): GoogleChatTransport + { + return new GoogleChatTransport('My-Space', 'theAccessKey', 'theAccessToken=', $client ?: $this->createMock(HttpClientInterface::class)); + } } diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/InfobipTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Infobip/InfobipTransportFactory.php index 3c8a7968db..6b1d6ca745 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/InfobipTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/InfobipTransportFactory.php @@ -31,6 +31,11 @@ final class InfobipTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('infobip' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'infobip', $this->getSupportedSchemes()); + } + $authToken = $this->getUser($dsn); $from = $dsn->getOption('from'); $host = $dsn->getHost(); @@ -40,11 +45,7 @@ final class InfobipTransportFactory extends AbstractTransportFactory throw new IncompleteDsnException('Missing from.', $dsn->getOriginalDsn()); } - if ('infobip' === $scheme) { - return (new InfobipTransport($authToken, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'infobip', $this->getSupportedSchemes()); + return (new InfobipTransport($authToken, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/README.md b/src/Symfony/Component/Notifier/Bridge/Infobip/README.md index 91b3d482bf..841b659d5f 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/README.md @@ -7,7 +7,6 @@ DSN example ----------- ``` -// .env file INFOBIP_DSN=infobip://AUTH_TOKEN@INFOBIP_HOST?from=FROM ``` diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportFactoryTest.php index 1478ebb169..a10c3214f4 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportFactoryTest.php @@ -21,43 +21,55 @@ final class InfobipTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = new InfobipTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'infobip://authtoken@default?from=0611223344'; - $transport = $factory->create(Dsn::fromString($dsn)); - $transport->setHost('host.test'); + $transport = $factory->create(Dsn::fromString('infobip://authtoken@host.test?from=0611223344')); $this->assertSame('infobip://host.test?from=0611223344', (string) $transport); } - public function testCreateWithNoFromThrowsMalformed() + public function testCreateWithNoFromThrowsIncompleteDsnException() { - $factory = new InfobipTransportFactory(); + $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); - - $dsnIncomplete = 'infobip://authtoken@default'; - $factory->create(Dsn::fromString($dsnIncomplete)); + $factory->create(Dsn::fromString('infobip://authtoken@default')); } - public function testSupportsInfobipScheme() + public function testSupportsReturnsTrueWithSupportedScheme() { - $factory = new InfobipTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'infobip://authtoken@default?from=0611223344'; - $dsnUnsupported = 'unsupported://authtoken@default?from=0611223344'; - - $this->assertTrue($factory->supports(Dsn::fromString($dsn))); - $this->assertFalse($factory->supports(Dsn::fromString($dsnUnsupported))); + $this->assertTrue($factory->supports(Dsn::fromString('infobip://authtoken@default?from=0611223344'))); } - public function testNonInfobipSchemeThrows() + public function testSupportsReturnsFalseWithUnsupportedScheme() { - $factory = new InfobipTransportFactory(); + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://authtoken@default?from=0611223344'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + $factory->create(Dsn::fromString('somethingElse://authtoken@default?from=FROM')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); $this->expectException(UnsupportedSchemeException::class); - $dsnUnsupported = 'unsupported://authtoken@default?from=0611223344'; - $factory->create(Dsn::fromString($dsnUnsupported)); + // unsupported scheme and missing "from" option + $factory->create(Dsn::fromString('somethingElse://authtoken@default')); + } + + private function createFactory(): InfobipTransportFactory + { + return new InfobipTransportFactory(); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportTest.php index ce616021ca..7c4a9d2143 100644 --- a/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Infobip/Tests/InfobipTransportTest.php @@ -22,34 +22,30 @@ final class InfobipTransportTest extends TestCase { public function testToStringContainsProperties() { - $transport = $this->getTransport(); + $transport = $this->createTransport(); $this->assertSame('infobip://host.test?from=0611223344', (string) $transport); } public function testSupportsMessageInterface() { - $transport = $this->getTransport(); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new SmsMessage('0611223344', 'Hello!'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { - $transport = $this->getTransport(); + $transport = $this->createTransport(); $this->expectException(LogicException::class); $transport->send($this->createMock(MessageInterface::class)); } - private function getTransport(): InfobipTransport + private function createTransport(): InfobipTransport { - return (new InfobipTransport( - 'authtoken', - '0611223344', - $this->createMock(HttpClientInterface::class) - ))->setHost('host.test'); + return (new InfobipTransport('authtoken', '0611223344', $this->createMock(HttpClientInterface::class)))->setHost('host.test'); } } diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransport.php b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransport.php index 9030496307..1e724846ef 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransport.php @@ -63,6 +63,7 @@ final class LinkedInTransport extends AbstractTransport if (!$message instanceof ChatMessage) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" (instance of "%s" given).', __CLASS__, ChatMessage::class, \get_class($message))); } + if ($message->getOptions() && !$message->getOptions() instanceof LinkedInOptions) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" for options.', __CLASS__, LinkedInOptions::class)); } @@ -87,7 +88,7 @@ final class LinkedInTransport extends AbstractTransport $result = $response->toArray(false); if (!$result['id']) { - throw new TransportException(sprintf('Unable to post the Linkedin message : "%s".', $result['error']), $response); + throw new TransportException(sprintf('Unable to post the Linkedin message: "%s".', $result['error']), $response); } $sentMessage = new SentMessage($message, (string) $this); diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransportFactory.php index f13afa4723..d6f1a8523b 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/LinkedInTransportFactory.php @@ -26,16 +26,17 @@ class LinkedInTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('linkedin' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'linkedin', $this->getSupportedSchemes()); + } + $authToken = $this->getUser($dsn); $accountId = $this->getPassword($dsn); $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - if ('linkedin' === $scheme) { - return (new LinkedInTransport($authToken, $accountId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'linkedin', $this->getSupportedSchemes()); + return (new LinkedInTransport($authToken, $accountId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md b/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md index 907b5cfc8b..29cb85b15b 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/README.md @@ -7,8 +7,7 @@ DSN example ----------- ``` -// .env file -LINKEDIN_DSN='linkedin://ACCESS_TOKEN:USER_ID@default' +LINKEDIN_DSN=linkedin://ACCESS_TOKEN:USER_ID@default ``` where: diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportFactoryTest.php index c4943464a3..51dd961963 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportFactoryTest.php @@ -12,39 +12,45 @@ final class LinkedInTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = new LinkedInTransportFactory(); + $factory = $this->createFactory(); - $dsn = 'linkedin://login:pass@default'; - $transport = $factory->create(Dsn::fromString($dsn)); - $transport->setHost('testHost'); + $transport = $factory->create(Dsn::fromString('linkedin://accessToken:UserId@host.test')); - $this->assertSame('linkedin://testHost', (string) $transport); + $this->assertSame('linkedin://host.test', (string) $transport); } - public function testSupportsLinkedinScheme() + public function testCreateWithOnlyAccessTokenOrUserIdThrowsIncompleteDsnException() { - $factory = new LinkedInTransportFactory(); + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('linkedin://AccessTokenOrUserId@default')); + } + + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); $this->assertTrue($factory->supports(Dsn::fromString('linkedin://host/path'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host/path'))); } - public function testNonLinkedinSchemeThrows() + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() { - $factory = new LinkedInTransportFactory(); + $factory = $this->createFactory(); $this->expectException(UnsupportedSchemeException::class); - - $dsn = 'foo://login:pass@default'; - $factory->create(Dsn::fromString($dsn)); + $factory->create(Dsn::fromString('somethingElse://accessToken:UserId@default')); } - public function testIncompleteDsnMissingUserThrows() + private function createFactory(): LinkedInTransportFactory { - $factory = new LinkedInTransportFactory(); - - $this->expectException(IncompleteDsnException::class); - - $factory->create(Dsn::fromString('somethingElse://host/path')); + return new LinkedInTransportFactory(); } } diff --git a/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportTest.php b/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportTest.php index cbf9a2ba5f..524bc5d8b7 100644 --- a/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/LinkedIn/Tests/LinkedInTransportTest.php @@ -16,14 +16,16 @@ use Symfony\Contracts\HttpClient\ResponseInterface; final class LinkedInTransportTest extends TestCase { - public function testToString() + public function testToStringContainsProperties() { - $this->assertSame(sprintf('linkedin://host.test'), (string) $this->getTransport()); + $transport = $this->createTransport(); + + $this->assertSame('linkedin://host.test', (string) $transport); } public function testSupportsChatMessage() { - $transport = $this->getTransport(); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new ChatMessage('testChatMessage'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); @@ -31,10 +33,9 @@ final class LinkedInTransportTest extends TestCase public function testSendNonChatMessageThrows() { + $transport = $this->createTransport(); + $this->expectException(LogicException::class); - - $transport = $this->getTransport(); - $transport->send($this->createMock(MessageInterface::class)); } @@ -54,7 +55,7 @@ final class LinkedInTransportTest extends TestCase return $response; }); - $transport = $this->getTransport($client); + $transport = $this->createTransport($client); $transport->send(new ChatMessage('testMessage')); } @@ -77,7 +78,7 @@ final class LinkedInTransportTest extends TestCase return $response; }); - $transport = $this->getTransport($client); + $transport = $this->createTransport($client); $transport->send(new ChatMessage('testMessage')); } @@ -98,19 +99,19 @@ final class LinkedInTransportTest extends TestCase $expectedBody = json_encode([ 'specificContent' => [ - 'com.linkedin.ugc.ShareContent' => [ - 'shareCommentary' => [ - 'attributes' => [], - 'text' => 'testMessage', - ], - 'shareMediaCategory' => 'NONE', - ], + 'com.linkedin.ugc.ShareContent' => [ + 'shareCommentary' => [ + 'attributes' => [], + 'text' => 'testMessage', ], - 'visibility' => [ - 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC', + 'shareMediaCategory' => 'NONE', ], + ], + 'visibility' => [ + 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC', + ], 'lifecycleState' => 'PUBLISHED', - 'author' => 'urn:li:person:MyLogin', + 'author' => 'urn:li:person:AccountId', ]); $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ( @@ -121,7 +122,7 @@ final class LinkedInTransportTest extends TestCase return $response; }); - $transport = $this->getTransport($client); + $transport = $this->createTransport($client); $transport->send(new ChatMessage($message)); } @@ -145,19 +146,19 @@ final class LinkedInTransportTest extends TestCase $expectedBody = json_encode([ 'specificContent' => [ - 'com.linkedin.ugc.ShareContent' => [ - 'shareCommentary' => [ - 'attributes' => [], - 'text' => 'testMessage', - ], - 'shareMediaCategory' => 'NONE', - ], + 'com.linkedin.ugc.ShareContent' => [ + 'shareCommentary' => [ + 'attributes' => [], + 'text' => 'testMessage', ], - 'visibility' => [ - 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC', + 'shareMediaCategory' => 'NONE', ], + ], + 'visibility' => [ + 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC', + ], 'lifecycleState' => 'PUBLISHED', - 'author' => 'urn:li:person:MyLogin', + 'author' => 'urn:li:person:AccountId', ]); $client = new MockHttpClient(function (string $method, string $url, array $options = []) use ( @@ -169,7 +170,7 @@ final class LinkedInTransportTest extends TestCase return $response; }); - $transport = $this->getTransport($client); + $transport = $this->createTransport($client); $transport->send($chatMessage); } @@ -182,17 +183,13 @@ final class LinkedInTransportTest extends TestCase return $this->createMock(ResponseInterface::class); }); - $transport = $this->getTransport($client); + $transport = $this->createTransport($client); $transport->send(new ChatMessage('testMessage', $this->createMock(MessageOptionsInterface::class))); } - public function getTransport($client = null) + private function createTransport(?HttpClientInterface $client = null): LinkedInTransport { - return (new LinkedInTransport( - 'MyToken', - 'MyLogin', - $client ?? $this->createMock(HttpClientInterface::class) - ))->setHost('host.test'); + return (new LinkedInTransport('AuthToken', 'AccountId', $client ?? $this->createMock(HttpClientInterface::class)))->setHost('host.test'); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Mattermost/MattermostTransport.php b/src/Symfony/Component/Notifier/Bridge/Mattermost/MattermostTransport.php index 98c43e26e0..903f067f43 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mattermost/MattermostTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Mattermost/MattermostTransport.php @@ -78,9 +78,9 @@ final class MattermostTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['id']); + $sentMessage = new SentMessage($sentMessage, (string) $this); + $sentMessage->setMessageId($success['id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Mattermost/Tests/MattermostTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Mattermost/Tests/MattermostTransportFactoryTest.php index daa774130a..030c53a457 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mattermost/Tests/MattermostTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Mattermost/Tests/MattermostTransportFactoryTest.php @@ -45,6 +45,7 @@ final class MattermostTransportFactoryTest extends TestCase $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('mattermost://host.test?channel=testChannel')); } diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytOptions.php b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytOptions.php index dc45cd780e..81b7400370 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytOptions.php +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytOptions.php @@ -25,7 +25,7 @@ final class MobytOptions implements MessageOptionsInterface public const MESSAGE_TYPE_QUALITY_MEDIUM = 'L'; public const MESSAGE_TYPE_QUALITY_LOW = 'LL'; - private $options = []; + private $options; public function __construct(array $options = []) { diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransport.php b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransport.php index 1f2a793dfd..18b37e5006 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransport.php @@ -34,7 +34,7 @@ final class MobytTransport extends AbstractTransport private $from; private $typeQuality; - public function __construct(string $accountSid, string $authToken, $from, string $typeQuality, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) + public function __construct(string $accountSid, string $authToken, string $from, string $typeQuality, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) { $this->accountSid = $accountSid; $this->authToken = $authToken; @@ -93,9 +93,9 @@ final class MobytTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['order_id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['order_id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransportFactory.php index d3eda1610f..ae86675ee6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/MobytTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Notifier\Bridge\Mobyt; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; use Symfony\Component\Notifier\Transport\AbstractTransportFactory; use Symfony\Component\Notifier\Transport\Dsn; @@ -29,18 +30,24 @@ final class MobytTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('mobyt' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'mobyt', $this->getSupportedSchemes()); + } + $accountSid = $this->getUser($dsn); $authToken = $this->getPassword($dsn); $from = $dsn->getOption('from'); + + if (!$from) { + throw new IncompleteDsnException('Missing from.', $dsn->getOriginalDsn()); + } + $typeQuality = $dsn->getOption('type_quality', MobytOptions::MESSAGE_TYPE_QUALITY_LOW); $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - if ('mobyt' === $scheme) { - return (new MobytTransport($accountSid, $authToken, $from, $typeQuality, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'mobyt', $this->getSupportedSchemes()); + return (new MobytTransport($accountSid, $authToken, $from, $typeQuality, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/README.md b/src/Symfony/Component/Notifier/Bridge/Mobyt/README.md index f992b3e1ed..0e556c1047 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/README.md @@ -7,15 +7,14 @@ DSN example ----------- ``` -// .env file MOBYT_DSN=mobyt://USER_KEY:ACCESS_TOKEN@default?from=FROM&type_quality=TYPE_QUALITY ``` where: - `USER_KEY` is your Mobyt user key - `ACCESS_TOKEN` is your Mobyt access token - - `TYPE_QUALITY` is the quality : `N` for high, `L` for medium, `LL` for low (default: `L`) - `FROM` is the sender + - `TYPE_QUALITY` is the quality : `N` for high, `L` for medium, `LL` for low (default: `L`) Resources --------- diff --git a/src/Symfony/Component/Notifier/Bridge/Mobyt/Tests/MobytOptionsTest.php b/src/Symfony/Component/Notifier/Bridge/Mobyt/Tests/MobytOptionsTest.php index 23f20819c4..a56f587324 100644 --- a/src/Symfony/Component/Notifier/Bridge/Mobyt/Tests/MobytOptionsTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Mobyt/Tests/MobytOptionsTest.php @@ -6,29 +6,23 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Notifier\Bridge\Mobyt\MobytOptions; use Symfony\Component\Notifier\Notification\Notification; -class MobytOptionsTest extends TestCase +final class MobytOptionsTest extends TestCase { /** * @dataProvider fromNotificationDataProvider */ - public function testFromNotification($importance, $expectedMessageType) + public function testFromNotification(string $importance, string $expectedMessageType) { $notification = (new Notification('Foo'))->importance($importance); $options = (MobytOptions::fromNotification($notification))->toArray(); - $this->assertEquals($expectedMessageType, $options['message_type']); - } - - public function testFromNotificationDefaultLevel() - { - $notification = (new Notification('Foo'))->importance('Bar'); - - $options = (MobytOptions::fromNotification($notification))->toArray(); - - $this->assertEquals(MobytOptions::MESSAGE_TYPE_QUALITY_HIGH, $options['message_type']); + $this->assertSame($expectedMessageType, $options['message_type']); } + /** + * @return \Generator + */ public function fromNotificationDataProvider(): \Generator { yield [Notification::IMPORTANCE_URGENT, MobytOptions::MESSAGE_TYPE_QUALITY_HIGH]; @@ -37,14 +31,22 @@ class MobytOptionsTest extends TestCase yield [Notification::IMPORTANCE_LOW, MobytOptions::MESSAGE_TYPE_QUALITY_LOW]; } + public function testFromNotificationDefaultLevel() + { + $notification = (new Notification('Foo'))->importance('Bar'); + + $options = (MobytOptions::fromNotification($notification))->toArray(); + + $this->assertSame(MobytOptions::MESSAGE_TYPE_QUALITY_HIGH, $options['message_type']); + } + public function testGetRecipientIdWhenSet() { - $options = [ + $mobytOptions = new MobytOptions([ 'recipient' => 'foo', - ]; - $mobytOptions = new MobytOptions($options); + ]); - $this->assertEquals('foo', $mobytOptions->getRecipientId()); + $this->assertSame('foo', $mobytOptions->getRecipientId()); } public function testGetRecipientIdWhenNotSet() @@ -54,11 +56,12 @@ class MobytOptionsTest extends TestCase public function testToArray() { - $options = [ + $mobytOptions = new MobytOptions([ 'message' => 'foo', 'recipient' => 'bar', - ]; - $this->assertEmpty((new MobytOptions($options))->toArray()); + ]); + + $this->assertEmpty($mobytOptions->toArray()); } public function testMessageType() @@ -66,6 +69,6 @@ class MobytOptionsTest extends TestCase $mobytOptions = new MobytOptions(); $mobytOptions->messageType('foo'); - $this->assertEquals(['message_type' => 'foo'], $mobytOptions->toArray()); + $this->assertSame(['message_type' => 'foo'], $mobytOptions->toArray()); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php b/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php index c506d0ab96..d0faded96e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Nexmo/NexmoTransport.php @@ -77,9 +77,9 @@ final class NexmoTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['messages'][0]['message-id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['messages'][0]['message-id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Nexmo/Tests/NexmoTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Nexmo/Tests/NexmoTransportTest.php index 6e4c21a28d..384b879c93 100644 --- a/src/Symfony/Component/Notifier/Bridge/Nexmo/Tests/NexmoTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Nexmo/Tests/NexmoTransportTest.php @@ -35,7 +35,7 @@ final class NexmoTransportTest extends TestCase $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { $transport = $this->createTransport(); diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php b/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php index ad1757ad51..152e371000 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/OvhCloudTransport.php @@ -94,10 +94,10 @@ final class OvhCloudTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['ids'][0]); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['ids'][0]); - return $message; + return $sentMessage; } /** diff --git a/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php b/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php index e834158246..9b3699f401 100644 --- a/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/OvhCloud/Tests/OvhCloudTransportTest.php @@ -35,7 +35,7 @@ final class OvhCloudTransportTest extends TestCase $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { $transport = $this->createTransport(); diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php b/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php index ef2f68b0fb..fdf3eafd4a 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/RocketChatTransport.php @@ -92,9 +92,9 @@ final class RocketChatTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['message']['_id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['message']['_id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/RocketChat/Tests/RocketChatTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/RocketChat/Tests/RocketChatTransportFactoryTest.php index 7d7c06e4e1..937571f44c 100644 --- a/src/Symfony/Component/Notifier/Bridge/RocketChat/Tests/RocketChatTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/RocketChat/Tests/RocketChatTransportFactoryTest.php @@ -36,6 +36,7 @@ final class RocketChatTransportFactoryTest extends TestCase $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('rocketchat://host.test?channel=testChannel')); } diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/README.md b/src/Symfony/Component/Notifier/Bridge/Sendinblue/README.md index 24016686f9..cf9336f454 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/README.md @@ -1,19 +1,18 @@ Sendinblue Notifier =================== -Provides Sendinblue integration for Symfony Notifier. +Provides [Sendinblue](https://sendinblue.com) integration for Symfony Notifier. DSN example ----------- ``` -// .env file -SENDINBLUE_DSN=sendinblue://API_KEY@default?sender=PHONE +SENDINBLUE_DSN=sendinblue://API_KEY@default?sender=SENDER ``` where: - `API_KEY` is your api key from your Sendinblue account - - `PHONE` is your sender's phone number + - `SENDER` is your sender's phone number See more info at https://developers.sendinblue.com/reference#sendtransacsms diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransport.php b/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransport.php index 889bd1c454..7964d4e20c 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransport.php @@ -75,9 +75,9 @@ final class SendinblueTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['messageId']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['messageId']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransportFactory.php index 7f9d1f9b4b..daed3ed3d6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/SendinblueTransportFactory.php @@ -29,20 +29,23 @@ final class SendinblueTransportFactory extends AbstractTransportFactory */ public function create(Dsn $dsn): TransportInterface { - if (!$sender = $dsn->getOption('sender')) { + $scheme = $dsn->getScheme(); + + if ('sendinblue' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'sendinblue', $this->getSupportedSchemes()); + } + + $apiKey = $this->getUser($dsn); + $sender = $dsn->getOption('sender'); + + if (!$sender) { throw new IncompleteDsnException('Missing sender.', $dsn->getOriginalDsn()); } - $scheme = $dsn->getScheme(); - $apiKey = $this->getUser($dsn); $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); $port = $dsn->getPort(); - if ('sendinblue' === $scheme) { - return (new SendinblueTransport($apiKey, $sender, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'sendinblue', $this->getSupportedSchemes()); + return (new SendinblueTransport($apiKey, $sender, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportFactoryTest.php index 187e1291e4..a2a1c605ef 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportFactoryTest.php @@ -14,43 +14,72 @@ namespace Symfony\Component\Notifier\Bridge\Sendinblue\Tests; use PHPUnit\Framework\TestCase; use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory; use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; use Symfony\Component\Notifier\Transport\Dsn; final class SendinblueTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = $this->initFactory(); + $factory = $this->createFactory(); - $dsn = 'sendinblue://apiKey@default?sender=0611223344'; - $transport = $factory->create(Dsn::fromString($dsn)); - $transport->setHost('host.test'); + $transport = $factory->create(Dsn::fromString('sendinblue://apiKey@host.test?sender=0611223344')); $this->assertSame('sendinblue://host.test?sender=0611223344', (string) $transport); } - public function testCreateWithNoPhoneThrowsMalformed() + public function testCreateWithMissingOptionSenderThrowsIncompleteDsnException() { - $factory = $this->initFactory(); + $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); - $dsnIncomplete = 'sendinblue://apiKey@default'; - $factory->create(Dsn::fromString($dsnIncomplete)); + $factory->create(Dsn::fromString('sendinblue://apiKey@host.test')); } - public function testSupportsSendinblueScheme() + public function testCreateWithNoApiKeyThrowsIncompleteDsnException() { - $factory = $this->initFactory(); + $factory = $this->createFactory(); - $dsn = 'sendinblue://apiKey@default?sender=0611223344'; - $dsnUnsupported = 'foobarmobile://apiKey@default?sender=0611223344'; + $this->expectException(IncompleteDsnException::class); - $this->assertTrue($factory->supports(Dsn::fromString($dsn))); - $this->assertFalse($factory->supports(Dsn::fromString($dsnUnsupported))); + $factory->create(Dsn::fromString('sendinblue://default?sender=0611223344')); } - private function initFactory(): SendinblueTransportFactory + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertTrue($factory->supports(Dsn::fromString('sendinblue://apiKey@default?sender=0611223344'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://apiKey@default?sender=0611223344'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + $factory->create(Dsn::fromString('somethingElse://apiKey@default?sender=0611223344')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "from" option + $factory->create(Dsn::fromString('somethingElse://apiKey@host')); + } + + private function createFactory(): SendinblueTransportFactory { return new SendinblueTransportFactory(); } diff --git a/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportTest.php index 4528f64ed0..438ba57960 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Sendinblue/Tests/SendinblueTransportTest.php @@ -25,22 +25,22 @@ final class SendinblueTransportTest extends TestCase { public function testToStringContainsProperties() { - $transport = $this->initTransport(); + $transport = $this->createTransport(); $this->assertSame('sendinblue://host.test?sender=0611223344', (string) $transport); } public function testSupportsMessageInterface() { - $transport = $this->initTransport(); + $transport = $this->createTransport(); $this->assertTrue($transport->supports(new SmsMessage('0611223344', 'Hello!'))); $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { - $transport = $this->initTransport(); + $transport = $this->createTransport(); $this->expectException(LogicException::class); $transport->send($this->createMock(MessageInterface::class)); @@ -60,17 +60,15 @@ final class SendinblueTransportTest extends TestCase return $response; }); - $transport = $this->initTransport($client); + $transport = $this->createTransport($client); $this->expectException(TransportException::class); $this->expectExceptionMessage('Unable to send the SMS: bad request'); $transport->send(new SmsMessage('phone', 'testMessage')); } - private function initTransport(?HttpClientInterface $client = null): SendinblueTransport + private function createTransport(?HttpClientInterface $client = null): SendinblueTransport { - return (new SendinblueTransport( - 'api-key', '0611223344', $client ?: $this->createMock(HttpClientInterface::class) - ))->setHost('host.test'); + return (new SendinblueTransport('api-key', '0611223344', $client ?: $this->createMock(HttpClientInterface::class)))->setHost('host.test'); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Sinch/SinchTransport.php b/src/Symfony/Component/Notifier/Bridge/Sinch/SinchTransport.php index 0810aae702..10f272db40 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sinch/SinchTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Sinch/SinchTransport.php @@ -76,9 +76,9 @@ final class SinchTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Sinch/Tests/SinchTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Sinch/Tests/SinchTransportTest.php index 7f7ea4208c..1332e59e95 100644 --- a/src/Symfony/Component/Notifier/Bridge/Sinch/Tests/SinchTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Sinch/Tests/SinchTransportTest.php @@ -35,7 +35,7 @@ final class SinchTransportTest extends TestCase $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { $transport = $this->createTransport(); diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/README.md b/src/Symfony/Component/Notifier/Bridge/Slack/README.md index 4eae10826d..518c843ca2 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Slack/README.md @@ -7,28 +7,24 @@ DSN example ----------- ``` -SLACK_DSN=slack://default/ID -``` - -where: -- `ID` is your webhook id (e.g. `/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX`) - -in this case: -``` -SLACK_DSN=slack://default/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX -``` - -DSN example ------------ - -``` -// .env file SLACK_DSN=slack://TOKEN@default?channel=CHANNEL ``` where: -- `TOKEN` is your Bot User OAuth Access Token -- `CHANNEL` is a Channel, private group, or IM channel to send message to. Can be an encoded ID, or a name +- `TOKEN` is your Bot User OAuth Access Token (they begin with `xoxb-`) +- `CHANNEL` is a channel, private group, or IM channel to send message to, it can be an encoded ID, or a name. + +valid DSN's are: +``` +SLACK_DSN=slack://xoxb-......@default?channel=my-channel-name +SLACK_DSN=slack://xoxb-......@default?channel=@fabien +``` + +invalid DSN's are: +``` +SLACK_DSN=slack://xoxb-......@default?channel=#my-channel-name +SLACK_DSN=slack://xoxb-......@default?channel=fabien +``` Resources --------- diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php b/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php index f4e144b9ac..6cdfefaddc 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/SlackTransport.php @@ -59,6 +59,7 @@ final class SlackTransport extends AbstractTransport if (!$message instanceof ChatMessage) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" (instance of "%s" given).', __CLASS__, ChatMessage::class, get_debug_type($message))); } + if ($message->getOptions() && !$message->getOptions() instanceof SlackOptions) { throw new LogicException(sprintf('The "%s" transport only supports instances of "%s" for options.', __CLASS__, SlackOptions::class)); } diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackSectionBlockTest.php b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackSectionBlockTest.php index 7e1e98981d..0a1c097bcf 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackSectionBlockTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/Block/SlackSectionBlockTest.php @@ -17,7 +17,7 @@ use Symfony\Component\Notifier\Bridge\Slack\Block\SlackSectionBlock; final class SlackSectionBlockTest extends TestCase { - public function testCanBeInstantiated(): void + public function testCanBeInstantiated() { $section = new SlackSectionBlock(); $section->text('section text'); @@ -44,7 +44,7 @@ final class SlackSectionBlockTest extends TestCase ], $section->toArray()); } - public function testThrowsWhenFieldsLimitReached(): void + public function testThrowsWhenFieldsLimitReached() { $section = new SlackSectionBlock(); for ($i = 0; $i < 10; ++$i) { diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportFactoryTest.php index 9c01d5b386..8965657f28 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportFactoryTest.php @@ -22,44 +22,55 @@ final class SlackTransportFactoryTest extends TestCase { public function testCreateWithDsn() { - $factory = new SlackTransportFactory(); + $factory = $this->createFactory(); - $transport = $factory->create(Dsn::fromString('slack://testUser@testHost/?channel=testChannel')); + $transport = $factory->create(Dsn::fromString('slack://testUser@host.test/?channel=testChannel')); - $this->assertSame('slack://testHost?channel=testChannel', (string) $transport); + $this->assertSame('slack://host.test?channel=testChannel', (string) $transport); } public function testCreateWithDeprecatedDsn() { + $factory = $this->createFactory(); + $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('Support for Slack webhook DSN has been dropped since 5.2 (maybe you haven\'t updated the DSN when upgrading from 5.1).'); - $factory = new SlackTransportFactory(); $factory->create(Dsn::fromString('slack://default/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX')); } - public function testCreateWithNoTokenThrowsMalformed() + public function testCreateWithNoTokenThrowsInclompleteDsnException() { - $factory = new SlackTransportFactory(); + $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); - $factory->create(Dsn::fromString(sprintf('slack://%s/?channel=%s', 'testHost', 'testChannel'))); + $factory->create(Dsn::fromString('slack://host.test?channel=testChannel')); } - public function testSupportsScheme() + public function testSupportsReturnsTrueWithSupportedScheme() { - $factory = new SlackTransportFactory(); + $factory = $this->createFactory(); - $this->assertTrue($factory->supports(Dsn::fromString('slack://host/?channel=testChannel'))); - $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host/?channel=testChannel'))); + $this->assertTrue($factory->supports(Dsn::fromString('slack://host?channel=testChannel'))); } - public function testNonSlackSchemeThrows() + public function testSupportsReturnsFalseWithUnsupportedScheme() { - $factory = new SlackTransportFactory(); + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host?channel=testChannel'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); $this->expectException(UnsupportedSchemeException::class); + $factory->create(Dsn::fromString('somethingElse://host?channel=testChannel')); + } - $factory->create(Dsn::fromString('somethingElse://user:pwd@host/?channel=testChannel')); + private function createFactory(): SlackTransportFactory + { + return new SlackTransportFactory(); } } diff --git a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php index 8f54c3884c..defcd0d535 100644 --- a/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Slack/Tests/SlackTransportTest.php @@ -31,9 +31,9 @@ final class SlackTransportTest extends TestCase $channel = 'test Channel'; // invalid channel name to test url encoding of the channel $transport = new SlackTransport('testToken', $channel, $this->createMock(HttpClientInterface::class)); - $transport->setHost('testHost'); + $transport->setHost('host.test'); - $this->assertSame('slack://testHost?channel=test+Channel', (string) $transport); + $this->assertSame('slack://host.test?channel=test+Channel', (string) $transport); } public function testSupportsChatMessage() @@ -46,10 +46,10 @@ final class SlackTransportTest extends TestCase public function testSendNonChatMessageThrowsLogicException() { - $this->expectException(LogicException::class); - $transport = new SlackTransport('testToken', 'testChannel', $this->createMock(HttpClientInterface::class)); + $this->expectException(LogicException::class); + $transport->send($this->createMock(MessageInterface::class)); } diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/README.md b/src/Symfony/Component/Notifier/Bridge/Smsapi/README.md index 804ad6c399..3491517eb6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/README.md @@ -1,19 +1,18 @@ SMSAPI Notifier =============== -Provides Smsapi integration for Symfony Notifier. +Provides [Smsapi](https://ssl.smsapi.pl) integration for Symfony Notifier. DSN example ----------- ``` -// .env file SMSAPI_DSN=smsapi://TOKEN@default?from=FROM ``` where: - - `TOKEN` is API Token (OAuth) - - `FROM` is sender name + - `TOKEN` is your API Token (OAuth) + - `FROM` is the sender name See your account info at https://ssl.smsapi.pl/ diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php index 83c3b1aaab..bab366232b 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransport.php @@ -22,6 +22,7 @@ use Symfony\Contracts\HttpClient\HttpClientInterface; /** * @author Marcin Szepczynski + * * @experimental in 5.2 */ final class SmsapiTransport extends AbstractTransport diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransportFactory.php index a573dbbb13..cbc3181868 100644 --- a/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/SmsapiTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Notifier\Bridge\Smsapi; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; use Symfony\Component\Notifier\Transport\AbstractTransportFactory; use Symfony\Component\Notifier\Transport\Dsn; @@ -18,6 +19,7 @@ use Symfony\Component\Notifier\Transport\TransportInterface; /** * @author Marcin Szepczynski + * * @experimental in 5.2 */ class SmsapiTransportFactory extends AbstractTransportFactory @@ -28,16 +30,22 @@ class SmsapiTransportFactory extends AbstractTransportFactory public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); - $authToken = $dsn->getUser(); - $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); - $from = $dsn->getOption('from'); - $port = $dsn->getPort(); - if ('smsapi' === $scheme) { - return (new SmsapiTransport($authToken, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + if ('smsapi' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'smsapi', $this->getSupportedSchemes()); } - throw new UnsupportedSchemeException($dsn, 'smsapi', $this->getSupportedSchemes()); + $authToken = $this->getUser($dsn); + $from = $dsn->getOption('from'); + + if (!$from) { + throw new IncompleteDsnException('Missing from.', $dsn->getOriginalDsn()); + } + + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new SmsapiTransport($authToken, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } protected function getSupportedSchemes(): array diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportFactoryTest.php new file mode 100644 index 0000000000..86c8ee63a5 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportFactoryTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Smsapi\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\Dsn; + +final class SmsapiTransportFactoryTest extends TestCase +{ + public function testCreateWithDsn() + { + $factory = $this->createFactory(); + + $transport = $factory->create(Dsn::fromString('smsapi://token@host.test?from=testFrom')); + + $this->assertSame('smsapi://host.test?from=testFrom', (string) $transport); + } + + public function testCreateWithMissingOptionFromThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('smsapi://token@host')); + } + + public function testCreateWithNoTokenThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('smsapi://host.test?from=testFrom')); + } + + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertTrue($factory->supports(Dsn::fromString('smsapi://host?from=testFrom'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host?from=testFrom'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + $factory->create(Dsn::fromString('somethingElse://token@host?from=testFrom')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "from" option + $factory->create(Dsn::fromString('somethingElse://token@host')); + } + + private function createFactory(): SmsapiTransportFactory + { + return new SmsapiTransportFactory(); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportTest.php new file mode 100644 index 0000000000..1aed295e36 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/Tests/SmsapiTransportTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Smsapi\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransport; +use Symfony\Component\Notifier\Exception\LogicException; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +final class SmsapiTransportTest extends TestCase +{ + public function testToStringContainsProperties() + { + $transport = $this->createTransport(); + + $this->assertSame('smsapi://test.host?from=testFrom', (string) $transport); + } + + public function testSupportsMessageInterface() + { + $transport = $this->createTransport(); + + $this->assertTrue($transport->supports(new SmsMessage('0611223344', 'Hello!'))); + $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); + } + + public function testSendNonChatMessageThrows() + { + $transport = $this->createTransport(); + + $this->expectException(LogicException::class); + $transport->send($this->createMock(MessageInterface::class)); + } + + private function createTransport(): SmsapiTransport + { + return (new SmsapiTransport('testToken', 'testFrom', $this->createMock(HttpClientInterface::class)))->setHost('test.host'); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Smsapi/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Smsapi/phpunit.xml.dist new file mode 100644 index 0000000000..d6c9a4d787 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Smsapi/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php b/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php index c5f8524c84..f84000021e 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/TelegramTransport.php @@ -21,10 +21,9 @@ use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** - * TelegramTransport. - * * To get the chat id, send a message in Telegram with the user you want - * and then curl 'https://api.telegram.org/bot%token%/getUpdates' | json_pp + * and then execute curl 'https://api.telegram.org/bot%token%/getUpdates' | json_pp + * command. * * @author Fabien Potencier * @@ -96,9 +95,9 @@ final class TelegramTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['result']['message_id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['result']['message_id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Telegram/Tests/TelegramTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Telegram/Tests/TelegramTransportFactoryTest.php index 8d43b1619e..e8ecfb6cb5 100644 --- a/src/Symfony/Component/Notifier/Bridge/Telegram/Tests/TelegramTransportFactoryTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Telegram/Tests/TelegramTransportFactoryTest.php @@ -33,6 +33,7 @@ final class TelegramTransportFactoryTest extends TestCase $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('telegram://simpleToken@host.test?channel=testChannel')); } @@ -41,6 +42,7 @@ final class TelegramTransportFactoryTest extends TestCase $factory = $this->createFactory(); $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('telegram://host.test?channel=testChannel')); } diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/Tests/TwilioTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Twilio/Tests/TwilioTransportTest.php index 5520735eca..f2c4eac8a7 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/Tests/TwilioTransportTest.php +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/Tests/TwilioTransportTest.php @@ -35,7 +35,7 @@ final class TwilioTransportTest extends TestCase $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); } - public function testSendNonSmsMessageThrowsException() + public function testSendNonSmsMessageThrowsLogicException() { $transport = $this->createTransport(); diff --git a/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php b/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php index 1c38a8c447..3f7e73d136 100644 --- a/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Twilio/TwilioTransport.php @@ -76,9 +76,9 @@ final class TwilioTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['sid']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['sid']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/README.md b/src/Symfony/Component/Notifier/Bridge/Zulip/README.md index 1c5052fee9..a81a4154a5 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/README.md +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/README.md @@ -1,20 +1,19 @@ Zulip Notifier ============== -Provides Zulip integration for Symfony Notifier. +Provides [Zulip](https://zulip.comw) integration for Symfony Notifier. DSN example ----------- ``` -// .env file -ZULIP_DSN=zulip://EMAIL:TOKEN@default?channel=Channel +ZULIP_DSN=zulip://EMAIL:TOKEN@default?channel=CHANNEL ``` where: - `EMAIL` is your Zulip email - `TOKEN` is your Zulip token - - `Channel` is the channel + - `CHANNEL` is the channel Resources --------- diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportFactoryTest.php new file mode 100644 index 0000000000..d60c7ad80f --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportFactoryTest.php @@ -0,0 +1,84 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Zulip\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransportFactory; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\Dsn; + +final class ZulipTransportFactoryTest extends TestCase +{ + public function testCreateWithDsn() + { + $factory = $this->createFactory(); + + $transport = $factory->create(Dsn::fromString('zulip://email:token@host.test?channel=testChannel')); + + $this->assertSame('zulip://host.test?channel=testChannel', (string) $transport); + } + + public function testCreateWithMissingOptionChannelThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('zulip://email:token@host')); + } + + public function testCreateWithOnlyEmailOrTokenThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + $factory->create(Dsn::fromString('zulip://testOneOfEmailOrToken@host.test?channel=testChannel')); + } + + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertTrue($factory->supports(Dsn::fromString('zulip://host?channel=testChannel'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://host?channel=testChannel'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + $factory->create(Dsn::fromString('somethingElse://email:token@host?channel=testChannel')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "channel" option + $factory->create(Dsn::fromString('somethingElse://email:token@host')); + } + + private function createFactory(): ZulipTransportFactory + { + return new ZulipTransportFactory(); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportTest.php new file mode 100644 index 0000000000..1af5b06eca --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/Tests/ZulipTransportTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Zulip\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransport; +use Symfony\Component\Notifier\Exception\LogicException; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +final class ZulipTransportTest extends TestCase +{ + public function testToStringContainsProperties() + { + $transport = $this->createTransport(); + + $this->assertSame('zulip://test.host?channel=testChannel', (string) $transport); + } + + public function testSupportsChatMessage() + { + $transport = $this->createTransport(); + + $this->assertTrue($transport->supports(new ChatMessage('testChatMessage'))); + $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); + } + + public function testSendNonChatMessageThrows() + { + $transport = $this->createTransport(); + + $this->expectException(LogicException::class); + $transport->send($this->createMock(MessageInterface::class)); + } + + private function createTransport(): ZulipTransport + { + return (new ZulipTransport('testEmail', 'testToken', 'testChannel', $this->createMock(HttpClientInterface::class)))->setHost('test.host'); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransport.php b/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransport.php index b12581abe7..297676202c 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransport.php +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransport.php @@ -93,9 +93,9 @@ class ZulipTransport extends AbstractTransport $success = $response->toArray(false); - $message = new SentMessage($message, (string) $this); - $message->setMessageId($success['id']); + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['id']); - return $message; + return $sentMessage; } } diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransportFactory.php index a624d3f2f7..3d2c870cf6 100644 --- a/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransportFactory.php +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/ZulipTransportFactory.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Notifier\Bridge\Zulip; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; use Symfony\Component\Notifier\Transport\AbstractTransportFactory; use Symfony\Component\Notifier\Transport\Dsn; @@ -24,27 +25,30 @@ use Symfony\Component\Notifier\Transport\TransportInterface; class ZulipTransportFactory extends AbstractTransportFactory { /** - * {@inheritdoc} + * @return ZulipTransport */ public function create(Dsn $dsn): TransportInterface { $scheme = $dsn->getScheme(); + + if ('zulip' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'zulip', $this->getSupportedSchemes()); + } + $email = $this->getUser($dsn); $token = $this->getPassword($dsn); $channel = $dsn->getOption('channel'); + + if (!$channel) { + throw new IncompleteDsnException('Missing channel.', $dsn->getOriginalDsn()); + } + $host = $dsn->getHost(); $port = $dsn->getPort(); - if ('zulip' === $scheme) { - return (new ZulipTransport($email, $token, $channel, $this->client, $this->dispatcher))->setHost($host)->setPort($port); - } - - throw new UnsupportedSchemeException($dsn, 'zulip', $this->getSupportedSchemes()); + return (new ZulipTransport($email, $token, $channel, $this->client, $this->dispatcher))->setHost($host)->setPort($port); } - /** - * {@inheritdoc} - */ protected function getSupportedSchemes(): array { return ['zulip']; diff --git a/src/Symfony/Component/Notifier/Bridge/Zulip/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Zulip/phpunit.xml.dist new file mode 100644 index 0000000000..88bfdb7020 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Zulip/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + +