Notify

From truxwiki.com
Jump to navigation Jump to search
Details
Executable Notify.exe
Stage 255
Percent Complete 100%
Message Queue notify

The Notify ETL is responsible for broadcasting different topics of information to different communications channels when a load completes. The progress of a load is not dependent on Notify.

Introduction

Truxton will automatically send out notifications of different events. There are two pieces of the notification puzzle, what to send and how to send it.

Configuration

When giving examples on how to configure the individual items of Notify, we will use the TruxtonSettings.xml file as an example. Configurations for notify are nested in a "Notify" block.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <!-- 
        individual notify configs here 
      -->
  </Notify>
</truxton_options>

Notifiers

The channel in which a topic will be sent. E.G. Email, SMS, Teams, etc. Each individual notifier must be configured in order to be enabled otherwise they will NOT send anything. This means each notifier is "opt-in". Configurations for each notifier is nested in a "Notifiers" block.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Notifiers>
      <!-- 
          individual notifiers configs here 
        -->
    </Notifiers>
  </Notify>
</truxton_options>

The Notify ETL contains the following notifiers out of the box:

Email

Sends email notifications via an SMTP server.

Configurations for this notifier are nested in an "Email" block.

Name Data Type Description
Host string The smtp host which will send the email.
Port number The smtp port to connect to the host. Optional. If omitted will default to the default smtp port: "587"
Sender string The name of the sender who sends this email
StaticRecipients string[] Who will receive this email.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Notifiers>
      <Email>
        <Host>smtp.gmail.com</Host>
        <!-- port will default to 587 (TLS), if you want SSL you need to manually specify -->
        <Port>465</Port>
        <Sender>load@mydomain.com</Sender>
        <StaticRecipients name="0">firstemail@mydomain.com</StaticRecipients>
        <StaticRecipients name="1">anotheremail@anotherdomain.com</StaticRecipients>
        <StaticRecipients name="2">andanotheremail@foo.com</StaticRecipients>
      </Email>
    </Notifiers>
  </Notify>
</truxton_options>

Microsoft Teams

Sends notifications to a Microsoft Teams instance. You will need to setup a webhook to enable his notifier.

Configurations for this notifier are nested in an "MSTeams" block.

Name Data Type Description
WebhookURL string The webhook url you configured for your microsoft teams instance.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Notifiers>
      <MSTeams>
        <WebhookURL>https://MYINSTANCE.webhook.office.com/webhookb2/FOO/IncomingWebhook/BAR</WebhookURL>
      </MSTeams>
    </Notifiers>
  </Notify>
</truxton_options>

Send Grid

Sends email notifications via a SendGrid account. You will need to setup a SendGrid account to enable this notifier.

Configurations for this notifier are nested in an "SendGrid" block.

Name Data Type Description
APIKey string A sendgrid API key.
Sender string The name of the sender who sends this email via sendgrid
StaticRecipients string[] Who will receive this email.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Notifiers>
      <SendGrid>
        <APIKey>A_REALLY_LONG_SET_OF_CHARACTERS_THAT_IS_YOUR_SEND_GRID_API_KEY</APIKey>
        <Sender>load@mydomain.com</Sender>
        <StaticRecipients name="0">firstemail@mydomain.com</StaticRecipients>
        <StaticRecipients name="1">anotheremail@anotherdomain.com</StaticRecipients>
        <StaticRecipients name="2">andanotheremail@foo.com</StaticRecipients>
      </SendGrid>
    </Notifiers>
  </Notify>
</truxton_options>

Slack

Sends notifications to a Slack instance. You will need to setup a webhook to enable his notifier.

Configurations for this notifier are nested in an "Slack" block.

