From 79572b27283211c6de486c79d4195309e31f61eb Mon Sep 17 00:00:00 2001 From: Vasilij Dusko Date: Sun, 11 Apr 2021 09:04:41 +0300 Subject: [PATCH] [Notifier] Add SmsBiuras notifier bridge --- .../Resources/config/notifier_transports.php | 5 + .../Notifier/Bridge/SmsBiuras/.gitattributes | 4 + .../Notifier/Bridge/SmsBiuras/.gitignore | 3 + .../Notifier/Bridge/SmsBiuras/CHANGELOG.md | 7 ++ .../Notifier/Bridge/SmsBiuras/LICENSE | 19 +++ .../Notifier/Bridge/SmsBiuras/README.md | 26 ++++ .../Bridge/SmsBiuras/SmsBiurasTransport.php | 117 ++++++++++++++++++ .../SmsBiuras/SmsBiurasTransportFactory.php | 49 ++++++++ .../Tests/SmsBiurasTransportFactoryTest.php | 57 +++++++++ .../Tests/SmsBiurasTransportTest.php | 47 +++++++ .../Notifier/Bridge/SmsBiuras/composer.json | 30 +++++ .../Bridge/SmsBiuras/phpunit.xml.dist | 31 +++++ .../Exception/UnsupportedSchemeException.php | 4 + src/Symfony/Component/Notifier/Transport.php | 2 + 14 files changed, 401 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitattributes create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitignore create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/SmsBiuras/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index ca2da3d018..5cd7e232e1 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -38,6 +38,7 @@ use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory; use Symfony\Component\Notifier\Bridge\Sinch\SinchTransportFactory; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory; +use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransportFactory; use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransportFactory; use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory; use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory; @@ -179,5 +180,9 @@ return static function (ContainerConfigurator $container) { ->set('notifier.transport_factory.lightsms', LightSmsTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + + ->set('notifier.transport_factory.smsbiuras', SmsBiurasTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') ; }; diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitattributes b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitattributes new file mode 100644 index 0000000000..84c7add058 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/.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/SmsBiuras/.gitignore b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitignore new file mode 100644 index 0000000000..c49a5d8df5 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/.gitignore @@ -0,0 +1,3 @@ +vendor/ +composer.lock +phpunit.xml diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/CHANGELOG.md new file mode 100644 index 0000000000..1f2b652ac2 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.3 +--- + + * Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE new file mode 100644 index 0000000000..efb17f98e7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021 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/SmsBiuras/README.md b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/README.md new file mode 100644 index 0000000000..722db8fd66 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/README.md @@ -0,0 +1,26 @@ +SmsBiuras Notifier +================== + +Provides [SmsBiuras](https://www.smsbiuras.lt) integration for Symfony Notifier. + +DSN example +----------- + +``` +SMSBIURAS_DSN=smsbiuras://UID:API_KEY@default?from=FROM&test_mode=0 +``` + +where: +- `UID` is your client code +- `API_KEY` is your SmsBiuras api key +- `FROM` is your sender +- `TEST_MODE` the test parameter is used during system connection testing. + Possible values: 0 (real SMS sent), 1 (test SMS, will not be delivered to the phone and will not be charged) + +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/SmsBiuras/SmsBiurasTransport.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php new file mode 100644 index 0000000000..a223d6dc61 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransport.php @@ -0,0 +1,117 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\SmsBiuras; + +use Symfony\Component\HttpFoundation\Response; +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 Vasilij Duško + */ +final class SmsBiurasTransport extends AbstractTransport +{ + protected const HOST = 'savitarna.smsbiuras.lt'; + + private $uid; + private $apiKey; + private $from; + private $testMode; + + private const ERROR_CODES = [ + 1 => 'The message was processed and sent to the mobile operator. But delivery confirmations have not yet been returned.', + 2 => 'SMS not delivered.', + 3 => 'The SMS message was successfully delivered to the recipient.', + 4 => 'The message was sent and expired because it could not be delivered to the recipient during its validity period (48 hours according to our default platform).', + 5 => 'The message was received but the operator returned "Rejected" as the final status.', + 6 => 'Missing parameters, check that you are using all required parameters.', + 7 => ' Wrong apikey or uid.', + 8 => 'Sender ID - "from". Must be approved by an administrator.', + 9 => 'Balance insufficient, please top up the account.', + 10 => 'Bad date format for schedule parameter. Ex: urlencode("2021-03-11 12:00").', + 999 => 'Unknown Error', + ]; + + public function __construct(string $uid, string $apiKey, string $from, bool $testMode, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) + { + $this->uid = $uid; + $this->apiKey = $apiKey; + $this->from = $from; + $this->testMode = $testMode; + + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + if ($this->testMode) { + return sprintf('smsbiuras://%s?from=%s&test_mode=%s', $this->getEndpoint(), $this->from, $this->testMode); + } + + return sprintf('smsbiuras://%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); + } + + $endpoint = sprintf('https://%s/api?', $this->getEndpoint()); + + $response = $this->client->request('GET', $endpoint, [ + 'query' => [ + 'uid' => $this->uid, + 'apikey' => $this->apiKey, + 'message' => $message->getSubject(), + 'from' => $this->from, + 'test' => $this->testMode ? 0 : 1, + 'to' => $message->getPhone(), + ], + ]); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + throw new TransportException('Unable to send the SMS.', $response); + } + + $matches = []; + if (preg_match('/^ERROR: (\d+)$/', $response->getContent(), $matches)) { + throw new TransportException('Unable to send the SMS: '.$this->getErrorMsg($matches[1] ?? 999), $response); + } + + $matches = []; + if (preg_match('/^OK: (\d+)$/', $response->getContent(), $matches)) { + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($matches[1] ?? 0); + + return $sentMessage; + } + + throw new TransportException('Unable to send the SMS.', $response); + } + + private function getErrorMsg(int $errorCode): string + { + return self::ERROR_CODES[$errorCode] ?? self::ERROR_CODES[999]; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransportFactory.php new file mode 100644 index 0000000000..e83fae3cf4 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/SmsBiurasTransportFactory.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\SmsBiuras; + +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 Vasilij Duško + */ +final class SmsBiurasTransportFactory extends AbstractTransportFactory +{ + /** + * @return SmsBiurasTransport + */ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + + if ('smsbiuras' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'smsbiuras', $this->getSupportedSchemes()); + } + + $uid = $this->getUser($dsn); + $apiKey = $this->getPassword($dsn); + $from = $dsn->getRequiredOption('from'); + $testMode = filter_var($dsn->getOption('test_mode', false), \FILTER_VALIDATE_BOOLEAN); + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new SmsBiurasTransport($uid, $apiKey, $from, $testMode, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return ['smsbiuras']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportFactoryTest.php new file mode 100644 index 0000000000..abb1dd3eb0 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportFactoryTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\SmsBiuras\Tests; + +use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransportFactory; +use Symfony\Component\Notifier\Test\TransportFactoryTestCase; +use Symfony\Component\Notifier\Transport\TransportFactoryInterface; + +final class SmsBiurasTransportFactoryTest extends TransportFactoryTestCase +{ + /** + * @return SmsBiurasTransportFactory + */ + public function createFactory(): TransportFactoryInterface + { + return new SmsBiurasTransportFactory(); + } + + public function createProvider(): iterable + { + yield [ + 'smsbiuras://host.test?from=0611223344', + 'smsbiuras://uid:api_key@host.test?from=0611223344&test_mode=0', + ]; + + yield [ + 'smsbiuras://host.test?from=0611223344&test_mode=1', + 'smsbiuras://uid:api_key@host.test?from=0611223344&test_mode=1', + ]; + } + + public function supportsProvider(): iterable + { + yield [true, 'smsbiuras://uid:api_key@default?from=0611223344']; + yield [false, 'somethingElse://uid:api_key@default?from=0611223344']; + } + + public function missingRequiredOptionProvider(): iterable + { + yield 'missing option: from' => ['smsbiuras://uid:api_key@default']; + } + + public function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://uid:api_key@default?from=0611223344']; + yield ['somethingElse://uid:api_key@default']; // missing "from" option + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php new file mode 100644 index 0000000000..28c2ace272 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/Tests/SmsBiurasTransportTest.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\SmsBiuras\Tests; + +use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransport; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Test\TransportTestCase; +use Symfony\Component\Notifier\Transport\TransportInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +final class SmsBiurasTransportTest extends TransportTestCase +{ + /** + * @return SmsBiurasTransport + */ + public function createTransport(?HttpClientInterface $client = null): TransportInterface + { + return new SmsBiurasTransport('uid', 'api_key', 'from', true, $client ?? $this->createMock(HttpClientInterface::class)); + } + + public function toStringProvider(): iterable + { + yield ['smsbiuras://savitarna.smsbiuras.lt?from=from&test_mode=1', $this->createTransport()]; + } + + public function supportedMessagesProvider(): iterable + { + yield [new SmsMessage('0611223344', 'Hello!')]; + } + + public function unsupportedMessagesProvider(): iterable + { + yield [new ChatMessage('Hello!')]; + yield [$this->createMock(MessageInterface::class)]; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json new file mode 100644 index 0000000000..3a6da1fa23 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/sms-biuras-notifier", + "type": "symfony-bridge", + "description": "Symfony SmsBiuras Notifier Bridge", + "keywords": ["sms", "smsbiuras", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Vasilij Duško", + "email": "vasilij@prado.lt" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/http-client": "^4.4|^5.2", + "symfony/notifier": "^5.3" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SmsBiuras\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/SmsBiuras/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/phpunit.xml.dist new file mode 100644 index 0000000000..32385feb75 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/SmsBiuras/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 13ac1df342..923f210371 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -132,6 +132,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory::class, 'package' => 'symfony/microsoft-teams-notifier', ], + 'smsbiuras' => [ + 'class' => Bridge\SmsBiuras\SmsBiurasTransportFactory::class, + 'package' => 'symfony/sms-biuras-notifier', + ], ]; /** diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index 4217ffe076..3a4df9d0e5 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -33,6 +33,7 @@ use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory; use Symfony\Component\Notifier\Bridge\Sinch\SinchTransportFactory; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory; +use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransportFactory; use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory; use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory; use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransportFactory; @@ -78,6 +79,7 @@ class Transport ClickatellTransportFactory::class, LightSmsTransportFactory::class, MicrosoftTeamsTransport::class, + SmsBiurasTransportFactory::class, ]; private $factories;