From d033677dfdb9cfa7656798c1bd01fe25749f0acb Mon Sep 17 00:00:00 2001 From: Christin Gruber Date: Thu, 14 Jan 2021 16:34:06 +0100 Subject: [PATCH] Add symfony/gitter-notifier bridge --- .../FrameworkExtension.php | 2 + .../Resources/config/notifier_transports.php | 5 ++ .../Notifier/Bridge/Gitter/.gitattributes | 4 + .../Notifier/Bridge/Gitter/CHANGELOG.md | 7 ++ .../Bridge/Gitter/GitterTransport.php | 81 +++++++++++++++++++ .../Bridge/Gitter/GitterTransportFactory.php | 44 ++++++++++ .../Component/Notifier/Bridge/Gitter/LICENSE | 19 +++++ .../Notifier/Bridge/Gitter/README.md | 23 ++++++ .../Tests/GitterTransportFactoryTest.php | 57 +++++++++++++ .../Gitter/Tests/GitterTransportTest.php | 47 +++++++++++ .../Notifier/Bridge/Gitter/composer.json | 31 +++++++ .../Notifier/Bridge/Gitter/phpunit.xml.dist | 31 +++++++ .../Exception/UnsupportedSchemeException.php | 4 + src/Symfony/Component/Notifier/Transport.php | 2 + 14 files changed, 357 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/.gitattributes create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/Gitter/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 4bf5c7153f..7da70be941 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -110,6 +110,7 @@ use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiTransportFactory; +use Symfony\Component\Notifier\Bridge\Gitter\GitterTransportFactory; use Symfony\Component\Notifier\Bridge\GoogleChat\GoogleChatTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; @@ -2247,6 +2248,7 @@ class FrameworkExtension extends Extension GatewayApiTransportFactory::class => 'notifier.transport_factory.gatewayapi', OctopushTransportFactory::class => 'notifier.transport_factory.octopush', MercureTransportFactory::class => 'notifier.transport_factory.mercure', + GitterTransportFactory::class => 'notifier.transport_factory.gitter', ]; 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 4053142a33..3d4fdcf101 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\Esendex\EsendexTransportFactory; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiTransportFactory; +use Symfony\Component\Notifier\Bridge\Gitter\GitterTransportFactory; use Symfony\Component\Notifier\Bridge\GoogleChat\GoogleChatTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; @@ -140,6 +141,10 @@ return static function (ContainerConfigurator $container) { ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') + ->set('notifier.transport_factory.gitter', GitterTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('chatter.transport_factory') + ->set('notifier.transport_factory.null', NullTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('chatter.transport_factory') diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/.gitattributes b/src/Symfony/Component/Notifier/Bridge/Gitter/.gitattributes new file mode 100644 index 0000000000..84c7add058 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/.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/Gitter/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/Gitter/CHANGELOG.md new file mode 100644 index 0000000000..1f2b652ac2 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.3 +--- + + * Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransport.php b/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransport.php new file mode 100644 index 0000000000..4b04fa4d26 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransport.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Gitter; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Notifier\Exception\TransportException; +use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException; +use Symfony\Component\Notifier\Message\ChatMessage; +use Symfony\Component\Notifier\Message\MessageInterface; +use Symfony\Component\Notifier\Message\SentMessage; +use Symfony\Component\Notifier\Transport\AbstractTransport; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\HttpClient\HttpClientInterface; + +/** + * @author Christin Gruber + */ +final class GitterTransport extends AbstractTransport +{ + protected const HOST = 'api.gitter.im'; + + private $token; + private $roomId; + + public function __construct(string $token, string $roomId, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) + { + $this->token = $token; + $this->roomId = $roomId; + + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + return sprintf('gitter://%s?room_id=%s', $this->getEndpoint(), $this->roomId); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof ChatMessage; + } + + /** + * @see https://developer.gitter.im/docs/rest-api + */ + protected function doSend(MessageInterface $message): SentMessage + { + if (!$message instanceof ChatMessage) { + throw new UnsupportedMessageTypeException(__CLASS__, ChatMessage::class, $message); + } + + $endpoint = sprintf('https://%s/v1/rooms/%s/chatMessages', $this->getEndpoint(), $this->roomId); + + $response = $this->client->request('POST', $endpoint, [ + 'auth_bearer' => $this->token, + 'json' => [ + 'text' => $message->getSubject(), + ], + ]); + + $result = $response->toArray(false); + + if (Response::HTTP_OK !== $response->getStatusCode()) { + throw new TransportException(sprintf('Unable to post the Gitter message: "%s".', $result['error']), $response); + } + + $sentMessage = new SentMessage($message, (string) $this); + $sentMessage->setMessageId($result['id']); + + return $sentMessage; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransportFactory.php new file mode 100644 index 0000000000..c86f29d6b9 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/GitterTransportFactory.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\Gitter; + +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 Christin Gruber + */ +final class GitterTransportFactory extends AbstractTransportFactory +{ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + + if ('gitter' !== $scheme) { + throw new UnsupportedSchemeException($dsn, 'gitter', $this->getSupportedSchemes()); + } + + $token = $this->getUser($dsn); + $roomId = $dsn->getRequiredOption('room_id'); + $host = 'default' === $dsn->getHost() ? null : $dsn->getHost(); + $port = $dsn->getPort(); + + return (new GitterTransport($token, $roomId, $this->client, $this->dispatcher))->setHost($host)->setPort($port); + } + + protected function getSupportedSchemes(): array + { + return ['gitter']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE b/src/Symfony/Component/Notifier/Bridge/Gitter/LICENSE new file mode 100644 index 0000000000..efb17f98e7 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/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/Gitter/README.md b/src/Symfony/Component/Notifier/Bridge/Gitter/README.md new file mode 100644 index 0000000000..bd318be6ed --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/README.md @@ -0,0 +1,23 @@ +Gitter Notifier +=============== + +Provides [Gitter](https://gitter.im) integration for Symfony Notifier. + +DSN example +----------- + +``` +GITTER_DSN=gitter://TOKEN@default?room_id=ROOM_ID +``` + +where: +- `TOKEN` is your Gitter token +- `ROOM_ID` is your Gitter room id + +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/Gitter/Tests/GitterTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportFactoryTest.php new file mode 100644 index 0000000000..24f1038ecf --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportFactoryTest.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\Gitter\Tests; + +use Symfony\Component\Notifier\Bridge\Gitter\GitterTransportFactory; +use Symfony\Component\Notifier\Tests\TransportFactoryTestCase; +use Symfony\Component\Notifier\Transport\TransportFactoryInterface; + +/** + * @author Christin Gruber + */ +final class GitterTransportFactoryTest extends TransportFactoryTestCase +{ + public function createFactory(): TransportFactoryInterface + { + return new GitterTransportFactory(); + } + + public function createProvider(): iterable + { + yield [ + 'gitter://api.gitter.im?room_id=5539a3ee5etest0d3255bfef', + 'gitter://token@api.gitter.im?room_id=5539a3ee5etest0d3255bfef', + ]; + } + + public function supportsProvider(): iterable + { + yield [true, 'gitter://token@host?room_id=5539a3ee5etest0d3255bfef']; + yield [false, 'somethingElse://token@host?room_id=5539a3ee5etest0d3255bfef']; + } + + public function incompleteDsnProvider(): iterable + { + yield 'missing token' => ['gitter://api.gitter.im?room_id=5539a3ee5etest0d3255bfef']; + } + + public function missingRequiredOptionProvider(): iterable + { + yield 'missing option: room_id' => ['gitter://token@host']; + } + + public function unsupportedSchemeProvider(): iterable + { + yield ['somethingElse://token@host?room_id=5539a3ee5etest0d3255bfef']; + yield ['somethingElse://token@host']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportTest.php b/src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportTest.php new file mode 100644 index 0000000000..b21311671d --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/Tests/GitterTransportTest.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\Gitter\Tests; + +use Symfony\Component\Notifier\Bridge\Gitter\GitterTransport; +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; + +/** + * @author Christin Gruber + */ +final class GitterTransportTest extends TransportTestCase +{ + public function createTransport(?HttpClientInterface $client = null): TransportInterface + { + return (new GitterTransport('token', '5539a3ee5etest0d3255bfef', $client ?: $this->createMock(HttpClientInterface::class)))->setHost('api.gitter.im'); + } + + public function toStringProvider(): iterable + { + yield ['gitter://api.gitter.im?room_id=5539a3ee5etest0d3255bfef', $this->createTransport()]; + } + + public function supportedMessagesProvider(): iterable + { + yield [new ChatMessage('Hello!')]; + } + + public function unsupportedMessagesProvider(): iterable + { + yield [new SmsMessage('0611223344', 'Hello!')]; + yield [$this->createMock(MessageInterface::class)]; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/composer.json b/src/Symfony/Component/Notifier/Bridge/Gitter/composer.json new file mode 100644 index 0000000000..95c67862fa --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/composer.json @@ -0,0 +1,31 @@ +{ + "name": "symfony/gitter-notifier", + "type": "symfony-bridge", + "description": "Symfony Gitter Notifier Bridge", + "keywords": ["chat", "gitter", "notifier"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Christin Gruber", + "email": "c.gruber@touchdesign.de" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "ext-json": "*", + "symfony/http-client": "^4.3|^5.0", + "symfony/notifier": "^5.3" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\Gitter\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/Gitter/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/Gitter/phpunit.xml.dist new file mode 100644 index 0000000000..65976bac0c --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/Gitter/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 0b9bf44b97..574d419a9f 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -104,6 +104,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\Mercure\MercureTransportFactory::class, 'package' => 'symfony/mercure-notifier', ], + 'gitter' => [ + 'class' => Bridge\Gitter\GitterTransportFactory::class, + 'package' => 'symfony/gitter-notifier', + ], ]; /** diff --git a/src/Symfony/Component/Notifier/Transport.php b/src/Symfony/Component/Notifier/Transport.php index 556c26b5e7..fd3af9b05c 100644 --- a/src/Symfony/Component/Notifier/Transport.php +++ b/src/Symfony/Component/Notifier/Transport.php @@ -17,6 +17,7 @@ use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiTransportFactory; +use Symfony\Component\Notifier\Bridge\Gitter\GitterTransportFactory; use Symfony\Component\Notifier\Bridge\Infobip\InfobipTransportFactory; use Symfony\Component\Notifier\Bridge\Iqsms\IqsmsTransportFactory; use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory; @@ -70,6 +71,7 @@ class Transport DiscordTransportFactory::class, GatewayApiTransportFactory::class, OctopushTransportFactory::class, + GitterTransportFactory::class, ]; private $factories;