biurad/flange

View on GitHub
src/Extensions/Symfony/NotifierExtension.php

Summary

Maintainability
C
1 day
Test Coverage
<?php declare(strict_types=1);

/*
 * This file is part of Biurad opensource projects.
 *
 * @copyright 2019 Biurad Group (https://biurad.com/)
 * @license   https://opensource.org/licenses/BSD-3-Clause License
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Flange\Extensions\Symfony;

use Flange\Extensions\EventDispatcherExtension;
use Rade\DI\Container;
use Rade\DI\Definitions\Reference;
use Rade\DI\Definitions\Statement;
use Rade\DI\Definitions\TaggedLocator;
use Rade\DI\Extensions\AliasedInterface;
use Rade\DI\Extensions\ExtensionInterface;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Notifier\Bridge\AllMySms\AllMySmsTransportFactory;
use Symfony\Component\Notifier\Bridge\AmazonSns\AmazonSnsTransportFactory;
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\Expo\ExpoTransportFactory;
use Symfony\Component\Notifier\Bridge\FakeChat\FakeChatTransportFactory;
use Symfony\Component\Notifier\Bridge\FakeSms\FakeSmsTransportFactory;
use Symfony\Component\Notifier\Bridge\Firebase\FirebaseTransportFactory;
use Symfony\Component\Notifier\Bridge\FortySixElks\FortySixElksTransportFactory;
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;
use Symfony\Component\Notifier\Bridge\KazInfoTeh\KazInfoTehTransportFactory;
use Symfony\Component\Notifier\Bridge\LightSms\LightSmsTransportFactory;
use Symfony\Component\Notifier\Bridge\LinkedIn\LinkedInTransportFactory;
use Symfony\Component\Notifier\Bridge\Mailjet\MailjetTransportFactory as MailjetNotifierTransportFactory;
use Symfony\Component\Notifier\Bridge\Mattermost\MattermostTransportFactory;
use Symfony\Component\Notifier\Bridge\Mercure\MercureTransportFactory;
use Symfony\Component\Notifier\Bridge\MessageBird\MessageBirdTransport;
use Symfony\Component\Notifier\Bridge\MessageMedia\MessageMediaTransportFactory;
use Symfony\Component\Notifier\Bridge\MicrosoftTeams\MicrosoftTeamsTransportFactory;
use Symfony\Component\Notifier\Bridge\Mobyt\MobytTransportFactory;
use Symfony\Component\Notifier\Bridge\Octopush\OctopushTransportFactory;
use Symfony\Component\Notifier\Bridge\OneSignal\OneSignalTransportFactory;
use Symfony\Component\Notifier\Bridge\OrangeSms\OrangeSmsTransportFactory;
use Symfony\Component\Notifier\Bridge\OvhCloud\OvhCloudTransportFactory;
use Symfony\Component\Notifier\Bridge\RocketChat\RocketChatTransportFactory;
use Symfony\Component\Notifier\Bridge\Sendinblue\SendinblueTransportFactory as SendinblueNotifierTransportFactory;
use Symfony\Component\Notifier\Bridge\Sinch\SinchTransportFactory;
use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory;
use Symfony\Component\Notifier\Bridge\Sms77\Sms77TransportFactory;
use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory;
use Symfony\Component\Notifier\Bridge\SmsBiuras\SmsBiurasTransportFactory;
use Symfony\Component\Notifier\Bridge\Smsc\SmscTransportFactory;
use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransportFactory;
use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory;
use Symfony\Component\Notifier\Bridge\Telnyx\TelnyxTransportFactory;
use Symfony\Component\Notifier\Bridge\TurboSms\TurboSmsTransport;
use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory;
use Symfony\Component\Notifier\Bridge\Vonage\VonageTransportFactory;
use Symfony\Component\Notifier\Bridge\Yunpian\YunpianTransportFactory;
use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransportFactory;
use Symfony\Component\Notifier\Channel\BrowserChannel;
use Symfony\Component\Notifier\Channel\ChannelPolicy;
use Symfony\Component\Notifier\Channel\ChatChannel;
use Symfony\Component\Notifier\Channel\EmailChannel;
use Symfony\Component\Notifier\Channel\PushChannel;
use Symfony\Component\Notifier\Channel\SmsChannel;
use Symfony\Component\Notifier\Chatter;
use Symfony\Component\Notifier\EventListener\NotificationLoggerListener;
use Symfony\Component\Notifier\EventListener\SendFailedMessageToNotifierListener;
use Symfony\Component\Notifier\Message\ChatMessage;
use Symfony\Component\Notifier\Message\PushMessage;
use Symfony\Component\Notifier\Message\SmsMessage;
use Symfony\Component\Notifier\Messenger\MessageHandler;
use Symfony\Component\Notifier\Notifier;
use Symfony\Component\Notifier\Recipient\Recipient;
use Symfony\Component\Notifier\Texter;
use Symfony\Component\Notifier\Transport;
use Symfony\Component\Notifier\Transport\NullTransportFactory;

use function Rade\DI\Loader\service;

/**
 * Symfony Notifier Extension.
 *
 * @author Divine Niiquaye Ibok <divineibok@gmail.com>
 */
