From 351065e77429d5939f79970fd6b6f6b35003f6bc Mon Sep 17 00:00:00 2001 From: James Hemery Date: Sat, 23 Jan 2021 02:31:58 +0100 Subject: [PATCH] [Notifier] [FakeSms] Add the bridge --- .../FrameworkExtension.php | 2 + .../Resources/config/notifier_transports.php | 5 ++ .../Notifier/Bridge/FakeSms/CHANGELOG.md | 7 ++ .../Bridge/FakeSms/FakeSmsEmailTransport.php | 75 +++++++++++++++++++ .../FakeSms/FakeSmsTransportFactory.php | 58 ++++++++++++++ .../Notifier/Bridge/FakeSms/README.md | 23 ++++++ .../Tests/FakeSmsEmailTransportTest.php | 49 ++++++++++++ .../Tests/FakeSmsTransportFactoryTest.php | 68 +++++++++++++++++ .../Notifier/Bridge/FakeSms/composer.json | 32 ++++++++ .../Notifier/Bridge/FakeSms/phpunit.xml.dist | 31 ++++++++ .../Exception/UnsupportedSchemeException.php | 4 + 11 files changed, 354 insertions(+) create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/CHANGELOG.md create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsEmailTransport.php create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsTransportFactory.php create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/README.md create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsEmailTransportTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsTransportFactoryTest.php create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json create mode 100644 src/Symfony/Component/Notifier/Bridge/FakeSms/phpunit.xml.dist diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 80f573235b75e..d7e3ad4ee3fdc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -112,6 +112,7 @@ use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory; use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory; use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory; +use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiTransportFactory; @@ -2332,6 +2333,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $ FirebaseTransportFactory::class => 'notifier.transport_factory.firebase', FreeMobileTransportFactory::class => 'notifier.transport_factory.freemobile', SpotHitTransportFactory::class => 'notifier.transport_factory.spothit', + FakeSmsTransportFactory::class => 'notifier.transport_factory.fakesms', OvhCloudTransportFactory::class => 'notifier.transport_factory.ovhcloud', SinchTransportFactory::class => 'notifier.transport_factory.sinch', ZulipTransportFactory::class => 'notifier.transport_factory.zulip', diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php index 23a9a47936df5..b8e4690176f52 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php @@ -15,6 +15,7 @@ use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory; use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory; use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory; +use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory; use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory; use Symfony\Component\Notifier\Bridge\FreeMobile\FreeMobileTransportFactory; use Symfony\Component\Notifier\Bridge\GatewayApi\GatewayApiTransportFactory; @@ -95,6 +96,10 @@ ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') + ->set('notifier.transport_factory.fakesms', FakeSmsTransportFactory::class) + ->parent('notifier.transport_factory.abstract') + ->tag('texter.transport_factory') + ->set('notifier.transport_factory.ovhcloud', OvhCloudTransportFactory::class) ->parent('notifier.transport_factory.abstract') ->tag('texter.transport_factory') diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/CHANGELOG.md b/src/Symfony/Component/Notifier/Bridge/FakeSms/CHANGELOG.md new file mode 100644 index 0000000000000..1f2b652ac20ea --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/CHANGELOG.md @@ -0,0 +1,7 @@ +CHANGELOG +========= + +5.3 +--- + + * Add the bridge diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsEmailTransport.php b/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsEmailTransport.php new file mode 100644 index 0000000000000..886b9286fbd6c --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsEmailTransport.php @@ -0,0 +1,75 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\FakeSms; + +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\Mailer\Exception\TransportExceptionInterface; +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Mime\Email; +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\HttpClient\HttpClientInterface; + +/** + * @author James Hemery + */ +final class FakeSmsEmailTransport extends AbstractTransport +{ + private $mailer; + private $to; + private $from; + + public function __construct(MailerInterface $mailer, string $to, string $from, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null) + { + $this->mailer = $mailer; + $this->to = $to; + $this->from = $from; + + parent::__construct($client, $dispatcher); + } + + public function __toString(): string + { + return sprintf('fakesms+email://%s?to=%s&from=%s', $this->getEndpoint(), $this->to, $this->from); + } + + public function supports(MessageInterface $message): bool + { + return $message instanceof SmsMessage; + } + + /** + * @param MessageInterface|SmsMessage $message + * + * @throws TransportExceptionInterface + */ + protected function doSend(MessageInterface $message): SentMessage + { + if (!$this->supports($message)) { + throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message); + } + + $email = (new Email()) + ->from($this->from) + ->to($this->to) + ->subject(sprintf('New SMS on phone number: %s', $message->getPhone())) + ->html($message->getSubject()) + ->text($message->getSubject()); + + $this->mailer->send($email); + + return new SentMessage($message, (string) $this); + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsTransportFactory.php b/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsTransportFactory.php new file mode 100644 index 0000000000000..2dbc36b466f92 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/FakeSmsTransportFactory.php @@ -0,0 +1,58 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\FakeSms; + +use Symfony\Component\Notifier\Exception\UnsupportedSchemeException; +use Symfony\Component\Notifier\Transport\AbstractTransportFactory; +use Symfony\Component\Notifier\Transport\Dsn; +use Symfony\Component\Notifier\Transport\TransportInterface; +use Symfony\Contracts\Service\ServiceProviderInterface; + +/** + * @author James Hemery + */ +final class FakeSmsTransportFactory extends AbstractTransportFactory +{ + protected $serviceProvider; + + public function __construct(ServiceProviderInterface $serviceProvider) + { + parent::__construct(); + + $this->serviceProvider = $serviceProvider; + } + + /** + * @return FakeSmsEmailTransport + */ + public function create(Dsn $dsn): TransportInterface + { + $scheme = $dsn->getScheme(); + + if (!\in_array($scheme, $this->getSupportedSchemes())) { + throw new UnsupportedSchemeException($dsn, 'fakesms', $this->getSupportedSchemes()); + } + + if ('fakesms+email' === $scheme) { + $serviceId = $dsn->getHost(); + $to = $dsn->getRequiredOption('to'); + $from = $dsn->getRequiredOption('from'); + + return (new FakeSmsEmailTransport($this->serviceProvider->get($serviceId), $to, $from))->setHost($serviceId); + } + } + + protected function getSupportedSchemes(): array + { + return ['fakesms+email']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/README.md b/src/Symfony/Component/Notifier/Bridge/FakeSms/README.md new file mode 100644 index 0000000000000..b216bd8a02f58 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/README.md @@ -0,0 +1,23 @@ +Fake SMS Notifier +================= + +Provides Fake SMS (as email during development) integration for Symfony Notifier. + +#### DSN example + +``` +FAKE_SMS_DSN=fakesms+email://MAILER_SERVICE_ID?to=TO&from=FROM +``` + +where: + - `MAILER_SERVICE_ID` is mailer service id (use `mailer` by default) + - `TO` is email who receive SMS during development + - `FROM` is email who send SMS during development + +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/FakeSms/Tests/FakeSmsEmailTransportTest.php b/src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsEmailTransportTest.php new file mode 100644 index 0000000000000..f1dd81894a871 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsEmailTransportTest.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\FakeSms\Tests; + +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsEmailTransport; +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 James Hemery + */ +final class FakeSmsEmailTransportTest extends TransportTestCase +{ + public function createTransport(?HttpClientInterface $client = null): TransportInterface + { + return (new FakeSmsEmailTransport($this->createMock(MailerInterface::class), 'recipient@email.net', 'from@email.net'))->setHost('mailer'); + } + + public function toStringProvider(): iterable + { + yield ['fakesms+email://mailer?to=recipient@email.net&from=from@email.net', $this->createTransport()]; + } + + public function supportedMessagesProvider(): iterable + { + yield [new SmsMessage('0611223344', 'Hello!')]; + 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/FakeSms/Tests/FakeSmsTransportFactoryTest.php b/src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsTransportFactoryTest.php new file mode 100644 index 0000000000000..9ca640b012773 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/Tests/FakeSmsTransportFactoryTest.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Notifier\Bridge\FakeSms\Tests; + +use Symfony\Component\Mailer\MailerInterface; +use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory; +use Symfony\Component\Notifier\Tests\TransportFactoryTestCase; +use Symfony\Component\Notifier\Transport\TransportFactoryInterface; +use Symfony\Contracts\Service\ServiceProviderInterface; + +/** + * @author James Hemery + */ +final class FakeSmsTransportFactoryTest extends TransportFactoryTestCase +{ + /** + * @return FakeSmsTransportFactory + */ + public function createFactory(): TransportFactoryInterface + { + $serviceProvider = $this->createMock(ServiceProviderInterface::class); + $serviceProvider->method('has')->willReturn(true); + $serviceProvider->method('get')->willReturn($this->createMock(MailerInterface::class)); + + return new FakeSmsTransportFactory($serviceProvider); + } + + public function createProvider(): iterable + { + yield [ + 'fakesms+email://mailer?to=recipient@email.net&from=sender@email.net', + 'fakesms+email://mailer?to=recipient@email.net&from=sender@email.net', + ]; + } + + public function missingRequiredOptionProvider(): iterable + { + yield 'missing option: from' => ['fakesms+email://mailer?to=recipient@email.net']; + yield 'missing option: to' => ['fakesms+email://mailer?from=sender@email.net']; + } + + public function supportsProvider(): iterable + { + yield [true, 'fakesms+email://mailer?to=recipient@email.net&from=sender@email.net']; + yield [false, 'somethingElse://api_token@default?from=MyCompany']; + } + + public function incompleteDsnProvider(): iterable + { + yield 'missing from' => ['fakesms+email://mailer?to=recipient@email.net']; + yield 'missing to' => ['fakesms+email://mailer?from=recipient@email.net']; + } + + public function unsupportedSchemeProvider(): iterable + { + yield ['foobar://api_token@default?from=MyCompany']; + yield ['foobar://api_token@default']; + } +} diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json new file mode 100644 index 0000000000000..b47e06e578d14 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/composer.json @@ -0,0 +1,32 @@ +{ + "name": "symfony/fake-sms-notifier", + "type": "symfony-bridge", + "description": "Fake SMS (as email during development) Notifier Bridge.", + "keywords": ["sms", "development", "email", "notifier", "symfony"], + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "James Hemery", + "homepage": "https://github.com/JamesHemery" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "require": { + "php": ">=7.2.5", + "symfony/http-client": "^4.4|^5.2", + "symfony/notifier": "^5.3", + "symfony/event-dispatcher-contracts": "^2", + "symfony/mailer": "^5.2" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Notifier\\Bridge\\FakeSms\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "minimum-stability": "dev" +} diff --git a/src/Symfony/Component/Notifier/Bridge/FakeSms/phpunit.xml.dist b/src/Symfony/Component/Notifier/Bridge/FakeSms/phpunit.xml.dist new file mode 100644 index 0000000000000..3d628a466b162 --- /dev/null +++ b/src/Symfony/Component/Notifier/Bridge/FakeSms/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 06bb494d5f86c..3c3d3c7393d58 100644 --- a/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php +++ b/src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php @@ -72,6 +72,10 @@ class UnsupportedSchemeException extends LogicException 'class' => Bridge\SpotHit\SpotHitTransportFactory::class, 'package' => 'symfony/spot-hit-notifier', ], + 'fakesms' => [ + 'class' => Bridge\FakeSms\FakeSmsTransportFactory::class, + 'package' => 'symfony/fake-sms-notifier', + ], 'ovhcloud' => [ 'class' => Bridge\OvhCloud\OvhCloudTransportFactory::class, 'package' => 'symfony/ovh-cloud-notifier',