Name Data Type Description
WebhookURL string The webhook url you configured for your microsoft teams instance.
Channel string The channel to post to. Optional. The webhook configured typically has a channel associated with it, but this overrides that.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Notifiers>
      <Slack>
        <WebhookURL>https://hooks.slack.com/services/FOO/BAR/FIZZBUZZ</WebhookURL>
        <Channel>OPTIONAL_CHANNEL_NAME</Channel>
      </Slack>
    </Notifiers>
  </Notify>
</truxton_options>

Twilio SMS

Sends SMS notifications via a Twilio account. You will need to setup a Twilio account to enable this notifier.

You will need three items of information in order to get text messaging to work. Log onto Twilio and gather the following items:

  1. Sender Phone Number - Click on the "See all phone numbers" link
  2. Account SID - You can get this from the ACCOUNT SID field
  3. Auth Token - Expand the AUTH TOKEN by clicking the "Show" button

Configurations for this notifier are nested in an "TwilioSMS" block.

Name Data Type Description
AccountSID string A Twilio Account SID.
AuthToken string A Twilio Authentication Token.
SenderNumber string A Twilio phone number.
StaticRecipients string[] A list of phones numbers who will receive the SMS.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>
    <Notifiers>
      <TwilioSMS>
        <AccountSID>AC4e6f742061205265616c546f6b656e</AccountSID>
        <AuthToken>546869732d69732d612d66616b652121</AuthToken>
        <SenderNumber>15558675309</SenderNumber>
        <StaticRecipients name="0">5555555555</StaticRecipients>
        <StaticRecipients name="1">5551234567</StaticRecipients>
      </TwilioSMS>
    </Notifiers>
  </Notify>
</truxton_options>

Topics

The information which is sent through a notifier. Configurations for each Topic is nested in a "Topics" block. Most topics DO NOT need to have configurations unless you plan on omitting certain topics across different notifiers - like if you didn't want the Alert topic to get sent via TwilioSMS. This means each topic is "opt-out". If you have no extra configuration for topics you can safely omit all "Topics" configurations.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Topics>
      <!-- 
          individual topic configs here 
        -->
    </Topics>
  </Notify>
</truxton_options>

The Notify ETL contains the following topics out of the box:

Alert

This topic contains relevant information about any triggered alerts on a media if applicable. If the media did not trigger an alert, this topic will not be sent.

Configurations for this topic are nested in an "Alert" block.

Name Data Type Description
DisabledNotifiers string[] A list notifiers which will NOT send this topic. The name of the notifier should name the configuration block name mentioned for each individual notifier. Optional.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Topics>
      <Alert>
        <DisabledNotifiers name="0">Email</DisabledNotifiers>
        <DisabledNotifiers name="1">SendGrid</DisabledNotifiers>
      </Alert>
    </Topics>
  </Notify>
</truxton_options>

Analyst Load Info

This topic contains relevant information about a completed load from the role of an analyst

Configurations for this topic are nested in an "AnalystLoadInfo" block.

Name Data Type Description
DisabledNotifiers string[] A list notifiers which will NOT send this topic. The name of the notifier should name the configuration block name mentioned for each individual notifier. Optional.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Topics>
      <AnalystLoadInfo>
        <DisabledNotifiers name="0">TwilioSMS</DisabledNotifiers>
        <DisabledNotifiers name="1">Slack</DisabledNotifiers>
      </AnalystLoadInfo>
    </Topics>
  </Notify>
</truxton_options>

Loader Load Info

This topic contains relevant information about a completed load from the role of an data loader. It tends to be more technical in nature.

Configurations for this topic are nested in an "LoaderLoadInfo" block.

Name Data Type Description
DisabledNotifiers string[] A list notifiers which will NOT send this topic. The name of the notifier should name the configuration block name mentioned for each individual notifier. Optional.

E.G. for TruxtonSettings.xml

<truxton_options>
  <!-- ...
       other configs
       ...
    -->
  <Notify>  
    <Topics>
      <LoaderLoadInfo>
        <DisabledNotifiers name="0">MSTeams</DisabledNotifiers>
      </LoaderLoadInfo>
    </Topics>
  </Notify>
