Notify
| 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.
Contents
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:
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:
- Sender Phone Number - Click on the "See all phone numbers" link
- Account SID - You can get this from the
ACCOUNT SIDfield - Auth Token - Expand the
AUTH TOKENby 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.