slack-notifier 1.5.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/slack-notifier.rb +35 -60
- data/lib/slack-notifier/config.rb +37 -0
- data/lib/slack-notifier/payload_middleware.rb +21 -0
- data/lib/slack-notifier/payload_middleware/base.rb +35 -0
- data/lib/slack-notifier/payload_middleware/format_attachments.rb +37 -0
- data/lib/slack-notifier/payload_middleware/format_message.rb +19 -0
- data/lib/slack-notifier/payload_middleware/stack.rb +35 -0
- data/lib/slack-notifier/util/escape.rb +15 -0
- data/lib/slack-notifier/util/http_client.rb +53 -0
- data/lib/slack-notifier/util/link_formatter.rb +63 -0
- data/lib/slack-notifier/version.rb +2 -1
- data/spec/end_to_end_spec.rb +84 -0
- data/spec/integration/ping_integration_test.rb +12 -5
- data/spec/lib/slack-notifier/config_spec.rb +71 -0
- data/spec/lib/slack-notifier/payload_middleware/base_spec.rb +75 -0
- data/spec/lib/slack-notifier/payload_middleware/format_attachments_spec.rb +35 -0
- data/spec/lib/slack-notifier/payload_middleware/format_message_spec.rb +26 -0
- data/spec/lib/slack-notifier/payload_middleware/stack_spec.rb +93 -0
- data/spec/lib/slack-notifier/payload_middleware_spec.rb +32 -0
- data/spec/lib/slack-notifier/util/http_client_spec.rb +34 -0
- data/spec/lib/slack-notifier/{link_formatter_spec.rb → util/link_formatter_spec.rb} +30 -19
- data/spec/lib/slack-notifier_spec.rb +62 -128
- data/spec/spec_helper.rb +20 -5
- metadata +30 -9
- data/lib/slack-notifier/default_http_client.rb +0 -51
- data/lib/slack-notifier/link_formatter.rb +0 -62
- data/spec/lib/slack-notifier/default_http_client_spec.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47af99d5a1eeca46a62425be8842bc6ff89d9d51
|
4
|
+
data.tar.gz: 3d0519d10cb7a251a49b32b0d27b2e0616f08c54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d9a915eb0ebc25b3d1d4042055419cf8628404cf9282fffa41a4f4dfada1eb8aac4c02c9b7415226895a160ee326f0c82d048fa84ede13f829dac19130b2cc8
|
7
|
+
data.tar.gz: 314a2d7812c2d699f201753e15fbcd6c34c778f863123f2e70e7731d8753a338e984bbcb3dd4cd125f01509990b79fc84ac8a3b7daf5b2e70f5484c66cede356
|
data/lib/slack-notifier.rb
CHANGED
@@ -1,81 +1,56 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "uri"
|
3
|
+
require "json"
|
4
4
|
|
5
|
-
require_relative
|
6
|
-
require_relative
|
5
|
+
require_relative "slack-notifier/util/http_client"
|
6
|
+
require_relative "slack-notifier/util/link_formatter"
|
7
|
+
require_relative "slack-notifier/util/escape"
|
8
|
+
require_relative "slack-notifier/payload_middleware"
|
9
|
+
require_relative "slack-notifier/config"
|
7
10
|
|
8
11
|
module Slack
|
9
12
|
class Notifier
|
10
|
-
attr_reader :endpoint
|
13
|
+
attr_reader :endpoint
|
11
14
|
|
12
|
-
def initialize webhook_url, options={}
|
13
|
-
@endpoint
|
14
|
-
@default_payload = options
|
15
|
-
end
|
16
|
-
|
17
|
-
def ping message, options={}
|
18
|
-
if message.is_a?(Hash)
|
19
|
-
message, options = nil, message
|
20
|
-
end
|
21
|
-
|
22
|
-
if attachments = options[:attachments] || options["attachments"]
|
23
|
-
wrap_array(attachments).each do |attachment|
|
24
|
-
["text", :text].each do |key|
|
25
|
-
attachment[key] = LinkFormatter.format(attachment[key]) if attachment.has_key?(key)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
payload = default_payload.merge(options)
|
31
|
-
client = payload.delete(:http_client) || http_client
|
32
|
-
http_options = payload.delete(:http_options)
|
33
|
-
|
34
|
-
unless message.nil?
|
35
|
-
payload.merge!(text: LinkFormatter.format(message))
|
36
|
-
end
|
15
|
+
def initialize webhook_url, options={}, &block
|
16
|
+
@endpoint = URI.parse webhook_url
|
37
17
|
|
38
|
-
|
39
|
-
|
18
|
+
config.http_client(options.delete(:http_client)) if options.key?(:http_client)
|
19
|
+
config.defaults options
|
20
|
+
config.instance_exec(&block) if block_given?
|
40
21
|
|
41
|
-
|
22
|
+
middleware.set config.middleware
|
42
23
|
end
|
43
24
|
|
44
|
-
def
|
45
|
-
|
25
|
+
def config
|
26
|
+
@_config ||= Config.new
|
46
27
|
end
|
47
28
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
29
|
+
def ping message, options={}
|
30
|
+
if message.is_a?(Hash)
|
31
|
+
options = message
|
32
|
+
else
|
33
|
+
options[:text] = message
|
34
|
+
end
|
51
35
|
|
52
|
-
|
53
|
-
default_payload[:channel] = channel
|
36
|
+
post options
|
54
37
|
end
|
55
38
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
39
|
+
def post payload={}
|
40
|
+
params = {}
|
41
|
+
client = payload.delete(:http_client) || config.http_client
|
42
|
+
payload = config.defaults.merge(payload)
|
59
43
|
|
60
|
-
|
61
|
-
|
62
|
-
end
|
44
|
+
params[:http_options] = payload.delete(:http_options) if payload.key?(:http_options)
|
45
|
+
params[:payload] = middleware.call(payload).to_json
|
63
46
|
|
64
|
-
|
65
|
-
HTML_ESCAPE = { '&' => '&', '>' => '>', '<' => '<' }
|
66
|
-
|
67
|
-
def escape(text)
|
68
|
-
text.gsub(HTML_ESCAPE_REGEXP, HTML_ESCAPE)
|
47
|
+
client.post endpoint, params
|
69
48
|
end
|
70
49
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
object.to_ary || [object]
|
76
|
-
else
|
77
|
-
[object]
|
50
|
+
private
|
51
|
+
|
52
|
+
def middleware
|
53
|
+
@middleware ||= PayloadMiddleware::Stack.new(self)
|
78
54
|
end
|
79
|
-
end
|
80
55
|
end
|
81
56
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class Config
|
5
|
+
def initialize
|
6
|
+
@http_client = Util::HTTPClient
|
7
|
+
@defaults = {}
|
8
|
+
@middleware = [:format_message, :format_attachments]
|
9
|
+
end
|
10
|
+
|
11
|
+
def http_client client=nil
|
12
|
+
return @http_client if client.nil?
|
13
|
+
raise ArgumentError, "the http client must respond to ::post" unless client.respond_to?(:post)
|
14
|
+
|
15
|
+
@http_client = client
|
16
|
+
end
|
17
|
+
|
18
|
+
def defaults new_defaults=nil
|
19
|
+
return @defaults if new_defaults.nil?
|
20
|
+
raise ArgumentError, "the defaults must be a Hash" unless new_defaults.is_a?(Hash)
|
21
|
+
|
22
|
+
@defaults = new_defaults
|
23
|
+
end
|
24
|
+
|
25
|
+
def middleware *args
|
26
|
+
return @middleware if args.empty?
|
27
|
+
|
28
|
+
@middleware =
|
29
|
+
if args.length == 1 && args.first.is_a?(Array) || args.first.is_a?(Hash)
|
30
|
+
args.first
|
31
|
+
else
|
32
|
+
args
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class PayloadMiddleware
|
5
|
+
class << self
|
6
|
+
def registry
|
7
|
+
@registry ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def register middleware, name
|
11
|
+
registry[name] = middleware
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
require_relative "payload_middleware/stack"
|
19
|
+
require_relative "payload_middleware/base"
|
20
|
+
require_relative "payload_middleware/format_message"
|
21
|
+
require_relative "payload_middleware/format_attachments"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class PayloadMiddleware
|
5
|
+
class Base
|
6
|
+
class << self
|
7
|
+
def middleware_name name
|
8
|
+
PayloadMiddleware.register self, name.to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def options default_opts
|
12
|
+
@default_opts = default_opts
|
13
|
+
end
|
14
|
+
|
15
|
+
def default_opts
|
16
|
+
@default_opts ||= {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :notifier, :options
|
21
|
+
|
22
|
+
def initialize notifier, opts={}
|
23
|
+
@notifier = notifier
|
24
|
+
@options = self.class.default_opts.merge opts
|
25
|
+
end
|
26
|
+
|
27
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
28
|
+
def call payload={}
|
29
|
+
raise NoMethodError, "method `call` not defined for class #{self.class}"
|
30
|
+
end
|
31
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class PayloadMiddleware
|
5
|
+
class FormatAttachments < Base
|
6
|
+
middleware_name :format_attachments
|
7
|
+
|
8
|
+
options formats: [:html, :markdown]
|
9
|
+
|
10
|
+
def call payload={}
|
11
|
+
attachments = payload.fetch(:attachments, payload["attachments"])
|
12
|
+
wrap_array(attachments).each do |attachment|
|
13
|
+
["text", :text].each do |key|
|
14
|
+
if attachment.key?(key)
|
15
|
+
attachment[key] = Util::LinkFormatter.format(attachment[key], options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
payload
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def wrap_array object
|
26
|
+
if object.nil?
|
27
|
+
[]
|
28
|
+
elsif object.respond_to?(:to_ary)
|
29
|
+
object.to_ary || [object]
|
30
|
+
else
|
31
|
+
[object]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class PayloadMiddleware
|
5
|
+
class FormatMessage < Base
|
6
|
+
middleware_name :format_message
|
7
|
+
|
8
|
+
options formats: [:html, :markdown]
|
9
|
+
|
10
|
+
def call payload={}
|
11
|
+
return payload unless payload[:text]
|
12
|
+
payload[:text] = Util::LinkFormatter.format(payload[:text], options)
|
13
|
+
|
14
|
+
payload
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
class PayloadMiddleware
|
5
|
+
class Stack
|
6
|
+
attr_reader :notifier,
|
7
|
+
:stack
|
8
|
+
|
9
|
+
def initialize notifier
|
10
|
+
@notifier = notifier
|
11
|
+
@stack = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def set *middlewares
|
15
|
+
middlewares =
|
16
|
+
if middlewares.length == 1 && middlewares.first.is_a?(Hash)
|
17
|
+
middlewares.first
|
18
|
+
else
|
19
|
+
middlewares.flatten
|
20
|
+
end
|
21
|
+
|
22
|
+
@stack = middlewares.map do |key, opts|
|
23
|
+
PayloadMiddleware.registry.fetch(key).new(*[notifier, opts].compact)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def call payload={}
|
28
|
+
stack.inject payload do |pld, middleware|
|
29
|
+
middleware.call(pld)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
module Util
|
5
|
+
module Escape
|
6
|
+
HTML_REGEXP = /[&><]/
|
7
|
+
HTML_REPLACE = { "&" => "&", ">" => ">", "<" => "<" }.freeze
|
8
|
+
|
9
|
+
def self.html string
|
10
|
+
string.gsub(HTML_REGEXP, HTML_REPLACE)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
module Slack
|
6
|
+
class Notifier
|
7
|
+
module Util
|
8
|
+
class HTTPClient
|
9
|
+
class << self
|
10
|
+
def post uri, params
|
11
|
+
HTTPClient.new(uri, params).call
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :uri, :params, :http_options
|
16
|
+
|
17
|
+
def initialize uri, params
|
18
|
+
@uri = uri
|
19
|
+
@http_options = params.delete(:http_options) || {}
|
20
|
+
@params = params
|
21
|
+
end
|
22
|
+
|
23
|
+
def call
|
24
|
+
http_obj.request request_obj
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def request_obj
|
30
|
+
req = Net::HTTP::Post.new uri.request_uri
|
31
|
+
req.set_form_data params
|
32
|
+
|
33
|
+
req
|
34
|
+
end
|
35
|
+
|
36
|
+
def http_obj
|
37
|
+
http = Net::HTTP.new uri.host, uri.port
|
38
|
+
http.use_ssl = (uri.scheme == "https")
|
39
|
+
|
40
|
+
http_options.each do |opt, val|
|
41
|
+
if http.respond_to? "#{opt}="
|
42
|
+
http.send "#{opt}=", val
|
43
|
+
else
|
44
|
+
warn "Net::HTTP doesn't respond to `#{opt}=`, ignoring that option"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
http
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Slack
|
3
|
+
class Notifier
|
4
|
+
module Util
|
5
|
+
class LinkFormatter
|
6
|
+
# http://rubular.com/r/19cNXW5qbH
|
7
|
+
HTML_PATTERN = / <a (?:.*?) href=['"](.+?)['"] (?:.*?)> (.+?) <\/a> /x
|
8
|
+
|
9
|
+
# http://rubular.com/r/guJbTK6x1f
|
10
|
+
MARKDOWN_PATTERN = /\[ ([^\[\]]*?) \] \( ((https?:\/\/.*?) | (mailto:.*?)) \) /x
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def format string, opts={}
|
14
|
+
LinkFormatter.new(string, opts).formatted
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :formats
|
19
|
+
|
20
|
+
def initialize string, formats: [:html, :markdown]
|
21
|
+
@formats = formats
|
22
|
+
@orig = string.respond_to?(:scrub) ? string.scrub : string
|
23
|
+
end
|
24
|
+
|
25
|
+
# rubocop:disable Style/GuardClause
|
26
|
+
def formatted
|
27
|
+
sub_markdown_links(sub_html_links(@orig))
|
28
|
+
rescue => e
|
29
|
+
if RUBY_VERSION < "2.1" && e.message.include?("invalid byte sequence")
|
30
|
+
raise e, "#{e.message}. Consider including the 'string-scrub' gem to strip invalid characters"
|
31
|
+
else
|
32
|
+
raise e
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop:enable Style/GuardClause
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def sub_html_links string
|
40
|
+
return string unless formats.include?(:html)
|
41
|
+
|
42
|
+
string.gsub(HTML_PATTERN) do
|
43
|
+
slack_link Regexp.last_match[1], Regexp.last_match[2]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def sub_markdown_links string
|
48
|
+
return string unless formats.include?(:markdown)
|
49
|
+
|
50
|
+
string.gsub(MARKDOWN_PATTERN) do
|
51
|
+
slack_link Regexp.last_match[2], Regexp.last_match[1]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def slack_link link, text=nil
|
56
|
+
"<#{link}" \
|
57
|
+
"#{text && !text.empty? ? "|#{text}" : ''}" \
|
58
|
+
">"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|