</truxton_options>

Plugins

Notify supports a plugin architecture to add additional Notifiers and Topics. See the .NET Core SDK articles to how to add a reference to the SDK. The nuget package thats relevant is Truxton.Notifications.Core - this package contains all the necessary transitive SDK dependencies.

Notification SDK

Truxton.Notifications.Core contains several interfaces and relevant classes to allow integration into the Notify ETL. Since this ETL is part of the Truxton dependency injection, you can inject anything already registered in the ServiceCollection. See the Dependency Injection section for relevant interfaces.

The two main plugin interfaces types are as follows:

/// <summary>
/// A plugin for notifiers
/// </summary>
[Description("Truxton Notifier Plugin")]
public interface ITruxtonNotifierPlugin : IInjectableTruxtonPlugin
{
}

/// <summary>
/// A plugin for topics
/// </summary>
[Description("Truxton Notification Topic Plugin")]
public interface ITruxtonTopicPlugin : IInjectableTruxtonPlugin
{
}

These interfaces depend on the following interfaces

/// <summary>
/// A plugin which can register dependencies via Microsoft.Extensions.DependencyInjection
/// </summary>
public interface IInjectableTruxtonPlugin : ITruxtonPlugin, IConfigureServices { }

/// <summary>
/// Indicates this class register dependencies via Microsoft.Extensions.DependencyInjection
/// </summary>
public interface IConfigureServices
{
    /// <summary>
    /// Registers necessary dependencies from an existing IServiceCollection
    /// </summary>
    /// <param name="environment"></param>
    /// <param name="configuration"></param>
    /// <param name="services"></param>
    void ConfigureServices(ITruxtonEnvironment environment,
            IConfiguration configuration,
            IServiceCollection services);
}

/// <summary>
/// A generic truxton plugin
/// </summary>
[Description("Generic Truxton Plugin")]
public interface ITruxtonPlugin
{
    /// <summary>
    /// The name of this plugin
    /// </summary>
    string Name { get; }

    /// <summary>
    /// The description of this plugin
    /// </summary>
    string Description { get; }

    /// <summary>
    /// The type information of this plugin
    /// </summary>
    Type PluginType { get; }

    /// <summary>
    /// Indicates this plugin has global configuration that persists between being loaded
    /// </summary>
    bool CanConfigure { get; }

    /// <summary>
    /// Indicates this plugin has been disabled either by a user, missing configuration or other means.
    /// </summary>
    bool IsDisabled { get; }
}

These plugin types merely allow your notifiers and topics to be discovered by the plugin system and then have any dependencies registered. The real implementation is in the form of the following interfaces also available in Truxton.Notifications.Core:

/// <summary>
/// Handles notifications over a specific protocol
/// </summary>
public interface INotifier
{
    /// <summary>
    /// The name of this Notifier.
    /// </summary>
    string Name { get; }

    /// <summary>
    /// True this notifier is disabled, false otherwise
    /// </summary>
    bool Disabled { get; }

    /// <summary>
    /// The media to send notifications about
    /// </summary>
    /// <param name="messages">Messages to send</param>
    /// <param name="ct">A token to signal cancellation of the operation</param>
    /// <returns>True if successful, false otherwise</returns>
    Task<bool> SendAsync(IList<NotificationMessage> messages, CancellationToken ct);
}

/// <summary>
/// A topic which has specific configurations
/// </summary>
/// <typeparam name="TTopicConfig"></typeparam>
public interface ITopic<TTopicConfig> : ITopic
       where TTopicConfig : ITopicConfig
{
    /// <summary>
    /// The specific implementation of this config
    /// </summary>
    TTopicConfig SpecificConfig { get; }
}

/// <summary>
/// A topic of interest
/// </summary>
public interface ITopic
{
    /// <summary>
    /// The name of the topic
    /// </summary>
    string Name { get; }

