smartnotify2 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +54 -0
- data/lib/config/config.yml +1 -0
- data/lib/modules/rotation_emailer/README.md +31 -0
- data/lib/modules/rotation_emailer/rotation_emailer.rb +120 -0
- data/lib/modules/slack_notifier/README.md +0 -0
- data/lib/modules/slack_notifier/slack_notifier.rb +62 -0
- data/lib/smartnotify.rb +99 -0
- metadata +92 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e687c007b40ebd3e8771c1fb9cf437bdfad4987d92c427d2bdc093d195ee10d1
|
4
|
+
data.tar.gz: eefb144a2d6eaf3a065ac8139dea554221541670a26348e9cde5ef6421e4c12a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9c37becbabaf10e56fd0d526685faaeec249c90c9bcbcfc0c10d4bba4ab0501ea6b40d9cc465479c20f1ca966a2062b1a9346655f9bae85b58e32629687f4a12
|
7
|
+
data.tar.gz: c60957633ede9af4f60ce2c43db426c6c88ebe2477eb012661d0ddce476fd1b30eba07a07a163a9d7196a10ef28f345dfef3fdba54dd8e64f3113e1cb3e07bdf
|
data/README.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# SmartNotify
|
2
|
+
|
3
|
+
Ruby Gem designed for sending various types of notifications via a common configurable interface.
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
The gem makes use of two components, modules, and templates.
|
7
|
+
|
8
|
+
Modules are defined within this gem, and provide various forms of generic notification functionality. The modules are created to be reusable. A module could be responsible for sending emails, posting Slack notifications, making a REST call to an external API, whatever.
|
9
|
+
|
10
|
+
Templates on the other hand, consume a specific module, in conjunction with template specific configuration, creating very specific use cases. These use cases could be team specific, allowing different consumers to tweak who, and how notifications are distributed.
|
11
|
+
|
12
|
+
## Why
|
13
|
+
There was a need to easily send SLA incident emails to the correct person on support, as well as to a MS Teams channel. A module was created `rotation_emailer` which could send emails, based on a given email template, and support rotation csv file. With that in place, a template was created `incident_start_email` which defines where the specific email template, and support rotation csv file is located for this particular instance. This is awesome, because a new team or solution could create a template, changing only the support rotation csv location, and get the same functionality for next to no setup cost.
|
14
|
+
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
Add smartnotify to your Gemfile, pointing at GitHub.
|
18
|
+
```
|
19
|
+
gem 'smartnotify', git: 'https://github.cerner.com/PHPerfImp/smartnotify.git', branch: 'master'
|
20
|
+
```
|
21
|
+
|
22
|
+
Within your code add:
|
23
|
+
```ruby
|
24
|
+
require 'smartnotify'
|
25
|
+
# ... your logic ...
|
26
|
+
smartnotify = SmartNotify.new
|
27
|
+
smartnotify.run(template, json, [debug], [dryrun])
|
28
|
+
```
|
29
|
+
|
30
|
+
*Note: when updates are made to the gem in github, bundle update & bundle install must be run to pull in the changes.
|
31
|
+
|
32
|
+
| Param | Type | Requirement | Default | Description |
|
33
|
+
| ---- | ---- | ----------- | ------- | --------------- |
|
34
|
+
| template | String | Required | | The id of the template to use. The templates are configured externally in [PHPerfImp/smartnotify-config](https://github.cerner.com/PHPerfImp/smartnotify-config) |
|
35
|
+
| json | String | Required | | The JSON key/value pairs to be used as input to the selected template. Each template defines what keys must be provided. |
|
36
|
+
| environment | String | Optional | production | The id of the environment configuration to use. Choices are local, development, staging, and production. |
|
37
|
+
| debug | Boolean | Optional | false | Enables debug logging within SmartNotify. |
|
38
|
+
| dryrun | Boolean | Optional | false | Enables debug logging within SmartNotify, and disables sending any notifications for testing purposes. |
|
39
|
+
|
40
|
+
### Example
|
41
|
+
```ruby
|
42
|
+
smartnotify = SmartNotify.new
|
43
|
+
smartnotify.run('incident_start_email', "{\"mnemonic\": \"sbmcin\", \"cerner_mnemonic\": \"UNVR_NY\", \"link\": \"https://jira2.cerner.com/browse/PERFIMPDEV-1349\", \"title\": \"Readmission SLA Exceeded\", \"message\": \"Readmission SLA exceeded, please check the JIRA for more information.\"}", 'production')
|
44
|
+
```
|
45
|
+
|
46
|
+
## Modules
|
47
|
+
The following are the currently available modules that can be consumed. Follow the link to get more information on
|
48
|
+
intended usage, and configuration.
|
49
|
+
|
50
|
+
* [Rotation Emailer](modules/roatation_emailer/README.md)
|
51
|
+
|
52
|
+
## Releasing New Version
|
53
|
+
|
54
|
+
*N/A - As long as we are using our internal GitHub for storage of the gem, there is no need to 'release' a new version. Using a different version of the branch can be achieved by pointing the Gemfile reference at a different ref. See http://bundler.io/v1.12/git.html for more information.
|
@@ -0,0 +1 @@
|
|
1
|
+
external-config-path: 'https://raw.github.cerner.com/PHPerfImp/smartnotify-config/master/config/config.yml'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Module - Rotation Emailer
|
2
|
+
|
3
|
+
**Module ID:** `rotation_emailer`
|
4
|
+
|
5
|
+
## Required Configuration
|
6
|
+
|
7
|
+
* *email_template* - A URL pointed to the template of the email message to send. This document should be a .erb file.
|
8
|
+
The data is fed into the template from the provided json, as a hash. *e.g accessing a title key would look like
|
9
|
+
`<%= data['title'] %>`*
|
10
|
+
|
11
|
+
* *support_rotation* - A URL pointed to a CSV file containing email addresses, combined with start/end times for
|
12
|
+
when the given address is valid to email. On each run, the file will be parsed looking for emails that are valid at that
|
13
|
+
time and will send the message to all the addresses as one group email. At a minimum, the following three columns
|
14
|
+
with headers must be specified:
|
15
|
+
|
16
|
+
* `Start Time` - Formatted as 12/Aug/2017. This is the date, starting at the beginning of the day, that the email
|
17
|
+
address can start being emailed.
|
18
|
+
* `End Time` - Formatted as 18/Aug/2017. This is the date, ending at midnight, that the email address is no longer
|
19
|
+
valid to notify after.
|
20
|
+
* `Email Address` - Email address to notify.
|
21
|
+
|
22
|
+
*Note:*
|
23
|
+
* *All times a calculated with the configured timezone of Central Time (US & Canada)*
|
24
|
+
* *Column headers are case sensitive.*
|
25
|
+
|
26
|
+
## Required JSON keys
|
27
|
+
While most of the JSON key/value pairs are defined based on what
|
28
|
+
the template needs, this module requires that a `title` JSON key
|
29
|
+
be provided which is used as the subject of the email. If no title
|
30
|
+
key is defined, a fallback *'Rotation Emailer Notification'* title
|
31
|
+
will be used as the email subject instead.
|
@@ -0,0 +1,120 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
require 'yaml'
|
5
|
+
require 'csv'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'logging'
|
8
|
+
|
9
|
+
class NotificationModule
|
10
|
+
|
11
|
+
def initialize(config)
|
12
|
+
@config = config
|
13
|
+
@log = Logging.logger['rotation_emailer']
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
|
18
|
+
c = @config
|
19
|
+
|
20
|
+
@log.info ''
|
21
|
+
@log.info 'Starting rotation_emailer.rb logic...'
|
22
|
+
|
23
|
+
# Setup Mailer
|
24
|
+
@log.info ''
|
25
|
+
@log.info 'Setting up Mail.defaults with the following configuration:'
|
26
|
+
@log.info " host: #{c[:global]['smtp_host']}"
|
27
|
+
@log.info " port: #{c[:global]['smtp_port']}"
|
28
|
+
|
29
|
+
|
30
|
+
Mail.defaults do
|
31
|
+
delivery_method :smtp, {
|
32
|
+
:address => c[:global]['smtp_host'],
|
33
|
+
:port => c[:global]['smtp_port']
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load Email Addresses
|
38
|
+
|
39
|
+
send_list = c[:config]['always_email']
|
40
|
+
|
41
|
+
unless c[:config]['support_rotation'].nil?
|
42
|
+
@log.info ''
|
43
|
+
@log.info 'Loading support rotation from:'
|
44
|
+
@log.info " #{c[:config]['support_rotation']}"
|
45
|
+
CSV.new(open(c[:config]['support_rotation']), :headers => :first_row).each do |email|
|
46
|
+
send_list.push("#{email['Email Address']}") if active_email(email, c[:global]['timezone'])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
send_list.uniq!
|
51
|
+
|
52
|
+
if send_list.empty?
|
53
|
+
@log.error 'No email addresses are setup for the current time.'
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
@log.info ''
|
58
|
+
@log.info 'Found the following email addresses to notify:'
|
59
|
+
|
60
|
+
send_list.each do |recipient|
|
61
|
+
@log.info " #{recipient}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# Load Template
|
65
|
+
@log.info ''
|
66
|
+
@log.info 'Loading email template from:'
|
67
|
+
@log.info " #{c[:config]['email_template']}"
|
68
|
+
|
69
|
+
email_template = open(c[:config]['email_template']).read
|
70
|
+
|
71
|
+
# Populate Template
|
72
|
+
b = binding
|
73
|
+
b.local_variable_set(:data, c[:data])
|
74
|
+
message = ERB.new(email_template).result(b)
|
75
|
+
|
76
|
+
if c[:global]['dryrun']
|
77
|
+
@log.info ''
|
78
|
+
@log.info 'Not sending email because dryrun is enabled.'
|
79
|
+
@log.info 'Would have emailed the following message:'
|
80
|
+
@log.info ''
|
81
|
+
@log.info "SUBJECT: #{c[:data]['title']}"
|
82
|
+
@log.info ''
|
83
|
+
@log.info '--- MESSAGE START ---'
|
84
|
+
message.each_line do |line|
|
85
|
+
@log.info " #{line.chomp}"
|
86
|
+
end
|
87
|
+
@log.info '--- MESSAGE END ---'
|
88
|
+
else
|
89
|
+
# Send The Emails
|
90
|
+
@log.info ''
|
91
|
+
@log.info 'Sending Emails...'
|
92
|
+
|
93
|
+
title = c[:data]['title'].nil? ? 'Rotation Emailer Notification' : c[:data]['title']
|
94
|
+
send_email(title, c[:global]['smtp_from'], send_list, message)
|
95
|
+
end
|
96
|
+
@log.info ''
|
97
|
+
@log.info ' Finished rotation_emailer.rb logic.'
|
98
|
+
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def active_email(email, tz)
|
103
|
+
start_time = DateTime.parse(email['Start Time'] + ' ' + tz)
|
104
|
+
end_time = DateTime.parse(email['End Time'] + ' ' + tz) + 1
|
105
|
+
current_time = DateTime.now
|
106
|
+
start_time <= current_time and end_time > current_time
|
107
|
+
end
|
108
|
+
|
109
|
+
def send_email(subject_text, from_email, to_email, body_text)
|
110
|
+
Mail.deliver do
|
111
|
+
to to_email
|
112
|
+
from from_email
|
113
|
+
subject subject_text
|
114
|
+
html_part do
|
115
|
+
content_type 'text/html; charset=UTF-8'
|
116
|
+
body body_text
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
File without changes
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
require 'yaml'
|
5
|
+
require 'csv'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'logging'
|
8
|
+
require 'slack-notifier'
|
9
|
+
|
10
|
+
class NotificationModule
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@config = config
|
14
|
+
@log = Logging.logger['slack_notifier']
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
|
19
|
+
c = @config
|
20
|
+
|
21
|
+
@log.info ''
|
22
|
+
@log.info 'Starting slack_notifier.rb logic...'
|
23
|
+
|
24
|
+
# Load Template
|
25
|
+
@log.info ''
|
26
|
+
@log.info 'Loading message template from:'
|
27
|
+
@log.info " #{c[:config]['message_template']}"
|
28
|
+
|
29
|
+
message_template = open(c[:config]['message_template']).read
|
30
|
+
|
31
|
+
# Populate Template
|
32
|
+
b = binding
|
33
|
+
b.local_variable_set(:data, c[:data])
|
34
|
+
message = ERB.new(message_template).result(b)
|
35
|
+
|
36
|
+
if c[:global]['dryrun']
|
37
|
+
@log.info ''
|
38
|
+
@log.info 'Not sending slack message because dryrun is enabled.'
|
39
|
+
@log.info 'Would have posted the following message:'
|
40
|
+
@log.info ''
|
41
|
+
@log.info '--- MESSAGE START ---'
|
42
|
+
message.each_line do |line|
|
43
|
+
@log.info " #{line.chomp}"
|
44
|
+
end
|
45
|
+
@log.info '--- MESSAGE END ---'
|
46
|
+
else
|
47
|
+
# Send The Emails
|
48
|
+
@log.info ''
|
49
|
+
@log.info 'Posting Message to Slack...'
|
50
|
+
@log.info " Channel: #{c[:config]['channel']} "
|
51
|
+
@log.info " Username: #{c[:config]['username']} "
|
52
|
+
|
53
|
+
notifier = Slack::Notifier.new "#{c[:config]['webhook']}" do
|
54
|
+
defaults channel: "#{c[:config]['channel']}",
|
55
|
+
username: "#{c[:config]['username']}"
|
56
|
+
end
|
57
|
+
notifier.ping message
|
58
|
+
end
|
59
|
+
@log.info ''
|
60
|
+
@log.info ' Finished slack_notifier.rb logic.'
|
61
|
+
end
|
62
|
+
end
|
data/lib/smartnotify.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'logging'
|
6
|
+
|
7
|
+
class SmartNotify
|
8
|
+
attr_accessor :config
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
Logging.logger.root.appenders = Logging.appenders.stdout
|
12
|
+
Logging.logger.root.level = :debug
|
13
|
+
@log = Logging.logger['smartnotify']
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(template, json, env = 'production', debug = false, dryrun = false)
|
17
|
+
|
18
|
+
# Ensure Options Provided
|
19
|
+
if json.nil? || template.nil? || env.nil?
|
20
|
+
@log.error 'Must provide template, JSON data, and environment name.'
|
21
|
+
return false
|
22
|
+
end
|
23
|
+
|
24
|
+
# Load Configuration
|
25
|
+
internal_config = YAML.load_file(File.join(__dir__, 'config/config.yml'))
|
26
|
+
@log.info 'Loading SmartNotify config from:'
|
27
|
+
@log.info " #{internal_config['external-config-path']}"
|
28
|
+
|
29
|
+
external_config = nil
|
30
|
+
begin
|
31
|
+
external_config = YAML.load(open(internal_config['external-config-path']).read)
|
32
|
+
rescue
|
33
|
+
@log.error 'Unable to load and parse configuration.'
|
34
|
+
@log.error 'Check that the file exists, and that it is valid YMAL.'
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
# Load Environment Configuration
|
39
|
+
env = env.downcase
|
40
|
+
|
41
|
+
@log.info ''
|
42
|
+
@log.info "SmartNotify Environment: #{env}"
|
43
|
+
|
44
|
+
config = external_config[env]
|
45
|
+
if config.nil?
|
46
|
+
@log.error 'Configuration does not contain configuration for specified environment.'
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
|
50
|
+
# Load Template Configuration
|
51
|
+
template_config = config[template]
|
52
|
+
if template_config.nil?
|
53
|
+
@log.error "Template '#{template}' not found."
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Configure Development Flags
|
58
|
+
config['global']['dryrun'] = dryrun
|
59
|
+
|
60
|
+
|
61
|
+
# Load JSON
|
62
|
+
begin
|
63
|
+
template_data = JSON.parse(json)
|
64
|
+
rescue JSON::ParserError => e
|
65
|
+
puts 'ERROR: JSON provided is unable to be parsed.'
|
66
|
+
return false
|
67
|
+
end
|
68
|
+
|
69
|
+
# Prepare Module Configuration
|
70
|
+
module_config = Hash.new
|
71
|
+
module_config[:config] = template_config
|
72
|
+
module_config[:data] = template_data
|
73
|
+
module_config[:global] = config['global']
|
74
|
+
|
75
|
+
# Ensure Required Fields Present
|
76
|
+
unless module_config[:config]['required_fields'].nil?
|
77
|
+
module_config[:config]['required_fields'].each do |field|
|
78
|
+
if module_config[:data][field].nil?
|
79
|
+
@log.error "JSON provided is missing required field '#{field}'"
|
80
|
+
return false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if module_config[:config]['module'] == 'rotation_emailer'
|
86
|
+
require_relative 'modules/rotation_emailer/rotation_emailer'
|
87
|
+
elsif module_config[:config]['module'] == 'slack_notifier'
|
88
|
+
require_relative 'modules/slack_notifier/slack_notifier'
|
89
|
+
else
|
90
|
+
@log.error "'#{module_config[:config]['module']}' module does not exist."
|
91
|
+
@log.error 'Ensure the correct module name is defined in the configuration.'
|
92
|
+
return false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Run Module
|
96
|
+
NotificationModule.new(module_config).run
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smartnotify2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Performance Improvement Dev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mail
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: logging
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: slack-notifier
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.3'
|
55
|
+
description: Utility for sending various types of notifications via a common configurable
|
56
|
+
interface.
|
57
|
+
email:
|
58
|
+
- kristopher.williams@cerner.com
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- README.md
|
64
|
+
- lib/config/config.yml
|
65
|
+
- lib/modules/rotation_emailer/README.md
|
66
|
+
- lib/modules/rotation_emailer/rotation_emailer.rb
|
67
|
+
- lib/modules/slack_notifier/README.md
|
68
|
+
- lib/modules/slack_notifier/slack_notifier.rb
|
69
|
+
- lib/smartnotify.rb
|
70
|
+
homepage: https://github.cerner.com/PHPerfImp/smartnotify
|
71
|
+
licenses: []
|
72
|
+
metadata: {}
|
73
|
+
post_install_message:
|
74
|
+
rdoc_options: []
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
requirements: []
|
88
|
+
rubygems_version: 3.0.3
|
89
|
+
signing_key:
|
90
|
+
specification_version: 4
|
91
|
+
summary: Common interface notification utility
|
92
|
+
test_files: []
|