From 93dde15dd2a82dc7142a0512aab5d77a9db04182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20MARTIN?= Date: Wed, 30 Dec 2020 11:50:55 +0100 Subject: [PATCH] Add Octopush notifier transport --- .../FrameworkExtension.php | 2 + .../Resources/config/notifier_transports.php | 5 ++ .../Notifier/Bridge/Octopush/.gitattributes | 4 + .../Notifier/Bridge/Octopush/CHANGELOG.md | 7 ++ .../Notifier/Bridge/Octopush/LICENSE | 19 ++++ .../Bridge/Octopush/OctopushTransport.php | 90 +++++++++++++++++++ .../Octopush/OctopushTransportFactory.php | 50 +++++++++++ .../Notifier/Bridge/Octopush/README.md | 24 +++++ .../Tests/OctopushTransportFactoryTest.php | 53 +++++++++++ .../Octopush/Tests/OctopushTransportTest.php | 47 ++++++++++ .../Notifier/Bridge/Octopush/composer.json | 30 +++++++ .../Notifier/Bridge/Octopush/phpunit.xml.dist | 31 +++++++ .../Exception/UnsupportedSchemeException.php | 4 + src/Symfony/Component/Notifier/Transport.php | 2 + 14 files changed, 368 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/.gitattributes create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/Octopush/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index f04591a3ccfb1..f391049f20fc5 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -113,6 +113,7 @@ use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory; +use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory as SendinblueNotifierTransportFactory; @@ -2238,6 +2239,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ DiscordTransportFactory::class => 'notifier.transport_factory.discord', LinkedInTransportFactory::class => 'notifier.transport_factory.linkedin', GatewayApiTransportFactory::class => 'notifier.transport_factory.gatewayapi', + OctopushTransportFactory::class => 'notifier.transport_factory.octopush', ]; foreach ($classToServices as $class => $service) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 74d356677c290..d8cb56862bfb7 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -23,6 +23,7 @@ use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory; +use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory; @@ -117,6 +118,10 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + ->set('notifier.transport_factory.octopush', OctopushTransportFactory::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/Octopush/.gitattributes b/src/Symfony/Component/Notifier/Bridge/Octopush/.gitattributes new file mode 100644 index 0000000000000..84c7add058fb5 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/.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/Octopush/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Octopush/CHANGELOG.md new file mode 100644 index 0000000000000..1f2b652ac20ea --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.3 +--- + + * Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE b/src/Symfony/Component/Notifier/Bridge/Octopush/LICENSE new file mode 100644 index 0000000000000..efb17f98e7dd3 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/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/Octopush/OctopushTransport.php b/src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransport.php new file mode 100644 index 0000000000000..8bba30f197a85 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransport.php @@ -0,0 +1,90 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Octopush; + +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 Aurélien Martin + */ +final class OctopushTransport extends AbstractTransport +{ + protected const HOST = 'www.octopush-dm.com'; + + private $userLogin; + private $apiKey; + private $from; + private $type; + + public function __construct(string $userLogin, string $apiKey, string $from, string $type, ?HttpClientInterface $client = null, ?EventDispatcherInterface $dispatcher = null) + { + $this->userLogin = $userLogin; + $this->apiKey = $apiKey; + $this->from = $from; + $this->type = $type; + + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + return sprintf('octopush://%s?from=%s&type=%s', $this->getEndpoint(), $this->from, $this->type); + } + + 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/sms/json', $this->getEndpoint()); + + $response = $this->client->request('POST', $endpoint, [ + 'headers' => [ + 'content_type' => 'multipart/form-data', + ], + 'body' => [ + 'user_login' => $this->userLogin, + 'api_key' => $this->apiKey, + 'sms_text' => $message->getSubject(), + 'sms_recipients' => $message->getPhone(), + 'sms_sender' => $this->from, + 'sms_type' => $this->type, + ], + ]); + + if (200 !== $response->getStatusCode()) { + $error = $response->toArray(false); + + throw new TransportException('Unable to send the SMS: '.$error['error_code'], $response); + } + + $success = $response->toArray(false); + + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($success['ticket']); + + return $sentMessage; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransportFactory.php new file mode 100644 index 0000000000000..0d93ef6909c2a --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/OctopushTransportFactory.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\Octopush; + +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 Aurélien Martin + */ +final class OctopushTransportFactory extends AbstractTransportFactory +{ + /** + * @return OctopushTransport + */ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + + if ('octopush' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'octopush', $this->getSupportedSchemes()); + } + + $userLogin = urlencode($this->getUser($dsn)); + $apiKey = $this->getPassword($dsn); + $from = $dsn->getRequiredOption('from'); + $type = $dsn->getRequiredOption('type'); + + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new OctopushTransport($userLogin, $apiKey, $from, $type, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return ['octopush']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/README.md b/src/Symfony/Component/Notifier/Bridge/Octopush/README.md new file mode 100644 index 0000000000000..e025aed7470c7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/README.md @@ -0,0 +1,24 @@ +Octopush Notifier +================= + +Provides [Octopush](https://www.octopush.com) integration for Symfony Notifier. + +DSN example +----------- + +``` +OCTOPUSH_DSN=octopush://USERLOGIN:APIKEY@default?from=FROM&type=TYPE +``` + +where: +- `USERLOGIN` is your Octopush email +- `APIKEY` is your Octopush token +- `FROM` is your sender +- `TYPE` is Octopush sms type (`XXX` = SMS LowCost; `FR` = SMS Premium; `WWW` = SMS World) +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/Octopush/Tests/OctopushTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportFactoryTest.php new file mode 100644 index 0000000000000..7e2034ed6a4a0 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportFactoryTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Octopush\Tests; + +use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory; +use Symfony\Component\Notifier\Tests\TransportFactoryTestCase; +use Symfony\Component\Notifier\Transport\TransportFactoryInterface; + +final class OctopushTransportFactoryTest extends TransportFactoryTestCase +{ + /** + * @return OctopushTransportFactory + */ + public function createFactory(): TransportFactoryInterface + { + return new OctopushTransportFactory(); + } + + public function createProvider(): iterable + { + yield [ + 'octopush://host.test?from=Heyliot&type=FR', + 'octopush://userLogin:apiKey@host.test?from=Heyliot&type=FR', + ]; + } + + public function supportsProvider(): iterable + { + yield [true, 'octopush://userLogin:apiKey@default?from=Heyliot&type=FR']; + yield [false, 'somethingElse://userLogin:apiKet@default?from=Heyliot&type=FR']; + } + + public function incompleteDsnProvider(): iterable + { + yield 'missing option: from' => ['octopush://userLogin:apiKey@default?type=FR']; + yield 'missing option: type' => ['octopush://userLogin:apiKey@default?from=Heyliot']; + } + + public function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://userLogin:apiKey@default?from=0611223344']; + yield ['somethingElse://userLogin:apiKey@default']; // missing "from" option + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportTest.php new file mode 100644 index 0000000000000..713eaeedb8278 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/Tests/OctopushTransportTest.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\Octopush\Tests; + +use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransport; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SmsMessage; +use Symfony\Component\Notifier\Tests\TransportTestCase; +use Symfony\Component\Notifier\Transport\TransportInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +final class OctopushTransportTest extends TransportTestCase +{ + /** + * @return OctopushTransport + */ + public function createTransport(?HttpClientInterface $client = null): TransportInterface + { + return new OctopushTransport('userLogin', 'apiKey', 'from', 'type', $client ?: $this->createMock(HttpClientInterface::class)); + } + + public function toStringProvider(): iterable + { + yield ['octopush://www.octopush-dm.com?from=from&type=type', $this->createTransport()]; + } + + public function supportedMessagesProvider(): iterable + { + yield [new SmsMessage('33611223344', 'Hello!')]; + } + + public function unsupportedMessagesProvider(): iterable + { + yield [new ChatMessage('Hello!')]; + yield [$this->createMock(MessageInterface::class)]; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json b/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json new file mode 100644 index 0000000000000..4bd901677399d --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/composer.json @@ -0,0 +1,30 @@ +{ + "name": "symfony/octopush-notifier", + "type": "symfony-bridge", + "description": "Symfony Octopush Notifier Bridge", + "keywords": ["sms", "octopush", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Aurélien Martin", + "email": "pro@aurelienmartin.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\\Octopush\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/Octopush/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Octopush/phpunit.xml.dist new file mode 100644 index 0000000000000..baaeecd31cfd8 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Octopush/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 65b08e64e4bb7..a6bfb64f729f7 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -92,6 +92,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\GatewayApi\GatewayApiTransportFactory::class, 'package' => 'symfony/gatewayapi-notifier', ], + 'octopush' => [ + 'class' => Bridge\Octopush\OctopushTransportFactory::class, + 'package' => 'symfony/octopush-notifier', + ], ]; /** diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index dd58cb70514c8..b3d8f5d610478 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -21,6 +21,7 @@ use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory; use Symfony\Component\Notifier\Bridge\Nexmo\NexmoTransportFactory; +use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory; use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory; use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory; use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory; @@ -66,6 +67,7 @@ class Transport SendinblueTransportFactory::class, DiscordTransportFactory::class, GatewayApiTransportFactory::class, + OctopushTransportFactory::class, ]; private $factories;