    /// <summary>
    /// Configuration for this topic
    /// </summary>
    ITopicConfig Config { get; }

    /// <summary>
    /// List all messages related to the media for this topic
    /// </summary>
    /// <param name="mediaID">A media ID</param>
    /// <param name="ct">A token to signal cancellation</param>
    /// <returns>A list of notification messages</returns>
    Task<IList<NotificationMessage>> ListAsync(Guid mediaID, CancellationToken ct);
}

These main interfaces depend on the following interfaces and classes:

/// <summary>
/// A notification message
/// </summary>
public class NotificationMessage
{
    /// <summary>
    /// Who to send this message to
    /// </summary>
    public IList<string> To { get; }

    /// <summary>
    /// The subject of this message
    /// </summary>
    public string Subject { get; set; }

    /// <summary>
    /// A list of bodies to send
    /// </summary>
    public List<NotificationBody> Bodies { get; set; }

    /// <summary>
    /// A list of attachements to send
    /// </summary>
    public List<NotificationAttachment> Attachments { get; }
}

/// <summary>
/// Indicates a type of body to send
/// </summary>
public enum BodyTypes
{
    Unknown,
    Text,
    HTML,
    Markdown,
    PDF,
    Docx,
    RTF,
    Binary,
    Other
}

/// <summary>
/// A body of a notification
/// </summary>
public class NotificationBody
{
    /// <summary>
    /// The type of body
    /// </summary>
    public BodyTypes Type { get; set; }

    /// <summary>
    /// The text of this body
    /// </summary>
    public string Text { get; set; }
}

/// <summary>
/// An attachment for a notification
/// </summary>
public class NotificationAttachment
{
    /// <summary>
    /// The name of the attachment
    /// </summary>
    public string Filename { get; set; }

    /// <summary>
    /// The MIME of this attachment
    /// </summary>
    public string MIME { get; set; }

    /// <summary>
    /// The content of this attachment
    /// </summary>
    public byte[] Content { get; set; }
}

/// <summary>
/// Topic configuration
/// </summary>
public interface ITopicConfig
{
    /// <summary>
    /// Indicates which notifiers will be ignored for this topic
    /// The string value should be the Name of the notifier
    /// </summary>
    List<string> DisabledNotifiers { get; set; }
}

Notifier Example

Putting all the interfaces together here is an example of a custom notifier:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Truxton;
using Truxton.Notifications.Core;

namespace MyNotifier
{
    public class FooBarNotifierPlugin : ITruxtonNotifierPlugin
    {
        public const string CONFIG_SECTION_NAME = "Notify:Notifiers:FooBar";

        public string Name => "Foo Bar Notifier";

        public string Description => "Send notifications via Foo Bar";

        public Type PluginType => typeof(ITruxtonNotifierPlugin);

        public bool CanConfigure => false;

        public bool IsDisabled => false;

        public void ConfigureServices(ITruxtonEnvironment environment,
            IConfiguration configuration,
            IServiceCollection services)
        {
            var config = configuration.GetSection(CONFIG_SECTION_NAME);
            services.Configure<FooBarConfig>(config);
            services.AddSingleton<INotifier, FooBarNotifier>();
        }
    }

    public class FooBarConfig
    {
        /// <summary>
        /// This config must be set in order for the foo bar notifier to be enabled
        /// </summary>
        public string RequiredConfigThing { get; set; }
    }

    public class FooBarNotifier : INotifier
    {
        public string Name => "Foo Bar";

        public bool Disabled { get; }


        readonly FooBarConfig _config;
        readonly ILogger _logger;