class NotifierExtension implements AliasedInterface, ConfigurationInterface, ExtensionInterface
{
    /**
     * {@inheritdoc}
     */
    public function getAlias(): string
    {
        return 'notifier';
    }

    /**
     * {@inheritdoc}
     */
    public function getConfigTreeBuilder(): TreeBuilder
    {
        $treeBuilder = new TreeBuilder(__CLASS__);

        $treeBuilder->getRootNode('notifier')
            ->info('Notifier configuration')
            ->canBeEnabled()
            ->fixXmlConfig('chatter_transport')
            ->children()
                ->arrayNode('chatter_transports')
                    ->useAttributeAsKey('name')
                    ->prototype('scalar')->end()
                ->end()
            ->end()
            ->fixXmlConfig('texter_transport')
            ->children()
                ->arrayNode('texter_transports')
                    ->useAttributeAsKey('name')
                    ->prototype('scalar')->end()
                ->end()
            ->end()
            ->children()
                ->booleanNode('notification_on_failed_messages')->defaultFalse()->end()
            ->end()
            ->children()
                ->arrayNode('channel_policy')
                    ->useAttributeAsKey('name')
                    ->prototype('array')
                        ->beforeNormalization()->ifString()->then(fn (string $v) => [$v])->end()
                        ->prototype('scalar')->end()
                    ->end()
                ->end()
            ->end()
            ->fixXmlConfig('admin_recipient')
            ->children()
                ->arrayNode('admin_recipients')
                    ->arrayPrototype()
                        ->children()
                            ->scalarNode('email')->cannotBeEmpty()->end()
                            ->scalarNode('phone')->defaultValue('')->end()
                        ->end()
                    ->end()
                ->end()
            ->end()
        ;

        return $treeBuilder;
    }

