From bf94bcb1f651f25af25a4ed5d64251c8e5a6e2aa Mon Sep 17 00:00:00 2001 From: Oleksandr Barabolia Date: Mon, 16 Nov 2020 18:55:32 +0200 Subject: [PATCH] [Notifier] add iqsms bridge --- .../FrameworkExtension.php | 2 + .../Resources/config/notifier_transports.php | 5 ++ .../Notifier/Bridge/Iqsms/.gitattributes | 4 + .../Notifier/Bridge/Iqsms/CHANGELOG.md | 7 ++ .../Notifier/Bridge/Iqsms/IqsmsTransport.php | 88 +++++++++++++++++++ .../Bridge/Iqsms/IqsmsTransportFactory.php | 56 ++++++++++++ .../Component/Notifier/Bridge/Iqsms/LICENSE | 19 ++++ .../Component/Notifier/Bridge/Iqsms/README.md | 24 +++++ .../Iqsms/Tests/IqsmsTransportFactoryTest.php | 76 ++++++++++++++++ .../Bridge/Iqsms/Tests/IqsmsTransportTest.php | 51 +++++++++++ .../Notifier/Bridge/Iqsms/composer.json | 34 +++++++ .../Notifier/Bridge/Iqsms/phpunit.xml.dist | 31 +++++++ .../Exception/UnsupportedSchemeException.php | 4 + src/Symfony/Component/Notifier/Transport.php | 2 + 14 files changed, 403 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/.gitattributes create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/Iqsms/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 8c91a05462e7b..3afcf17d8cc68 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -107,6 +107,7 @@ use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GoogleChat\GoogleChatTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; +use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; use Symfony\Component\Notifier\Bridge\LinkedIn\LinkedInTransportFactory; use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; @@ -2220,6 +2221,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ MattermostTransportFactory::class => 'notifier.transport_factory.mattermost', GoogleChatTransportFactory::class => 'notifier.transport_factory.googlechat', NexmoTransportFactory::class => 'notifier.transport_factory.nexmo', + IqsmsTransportFactory::class => 'notifier.transport_factory.iqsms', RocketChatTransportFactory::class => 'notifier.transport_factory.rocketchat', InfobipTransportFactory::class => 'notifier.transport_factory.infobip', TwilioTransportFactory::class => 'notifier.transport_factory.twilio', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index be40329cc9ebc..88952b80aeb99 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -17,6 +17,7 @@ use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GoogleChat\GoogleChatTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; +use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; use Symfony\Component\Notifier\Bridge\LinkedIn\LinkedInTransportFactory; use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; @@ -111,6 +112,10 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + ->set('notifier.transport_factory.iqsms', IqsmsTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') + ->set('notifier.transport_factory.discord', DiscordTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/.gitattributes b/src/Symfony/Component/Notifier/Bridge/Iqsms/.gitattributes new file mode 100644 index 0000000000000..84c7add058fb5 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/.gitattributes @@ -0,0 +1,4 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Iqsms/CHANGELOG.md new file mode 100644 index 0000000000000..ee65aa9b4a466 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.3.0 +----- + + * Added the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php new file mode 100644 index 0000000000000..6e94473fe39eb --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransport.php @@ -0,0 +1,88 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Iqsms; + +use Symfony\Component\Notifier\Exception\TransportException; +use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SentMessage; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Transport\AbstractTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * @author Oleksandr Barabolia + * + * @experimental in 5.3 + */ +final class IqsmsTransport extends AbstractTransport +{ + protected const HOST = 'api.iqsms.ru'; + + private $login; + private $password; + private $from; + + public function __construct(string $login, string $password, string $from, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) + { + $this->login = $login; + $this->password = $password; + $this->from = $from; + + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + return sprintf('iqsms://%s?from=%s', $this->getEndpoint(), $this->from); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof SmsMessage; + } + + protected function doSend(MessageInterface $message): SentMessage + { + if (!$message instanceof SmsMessage) { + throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message); + } + + $response = $this->client->request('POST', 'https://'.$this->getEndpoint().'/messages/v2/send.json', [ + 'json' => [ + 'messages' => [ + [ + 'phone' => $message->getPhone(), + 'text' => $message->getSubject(), + 'sender' => $this->from, + 'clientId' => uniqid(), + ], + ], + 'login' => $this->login, + 'password' => $this->password, + ], + ]); + + $result = $response->toArray(false); + foreach ($result['messages'] as $msg) { + if ('accepted' !== $msg['status']) { + throw new TransportException(sprintf('Unable to send the SMS: "%s".', $msg['status']), $response); + } + } + + $message = new SentMessage($message, (string) $this); + $message->setMessageId($result['messages'][0]['smscId']); + + return $message; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransportFactory.php new file mode 100644 index 0000000000000..8faafc8185228 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/IqsmsTransportFactory.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Iqsms; + +use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\AbstractTransportFactory; +use Symfony\Component\Notifier\Transport\Dsn; +use Symfony\Component\Notifier\Transport\TransportInterface; + +/** + * @author Oleksandr Barabolia + * + * @experimental in 5.3 + */ +final class IqsmsTransportFactory extends AbstractTransportFactory +{ + /** + * @return IqsmsTransport + */ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + + if ('iqsms' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'iqsms', $this->getSupportedSchemes()); + } + + $login = $this->getUser($dsn); + $password = $this->getPassword($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 IqsmsTransport($login, $password, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return ['iqsms']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE new file mode 100644 index 0000000000000..5593b1d84f74a --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md b/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md new file mode 100644 index 0000000000000..ac137c462840c --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/README.md @@ -0,0 +1,24 @@ +Iqsms Notifier +============== + +Provides [Iqsms[(https://iqsms.ru) integration for Symfony Notifier. + +DSN example +----------- + +``` +IQSMS_DSN=iqsms://LOGIN:PASSWORD@default?from=FROM +``` + +where: + - `LOGIN` is your IQSMS login + - `PASSWORD` is your IQSMS password + - `FROM` is the sender + +Resources +--------- + + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportFactoryTest.php new file mode 100644 index 0000000000000..cb4c079fd2c3e --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportFactoryTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Iqsms\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; +use Symfony\Component\Notifier\Exception\IncompleteDsnException; +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\Dsn; + +final class IqsmsTransportFactoryTest extends TestCase +{ + public function testCreateWithDsn() + { + $factory = $this->createFactory(); + + $transport = $factory->create(Dsn::fromString('iqsms://login:password@host.test?from=some')); + $this->assertSame('iqsms://host.test?from=some', (string) $transport); + } + + public function testCreateWithMissingOptionFromThrowsIncompleteDsnException() + { + $factory = $this->createFactory(); + + $this->expectException(IncompleteDsnException::class); + + $factory->create(Dsn::fromString('iqsms://login:password@default')); + } + + public function testSupportsReturnsTrueWithSupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertTrue($factory->supports(Dsn::fromString('iqsms://login:password@default?from=some'))); + } + + public function testSupportsReturnsFalseWithUnsupportedScheme() + { + $factory = $this->createFactory(); + + $this->assertFalse($factory->supports(Dsn::fromString('somethingElse://login:password@default?from=some'))); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeException() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + $factory->create(Dsn::fromString('somethingElse://login:password@default?from=some')); + } + + public function testUnsupportedSchemeThrowsUnsupportedSchemeExceptionEvenIfRequiredOptionIsMissing() + { + $factory = $this->createFactory(); + + $this->expectException(UnsupportedSchemeException::class); + + // unsupported scheme and missing "from" option + $factory->create(Dsn::fromString('somethingElse://login:password@default')); + } + + private function createFactory(): IqsmsTransportFactory + { + return new IqsmsTransportFactory(); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportTest.php new file mode 100644 index 0000000000000..1266d16a84e77 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/Tests/IqsmsTransportTest.php @@ -0,0 +1,51 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Iqsms\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransport; +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 IqsmsTransportTest extends TestCase +{ + public function testToStringContainsProperties() + { + $transport = $this->createTransport(); + + $this->assertSame('iqsms://host.test?from=sender', (string) $transport); + } + + public function testSupportsMessageInterface() + { + $transport = $this->createTransport(); + + $this->assertTrue($transport->supports(new SmsMessage('9031223344', 'Hello!'))); + $this->assertFalse($transport->supports($this->createMock(MessageInterface::class))); + } + + public function testSendNonSmsMessageThrowsException() + { + $transport = $this->createTransport(); + + $this->expectException(LogicException::class); + + $transport->send($this->createMock(MessageInterface::class)); + } + + private function createTransport(): IqsmsTransport + { + return (new IqsmsTransport('login', 'password', 'sender', $this->createMock(HttpClientInterface::class)))->setHost('host.test'); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json b/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json new file mode 100644 index 0000000000000..bcc6f74e43153 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/composer.json @@ -0,0 +1,34 @@ +{ + "name": "symfony/iqsms-notifier", + "type": "symfony-bridge", + "description": "Symfony Iqsms Notifier Bridge", + "keywords": ["sms", "iqsms", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Oleksandr Barabolia", + "email": "alexandrbarabolya@gmail.com" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/http-client": "^4.3|^5.0", + "symfony/notifier": "^5.3" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Iqsms\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/Iqsms/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Iqsms/phpunit.xml.dist new file mode 100644 index 0000000000000..d73fef8d446f6 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Iqsms/phpunit.xml.dist @@ -0,0 +1,31 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Resources + ./Tests + ./vendor + + + + diff --git a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php index 206711f0333c0..2ac36c47d19e9 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -42,6 +42,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\Nexmo\NexmoTransportFactory::class, 'package' => 'symfony/nexmo-notifier', ], + 'iqsms' => [ + 'class' => Bridge\Iqsms\IqsmsTransportFactory::class, + 'package' => 'symfony/iqsms-notifier', + ], 'rocketchat' => [ 'class' => Bridge\RocketChat\RocketChatTransportFactory::class, 'package' => 'symfony/rocket-chat-notifier', diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index a5d419367eb64..f370af913bd94 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -16,6 +16,7 @@ use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; +use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory; @@ -51,6 +52,7 @@ class Transport TelegramTransportFactory::class, MattermostTransportFactory::class, NexmoTransportFactory::class, + IqsmsTransportFactory::class, RocketChatTransportFactory::class, TwilioTransportFactory::class, InfobipTransportFactory::class,