Slide 1

Slide 1 text

Symfony Notifier Sending messages via a unified API Jan Schädlich @jschaedl SensioLabs 2020-02-04 | Symfony User Group Hamburg

Slide 2

Slide 2 text

“… a notification system is a … software … that provides a means of delivering a message to a set of recipients … “ https://en.wikipedia.org/wiki/Notification_system What is a Notification System?

Slide 3

Slide 3 text

Application Incident Notification System Sms Chat Email Notification

Slide 4

Slide 4 text

Application Incident Notification System Policy Sms Chat Email Notification

Slide 5

Slide 5 text

Symfony Notifier Component experimental

Slide 6

Slide 6 text

ChannelPolicy ChatChannel SmsChannel EmailChannel Notifier Notification ChatMessage SmsMessage EmailMessage Texter Transport SmsMessage Transport ChatMessage Chatter Transports Transports Transports

Slide 7

Slide 7 text

$ composer req symfony/notifier

Slide 8

Slide 8 text

Transport Install with Slack composer require symfony/slack-notifier Telegram composer require symfony/telegram-notifier Twilio composer require symfony/twilio-notifier Nexmo composer require symfony/nexmo-notifier * Using Mailer Transports is also possible.

Slide 9

Slide 9 text

Notifications

Slide 10

Slide 10 text

Content Importance Subject

Slide 11

Slide 11 text

use Symfony\Component\Notifier\Notification\Notification; $notification = (new Notification()) ->subject('A nice subject') ->content(‘An even nicer content.') ->importance(Notification::IMPORTANCE_URGENT) ; 1 2 3 4 5 6 7

Slide 12

Slide 12 text

use Symfony\Component\Notifier\Notification\Notification; $notification = (new Notification()) ->subject('A nice subject') ->content(‘An even nicer content.') ->importance(Notification::IMPORTANCE_URGENT) ->emoji('!') # email, chat, sms # chat/slack, chat/telegram, sms/twilio, sms/nexmo # {channel}/{transport} ->channels([‘chat/slack', 'email']) ; 1 2 3 4 5 6 7 8 9 10 11 12

Slide 13

Slide 13 text

use Symfony\Component\Notifier\Notification\Notification; $errorNotification = Notification::fromThrowable( new \Exception('Oops!') ); 1 2 3 4 5

Slide 14

Slide 14 text