    /**
     * {@inheritdoc}
     */
    public function register(Container $container, array $configs = []): void
    {
        if (!$configs['enabled']) {
            return;
        }

        if (!\class_exists(Notifier::class)) {
            throw new \LogicException('Notifier support cannot be enabled as the component is not installed. Try running "composer require symfony/notifier".');
        }

        $definitions = [
            'notifier.chatter.transport_factory' => service(Transport::class, [new TaggedLocator('chatter.transport_factory')])->public(false),
            'notifier.chatter.transports' => service([new Reference('notifier.chatter.transport_factory'), 'fromStrings'], [$configs['chatter_transports'] ?? []])->public(false),
            'notifier.chatter.messenger.chat_handler' => service(MessageHandler::class, [$ct = new Reference('notifier.chatter.transports')])->public(false)->tag('messenger.message_handler', ['handles' => ChatMessage::class]),
            'notifier.texter.transport_factory' => service(Transport::class, [new TaggedLocator('notifier.texter.transport_factory')])->public(false),
            'notifier.texter.transports' => service([new Reference('notifier.texter.transport_factory'), 'fromStrings'], [$configs['texter_transports'] ?? []])->public(false),
            'notifier.texter.messenger.sms_handler' => service(MessageHandler::class, [$tt = new Reference('notifier.texter.transports')])->public(false)->tag('messenger.message_handler', ['handles' => SmsMessage::class]),
            'notifier.texter.messenger.push_handler' => service(MessageHandler::class, [$tt])->public(false)->tag('messenger.message_handler', ['handles' => PushMessage::class]),
            'notifier.channel.browser' => service(BrowserChannel::class)->public(false)->tag('notifier.channel', ['channel' => 'browser']),
            'notifier.channel.chat' => $cc = service(ChatChannel::class, [$ct, $ms = new Reference('?messenger.default_bus')])->public(false)->tag('notifier.channel', ['channel' => 'chat']),
            'notifier.channel.sms' => $sc = service(SmsChannel::class, [$tt, $ms])->public(false)->tag('notifier.channel', ['channel' => 'sms']),
            'notifier.channel.push' => $pc = service(PushChannel::class, [$tt, $ms])->public(false)->tag('notifier.channel', ['channel' => 'push']),
            'notifier' => $notifier = service(Notifier::class, [new TaggedLocator('notifier.channel', 'channel'), new Statement(ChannelPolicy::class, [$configs['channel_policy']])])->typed(),
        ];

        if (isset($configs['admin_recipients'])) {
            foreach ($configs['admin_recipients'] as $recipient) {
                $notifier->bind('addAdminRecipient', [new Statement(Recipient::class, [$recipient['email'], $recipient['phone']])]);
            }
        }

        if ($configs['texter_transports']) {
            $definitions['notifier.texter'] = service(Texter::class, [$tt, $ms])->typed();
        }

        if ($configs['chatter_transports']) {
            $definitions['notifier.chatter'] = service(Chatter::class, [$ct, $ms])->typed();
        }

        if ($container->hasExtension(EventDispatcherExtension::class)) {
            if ($container->hasExtension(MailerExtension::class)) {
                $sender = $container->definition('mailer.envelope_listener')->getArgument(0);
                $definitions['notifier.channel.email'] = $ec = service(EmailChannel::class, [new Reference('mailer.transports'), $ms, $sender])->public(false)->tag('notifier.channel', ['channel' => 'email']);
            }

            $definitions['notifier.listener.logger_notification'] = service(NotificationLoggerListener::class)->public(false)->tag('event_subscriber');
        }

        if ($container->hasExtension(MessengerExtension::class)) {
            $definitions['notifier.listener.failed_message'] = service(SendFailedMessageToNotifierListener::class, [new Reference('notifier')])->public(false)->tag('event_subscriber');

            // as we have a bus, the channels don't need the transports
            $cc->args([null, $ms]);
            $sc->args([null, $ms]);
            $pc->args([null, $ms]);

            if (isset($ec)) {
                $ec->args([null, $ms]);
            }
        }

        $classToServices = [
            AllMySmsTransportFactory::class => 'texter.transport_factory',
            AmazonSnsTransportFactory::class => ['texter.transport_factory', 'chatter.transport_factory'],
            ClickatellTransportFactory::class => 'texter.transport_factory',
            DiscordTransportFactory::class => 'chatter.transport_factory',
            EsendexTransportFactory::class => 'texter.transport_factory',
            ExpoTransportFactory::class => 'texter.transport_factory',
            FakeChatTransportFactory::class => 'chatter.transport_factory',
            FakeSmsTransportFactory::class => 'texter.transport_factory',
            FirebaseTransportFactory::class => 'chatter.transport_factory',
            FortySixElksTransportFactory::class => 'texter.transport_factory',
            FreeMobileTransportFactory::class => 'texter.transport_factory',
            GatewayApiTransportFactory::class => 'texter.transport_factory',
            GitterTransportFactory::class => 'chatter.transport_factory',
            GoogleChatTransportFactory::class => 'chatter.transport_factory',
            InfobipTransportFactory::class => 'texter.transport_factory',
            IqsmsTransportFactory::class => 'texter.transport_factory',
            KazInfoTehTransportFactory::class => 'texter.transport_factory',
            LightSmsTransportFactory::class => 'texter.transport_factory',
            LinkedInTransportFactory::class => 'chatter.transport_factory',
            MailjetNotifierTransportFactory::class => 'texter.transport_factory',
            MattermostTransportFactory::class => 'chatter.transport_factory',
            MercureTransportFactory::class => 'chatter.transport_factory',
            MessageBirdTransport::class => 'texter.transport_factory',
            MessageMediaTransportFactory::class => 'texter.transport_factory',
            MicrosoftTeamsTransportFactory::class => 'chatter.transport_factory',
            MobytTransportFactory::class => 'texter.transport_factory',
            NullTransportFactory::class => ['texter.transport_factory', 'chatter.transport_factory'],
            OctopushTransportFactory::class => 'texter.transport_factory',
            OneSignalTransportFactory::class => 'texter.transport_factory',
            OrangeSmsTransportFactory::class => 'texter.transport_factory',
            OvhCloudTransportFactory::class => 'texter.transport_factory',
            RocketChatTransportFactory::class => 'chatter.transport_factory',
            SendinblueNotifierTransportFactory::class => 'texter.transport_factory',
            SinchTransportFactory::class => 'texter.transport_factory',
            SlackTransportFactory::class => 'chatter.transport_factory',
            Sms77TransportFactory::class => 'texter.transport_factory',
            SmsapiTransportFactory::class => 'texter.transport_factory',
            SmsBiurasTransportFactory::class => 'texter.transport_factory',
            SmscTransportFactory::class => 'texter.transport_factory',
            SpotHitTransportFactory::class => 'texter.transport_factory',
            TelegramTransportFactory::class => 'chatter.transport_factory',
            TelnyxTransportFactory::class => 'texter.transport_factory',
            TurboSmsTransport::class => 'texter.transport_factory',
            TwilioTransportFactory::class => 'texter.transport_factory',
            VonageTransportFactory::class => 'texter.transport_factory',
            YunpianTransportFactory::class => 'texter.transport_factory',
            ZulipTransportFactory::class => 'chatter.transport_factory',
        ];

        foreach ($classToServices as $class => $classTag) {
            if (!\class_exists($class)) {
                continue;
            }

            $container->tag($class, $classTag);
        }

        $container->multiple($definitions);
    }
}