        public FooBarNotifier(IOptions<FooBarConfig> options,
            ILogger<FooBarNotifier> logger)
        {
            _logger = logger;

            try
            {
                _config = options.Value;
            }
            catch (Exception ex)
            {
                _logger.LogError($"There was a problem parsing the config: {FooBarNotifierPlugin.CONFIG_SECTION_NAME}. Exception follows:");
                _logger.LogError(ex.ToString());
                _config = new FooBarConfig();
            }

            Disabled = string.IsNullOrWhiteSpace(_config.RequiredConfigThing);
            if (Disabled)
            {
                _logger.LogInformation($"Did not find or correctly parse the config: {FooBarNotifierPlugin.CONFIG_SECTION_NAME}. FooBar notifications are disabled.");
            }
            else
            {
                _logger.LogInformation($"RequiredConfigThing found for {FooBarNotifierPlugin.CONFIG_SECTION_NAME}. Will attempt to send Foo Bar notifications");
            }
        }

        public Task<bool> SendAsync(IList<NotificationMessage> messages,
            CancellationToken ct)
        {
            ///send the messages!

            return Task.FromResult(true);
        }
    }
}

Topic Example

And here is an example of a custom Topic:

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Truxton;
using Truxton.Notifications.Core;

namespace MyTopic
{
    public class FizzBuzzTopicPlugin : ITruxtonTopicPlugin
    {
        public const string CONFIG_SECTION_NAME = "Notify:Topics:FizzBuzz";

        public string Name => "Fizz Buzz Topic";

        public string Description => "A notification topic all about Fizz and Buzz";

        public Type PluginType => typeof(ITruxtonTopicPlugin);

        public bool CanConfigure => false;

        public bool IsDisabled => false;

        public void ConfigureServices(ITruxtonEnvironment environment, 
            IConfiguration configuration, 
            IServiceCollection services)
        {
            var config = configuration.GetSection(CONFIG_SECTION_NAME);
            services.Configure<FizzBuzzTopicConfig>(config);
            services.AddSingleton<ITopic, FizzBuzzTopic>();
        }
    }

    public class FizzBuzzTopicConfig : TopicConfig { }

    public class FizzBuzzTopic : ITopic<FizzBuzzTopicConfig>
    {
        public FizzBuzzTopicConfig SpecificConfig { get; }

        public string Name => "FizzBuzz";

        public ITopicConfig Config => SpecificConfig;
        
        readonly ITruxtonDatase _truxtonDatabase;
        readonly ILogger _logger;

        public FizzBuzzTopic(IOptions<FizzBuzzTopicConfig> options,
            ILogger<FizzBuzzTopic> logger,
            ITruxtonDatabase truxtonDatabase)
        {
            try
            {
                SpecificConfig = options.Value;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.ToString());
                SpecificConfig = new FizzBuzzTopicConfig();
            }

            _logger = logger;
            _truxtonDatabase = truxtonDatabase;

            _logger.LogInformation($"Added topic: {Name}. {(Config.DisabledNotifiers.Any() ? $"This topic has disabled the following notifiers: {string.Join(", ", Config.DisabledNotifiers)}." : "All notifiers enabled.")}");
        }

        public Task<IList<NotificationMessage>> ListAsync(Guid mediaID, CancellationToken ct)
        {
            //Make your messages by retrieving data from _truxtonDatabase.
            IList<NotificationMessage> messages = new List<NotificationMessage>();
            return Task.FromResult(messages);
        }
    }
}

Plugin Scan Directory

Notify will scan C:\ProgramData\Truxton\Plugins when it starts up for plugins (if the plugin folder does not exist go ahead and create one).

The folder structure for a plugin must be as follows:

  • C:\ProgramData\Truxton\Plugins
    • \PLUGIN_ASSEMBLY_NAME
      • \PLUGIN_ASSEMBLY_NAME.dll
      • (any additional dependencies)

Please take note of the the nested directory. This is so every assembly doesn't need to scanned on startup. So as an example, if my plugin was foobar.dll, you would place it here: C:\ProgramData\Truxton\Plugins\foobar\foobar.dll. Any additional dependencies that foobar.dll needs will go into that directory.