herpaderpaldent/seat-notifications

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# seat-notifications
With this [SeAT](https://github.com/eveseat/seat) Package you can setup and manage notifications. It is build to be expended from within the package or from another package. Please read more about it further down.

[![Latest Stable Version](https://poser.pugx.org/herpaderpaldent/seat-notifications/v/stable)](https://packagist.org/packages/herpaderpaldent/seat-notifications)
[![StyleCI](https://github.styleci.io/repos/140680541/shield?branch=master)](https://github.styleci.io/repos/140680541)
[![Maintainability](https://api.codeclimate.com/v1/badges/2270953cdfaa22197d78/maintainability)](https://codeclimate.com/github/herpaderpaldent/seat-notifications/maintainability)
[![License](https://poser.pugx.org/herpaderpaldent/seat-notifications/license)](https://packagist.org/packages/herpaderpaldent/seat-notifications)
[![Total Downloads](https://poser.pugx.org/herpaderpaldent/seat-notifications/downloads)](https://packagist.org/packages/herpaderpaldent/seat-notifications)

***Important**: seat-notifications are work in progress and certainly have some bugs
please do report any findings to [seat-slack](https://eveseat-slack.herokuapp.com/) and report it as an issue*.

## Installation

1. cd to `/var/www/seat`
2. enter `composer require herpaderpaldent/seat-notifications`
4. run migration `php artisan migrate`

### Enable Notification Channel
To enable `seat-notifications` functionality of sending notifications. Create a bot for your notification channel. By default seat-notifications offers two notification channels which could be extended by other packages: `slack` and `discord`:
![configure](https://i.imgur.com/3ueTIaO.png)

a more detailed guide on oAuth creation will follow for now the blow table must suffice:

| Notification Channel | Redirect URLs                                                      | Comment                                                                |
|----------------------|--------------------------------------------------------------------|------------------------------------------------------------------------|
| Discord              | {seat_url}/seatnotifications/discord/configuration/callback/server | This callback url is needed for the configuration of your discord bot. |
| Discord              | {seat_url}/seatnotifications/discord/callback/user                 | This callback url is needed for the authentication of a discord user.  |
| Slack                | {seat_url}/seatnotifications/slack/configuration/callback/server   | This callback url is needed for the configuration of your slack bot.   |
| Slack                | {seat_url}/seatnotifications/slack/callback/user                   | This callback url is needed for the authentication of a slack user.    |

***Note**: you may only configure one notification channel at your will. However, for discord you must create a bot in your application. For Slack you need to add the bot permission to your oauth scope.*

### Setup Roles
To be able to subscribe to a notification the user needs the appropriate permission. Please setup a role that carries the needed permission and assign it to users that should be able to receive the notification.

### Restart workers
Since the notifications are send out by your workers you need to restart them to pick up the new code. Do this either via `docker-compose restart seat-worker` if you use docker, or `supervisorctl restart worker` on linux distributions.

### Authenticate
Users need to authenticate for your setup notification channel prior to receiving notifications. they may do this in the registration box on the notification page.

## What does the package do at the moment
* Send `RefreshTokenDeleted` Notification to Discord and/or Slack when a `refresh_token` is deleted.
* Using Model Observer to dispatch Notifications
* Notifications are queueable and send out via the queue. 
* The RefreshTokenNotification is able to be delivered to Channels or via DM on the users preference. 

Example:
![image](https://user-images.githubusercontent.com/6583519/50541121-0f8b3e00-0b9f-11e9-9319-1a4512376271.png)
![picture](https://i.imgur.com/img64u6.png)

## Developing

Most importantly: take note of [laravel's notification documentation](https://laravel.com/docs/5.5/notifications). Secondly have a look at [this package service provider](https://github.com/herpaderpaldent/seat-notifications/blob/master/src/SeatNotificationsServiceProvider.php) as it helps with merging the services array properly. The provided example of [RefreshTokenDeletedNotification](https://github.com/herpaderpaldent/seat-notifications/blob/master/src/Notifications/RefreshTokenDeletedNotification.php) is based upon it. Notifications are being send by using the `Notification` facade:

```
Notification::send($receipients, (new RefreshTokenDeletedNotification($refresh_token)));
```

where `$receipients` are a collection of modal that should receive the notification and `$refresh_token` is the deleted `refresh_token` that is passed to the constructor. In this example we use an Observer to send notifications: [Observer](https://github.com/herpaderpaldent/seat-notifications/blob/master/src/Observers/RefreshTokenObserver.php).

### Architecture

![uml_diagram](https://yuml.me/diagram/scruffy/class/[%3C%3CINotification%3E%3E%7Bbg:deepskyblue%7D]%5E-.-[AbstractNotification%7Bbg:deepskyblue%7D],[AbstractNotification]%5E[AbstractRefreshTokenNotification],[AbstractRefreshTokenNotification]%5E[DiscordRefreshTokenNotification%7Bbg:yellowgreen%7D],[AbstractRefreshTokenNotification%7Bbg:yellowgreen%7D]%5E[SlackRefreshTokenNotification%7Bbg:yellowgreen%7D],[AbstractNotification]++-%3E[%3C%3CINotificationDriver%3E%3E],[%3C%3CINotificationDriver%3E%3E]%5E-.-[DiscordNotificationDriver%7Bbg:lightseagreen%7D],[%3C%3CINotificationDriver%3E%3E%7Bbg:lightseagreen%7D]%5E-.-[SlackNotificationDriver%7Bbg:lightseagreen%7D],[AbstractNotification]uses%20-.-%3E[NotificationSubscription%7Bbg:orange%7D],[NotificationSubscription]++-%3E[NotificationRecipient%7Bbg:orange%7D],[NotificationRecipient]++-1%3E[Group%7Bbg:tomato%7D],[Group]++-%3E[User%7Bbg:tomato%7D])

### Add new drivers

If you have written a new notification driver that you would like to use for sending notifications to your users you might extend `config/services.php` similar to the provided example:

```
'seat-notification-channel' => [
        'discord'   => Herpaderpaldent\Seat\SeatNotifications\Drivers\DiscordNotificationDriver::class,
    ],
```

Your driver should extend `Herpaderpaldent\Seat\SeatNotifications\Drivers\AbstractNotificationDriver` and should contain;

```
    /**
     * The view name which will be used to store the channel settings.
     *
     * @return string
     */
    public static function getSettingsView() : string;

    /**
     * The label which will be applied to the subscription button.
     *
     * @return string
     */
    public static function getButtonLabel() : string;

    /**
     * The CSS class which have to be append into the subscription button.
     *
     * @return string
     */
    public static function getButtonIconClass() : string;

    /**
     * @return array
     */
    public static function getChannels() : array;

    /**
     * Determine if a channel has been properly setup.
     *
     * @return bool
     */
    public static function isSetup(): bool;

```

### Add new notifications

If you want to extend the available notifications you need to extend the `seat-notification` array in `config/services.php`:

```
'seat-notification'         => [
         // notification => [provider => implementation]
         Herpaderpaldent\Seat\SeatNotifications\Notifications\RefreshToken\AbstractRefreshTokenNotification::class => [
             'discord' => Herpaderpaldent\Seat\SeatNotifications\Notifications\RefreshToken\DiscordRefreshTokenNotification::class,
             'slack'   => Herpaderpaldent\Seat\SeatNotifications\Notifications\RefreshToken\SlackRefreshTokenNotification::class,
         ],
```

Your abstract notification must extend `Herpaderpaldent\Seat\SeatNotifications\Notifications\AbstractNotification` and contain the following methods to add your notification to the users notification list:

```
    /**
     * Return a title for the notification which will be displayed in UI notification list.
     * @return string
     */
    public static function getTitle(): string;

    /**
     * Return a description for the notification which will be displayed in UI notification list.
     * @return string
     */
    public static function getDescription(): string;

    /**
     * Determine if a notification can target public channel (forum category, chat, etc...).
     * @return bool
     */
    public static function isPublic(): bool;

    /**
     * Determine if a notification can target personal channel (private message, e-mail, etc...).
     * @return bool
     */
    public static function isPersonal(): bool;

    /**
     * Determine the permission needed to represent driver buttons.
     * @return string
     */
    public static function getPermission(): string;

```


### Use Notification Dispatcher

In order to dispatch notification solely to receiver that subscribed to the notification it is advised to use a custom dispatcher job f.e.:


````php
 public function handle()
    {
        Redis::funnel('soft_delete:refresh_token_' . $this->refresh_token->user->id)->limit(1)->then(function () {
            logger()->info('SoftDelete detected of ' . $this->refresh_token->user->name);

            $recipients = NotificationRecipient::all()
                ->filter(function ($recipient) {
                    return $recipient->shouldReceive(AbstractRefreshTokenNotification::class);
                });

            if($recipients->isEmpty()){
                logger()->debug('No Receiver found for this Notification. This job is going to be deleted.');
                $this->delete();
            }

            $recipients->groupBy('driver')
                ->each(function ($grouped_recipients) {
                    $driver = (string) $grouped_recipients->first()->driver;
                    $notification_class = AbstractRefreshTokenNotification::getDriverImplementation($driver);

                    Notification::send($grouped_recipients, (new $notification_class($this->refresh_token)));
                });

        }, function () {

            logger()->debug('A Soft-Delete job is already running for ' . $this->refresh_token->user->name);
            $this->delete();
        });
    }
````