use Symfony\Component\Notifier\Notification\Notification; $errorNotification = Notification::fromThrowable( new \Exception(‘Oops!'), # email, chat, sms # chat/slack, chat/telegram, sms/twilio, sms/nexmo ['chat/slack', 'email'] ); 1 2 3 4 5 6 7 8

Slide 15

Slide 15 text

Notifier

Slide 16

Slide 16 text

use Symfony\Component\Notifier\Notifier; // ??? $notifier = new Notifier($channels, $channelPolicy); $notifier->send(new Notification(…)); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 17

Slide 17 text

use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; $transport = (new SlackTransportFactory())->create(‘slack://...’); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 18

Slide 18 text

use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Transport; $transport = Transport::fromDsns([ 'slack://...', 'telegram://...' ]); // returns a Transports object 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 19

Slide 19 text

use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; use Symfony\Component\Notifier\Channel\ChatChannel; $transport = (new SlackTransportFactory())->create(‘slack://...’); $channels = [ 'chat' => new ChatChannel($transport) ]; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 20

Slide 20 text

use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; use Symfony\Component\Notifier\Channel\ChatChannel; use Symfony\Component\Notifier\Channel\ChannelPolicy; $transport = (new SlackTransportFactory())->create(‘slack://...’); $channels = ['chat' => new ChatChannel($transport)]; $channelPolicy = new ChannelPolicy([ 'urgent' => [‘chat‘], 'high' => ['chat'], 'medium' => [‘chat/slack‘], 'low' => [‘chat/slack'], ]); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Slide 21

Slide 21 text

use Symfony\Component\Notifier\Notifier; use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory; use Symfony\Component\Notifier\Channel\ChatChannel; use Symfony\Component\Notifier\Channel\ChannelPolicy; $transport = (new SlackTransportFactory())->create(‘slack://...’); $channels = ['chat' => new ChatChannel($transport)]; $channelPolicy = new ChannelPolicy([/* ... */]); $notifier = new Notifier($channels, $channelPolicy); $notifier->send(new Notification(…), new Recipient(…)); 1 2 3 4 5 6 7 8 9 10 … 16 17 18 19 20

Slide 22

Slide 22 text

Let’s take a closer look

Slide 23

Slide 23 text

ChannelPolicy Routing to Channels and Transports

Slide 24

Slide 24 text

$channelPolicy = new ChannelPolicy([ 'urgent' => ['email', 'chat/slack', 'sms'], 'high' => ['email', ‘chat/slack'], 'medium' => ['email'], 'low' => ['pidgeon'], ]); • Routing Notifications by importance to channels and transports • Define your own importance

Slide 25

Slide 25 text

Channels Notifications to Messages

Slide 26

Slide 26 text

$channels = [ 'email' => new EmailChannel($emailTransports), 'chat' => new ChatChannel($chatTransports), 'sms' => new SmsChannel($smsTransports), // ... ]; • Channels are like categories • Channels can hold several Transports • Channels convert Notifications into Messages - EmailNotificationInterface - ChatNotificationInterface - SmsNotificationInterface

Slide 27

Slide 27 text

$channels = [ 'email' => new EmailChannel($emailTransports), 'chat' => new ChatChannel($chatTransports), 'sms' => new SmsChannel($smsTransports), 'browser' => new BrowserChannel($requestStack), ]; • BrowserChannel holds the RequestStack • BrowserChannel writes Notification as flash messages

Slide 28

Slide 28 text

Transports Sending via third-party Service

Slide 29

Slide 29 text

$chatTransports = Transport::fromDsns([ 'slack://ACCES_TOKEN@default?channel=CHANNEL', 'telegram://TELEGRAM_TOKEN@default' ]); $smsTransports = Transport::fromDsns([ 'twilio://ACCOUNT_ID:ACCOUNT_TOKEN@default?from=SENDER_NAME', 'nexmo://API_TOKEN:API_SECRET@default?from=SENDER_NAME' ]); Supports ChatMessage only Supports SmsMessage only

Slide 30

Slide 30 text

$dsn = 'failover(slack://ACCES_TOKEN@default?channel=CHANNEL telegram://TELEGRAM_TOKEN@default)';

Slide 31

Slide 31 text

$dsn = 'roundrobin(slack://ACCES_TOKEN@default?channel=CHANNEL telegram://TELEGRAM_TOKEN@default)';

Slide 32

Slide 32 text

Framework Integration

Slide 33

Slide 33 text

framework: notifier: enabled: true chatter_transports: slack: '%env(SLACK_DSN)%' telegram: '%env(TELEGRAM_DSN)%' texter_transports: twilio: '%env(TWILIO_DSN)%' nexmo: '%env(NEXMO_DSN)%' channel_policy: # use chat/slack, chat/telegram, sms/twilio, sms/nexmo # or email, chat, sms urgent: ['email', 'chat', 'sms'] high: ['email', 'chat'] medium: ['email'] low: ['email'] admin_recipients: - { email: 'admin@example.com', phone: '0815 223445' } notification_on_failed_messages: false

Slide 34

Slide 34 text

Monolog Handler

Slide 35

Slide 35 text

# config/packages/prod/monolog.yaml monolog: channels: ['notifier'] handlers: admin_notifications: type: filter handler: notifier max_level: info channels: ['notifier'] notifier: type: service id: Symfony\Bridge\Monolog\Handler\NotifierHandler

Slide 36

Slide 36 text

Messenger Integration

Slide 37

Slide 37 text

namespace Symfony\Component\Notifier\Channel; use Symfony\Component\Messenger\MessageBusInterface; use Symfony\Component\Notifier\Exception\LogicException; use Symfony\Component\Notifier\Transport\TransportInterface; abstract class AbstractChannel implements ChannelInterface { protected $transport; protected $bus; public function __construct( TransportInterface $transport = null, MessageBusInterface $bus = null ) { if (null === $transport && null === $bus) { throw new LogicException(...); } $this->transport = $transport; $this->bus = $bus; } }

Slide 38

Slide 38 text

namespace Symfony\Component\Notifier\Messenger; use Symfony\Component\Notifier\Message\MessageInterface; use Symfony\Component\Notifier\Transport\TransportInterface; final class MessageHandler { private $transport; public function __construct(TransportInterface $transport) { $this->transport = $transport; } public function __invoke(MessageInterface $message) { $this->transport->send($message); } }

Slide 39

Slide 39 text

# FrameworkBundle/Resources/config/notifier.xml

Slide 40

Slide 40 text

Disable delivery

Slide 41

Slide 41 text

# config/packages/dev/notifier.yaml # config/packages/test/notifier.yaml framework: notifier: chatter_transports: slack: 'null://' telegram: 'null://' texter_transports: twilio: 'null://' nexmo: 'null://'

Slide 42

Slide 42 text

Feature Recap • Sending chat, sms, email and browser notifications • Built-in providers (Slack, Telegram, Nexmo, Twilio) • Monolog Handler • Async notifications via messenger • NULL transport for dev and test environment

Slide 43

Slide 43 text

What’s missing? • More built-in providers (Microsofft Teams, Mattermost, etc.) • Possibility to define a custom transport • (Disable usage of messenger) • WebProfiler and DebugToolBar integration • WebTestCase notifier assertions

Slide 44

Slide 44 text

Thank you! https://speakerdeck.com/jschaedl