Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 93 additions & 83 deletions lib/exception_notifier/teams_notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ class TeamsNotifier < BaseNotifier
include ExceptionNotifier::BacktraceCleaner

class MissingController
def method_missing(*args, &block)
end

def respond_to_missing?(*args)
end
def method_missing(*); end
def respond_to_missing?(*); true; end
end

attr_accessor :httparty
Expand All @@ -25,15 +22,15 @@ def initialize(options = {})
end

def call(exception, options = {})
@options = options.merge(@default_options)
@options = options.merge(@default_options)
@exception = exception
@backtrace = exception.backtrace ? clean_backtrace(exception) : nil

@env = @options.delete(:env)

@application_name = @options.delete(:app_name) || rails_app_name
@gitlab_url = @options.delete(:git_url)
@jira_url = @options.delete(:jira_url)
@jira_url = @options.delete(:jira_url)

@webhook_url = @options.delete(:webhook_url)
raise ArgumentError, "You must provide 'webhook_url' parameter." unless @webhook_url
Expand All @@ -53,141 +50,154 @@ def call(exception, options = {})

end

payload = message_text
payload = adaptive_card_payload

@options[:body] = payload.to_json
@options[:headers] ||= {}
@options[:headers]["Content-Type"] = "application/json"
@options[:debug_output] = $stdout

@httparty.post(@webhook_url, @options)
end

private

def message_text
text = {
"@type" => "MessageCard",
"@context" => "http://schema.org/extensions",
"summary" => "#{@application_name} Exception Alert",
"title" => "⚠️ Exception Occurred in #{env_name} ⚠️",
"sections" => [
{
"activityTitle" => activity_title,
"activitySubtitle" => @exception.message.to_s
}
],
"potentialAction" => []
def adaptive_card_payload
{
"type" => "AdaptiveCard",
"$schema" => "http://adaptivecards.io/schemas/adaptive-card.json",
"version" => "1.4",
"body" => card_body,
"actions" => card_actions
}
end

text["sections"].push details
text["potentialAction"].push gitlab_view_link unless @gitlab_url.nil?
text["potentialAction"].push gitlab_issue_link unless @gitlab_url.nil?
text["potentialAction"].push jira_issue_link unless @jira_url.nil?
def card_body
body = []

text
body << header_block
body << activity_block
body << message_block
body << facts_block if facts_block

body.compact
end

def details
details = {
"title" => "Details",
"facts" => []
def header_block
{
"type" => "TextBlock",
"text" => "⚠️ Exception Occurred in #{env_name} ⚠️",
"weight" => "bolder",
"size" => "large"
}

details["facts"].push message_request unless @request_items.nil?
details["facts"].push message_backtrace unless @backtrace.nil?
details["facts"].push additional_exception_data unless @additional_exception_data.nil?
details
end

def activity_title
errors_count = @options[:accumulated_errors_count].to_i
def activity_block
{
"type" => "TextBlock",
"text" => activity_title
}
end

"#{(errors_count > 1) ? errors_count : "A"} *#{@exception.class}* occurred" +
(@controller ? " in *#{controller_and_method}*." : ".")
def message_block
{
"type" => "TextBlock",
"text" => @exception.message.to_s,
"wrap" => true,
"isSubtle" => true
}
end

def message_request
def facts_block
facts = []
facts << request_fact if @request_items
facts << backtrace_fact if @backtrace
facts << data_fact if @additional_exception_data

return nil if facts.empty?

{
"name" => "Request",
"value" => "#{hash_presentation(@request_items)}\n "
"type" => "FactSet",
"facts" => facts
}
end

def message_backtrace(size = 3)
text = []
size = (@backtrace.size < size) ? @backtrace.size : size
text << "```"
size.times { |i| text << "* " + @backtrace[i] }
text << "```"
def request_fact
{
"title" => "Request",
"value" => hash_presentation(@request_items)
}
end

def backtrace_fact(limit = 3)
{
"name" => "Backtrace",
"value" => text.join(" \n").to_s
"title" => "Backtrace",
"value" => @backtrace.first(limit).join("\n")
}
end

def additional_exception_data
def data_fact
{
"name" => "Data",
"value" => "`#{@additional_exception_data}`\n "
"title" => "Data",
"value" => @additional_exception_data.to_s
}
end

def card_actions
actions = []
actions << gitlab_view_link if @gitlab_url
actions << gitlab_issue_link if @gitlab_url
actions << jira_issue_link if @jira_url
actions
end

def gitlab_view_link
{
"@type" => "ViewAction",
"name" => "\u{1F98A} View in GitLab",
"target" => [
"#{@gitlab_url}/#{@application_name}"
]
"type" => "Action.OpenUrl",
"title" => "🦊 View in GitLab",
"url" => "#{@gitlab_url}/#{@application_name}"
}
end

def gitlab_issue_link
link = [@gitlab_url, @application_name, "issues", "new"].join("/")
params = {
"issue[title]" => ["[BUG] Error 500 :",
"issue[title]" => [
"[BUG]",
controller_and_method,
"(#{@exception.class})",
@exception.message].compact.join(" ")
@exception.message
].compact.join(" ")
}.to_query

{
"@type" => "ViewAction",
"name" => "\u{1F98A} Create Issue in GitLab",
"target" => [
"#{link}/?#{params}"
]
"type" => "Action.OpenUrl",
"title" => "🦊 Create Issue in GitLab",
"url" => "#{link}?#{params}"
}
end

def jira_issue_link
{
"@type" => "ViewAction",
"name" => "🐞 Create Issue in Jira",
"target" => [
"#{@jira_url}/secure/CreateIssue!default.jspa"
]
"type" => "Action.OpenUrl",
"title" => "Create Jira Issue",
"url" => "#{@jira_url}/secure/CreateIssue!default.jspa"
}
end

def activity_title
errors_count = @options[:accumulated_errors_count].to_i

"#{(errors_count > 1) ? errors_count : "A"} *#{@exception.class}* occurred" +
(@controller ? " in *#{controller_and_method}*." : ".")
end

def controller_and_method
if @controller
"#{@controller.controller_name}##{@controller.action_name}"
else
""
end
return "" unless @controller
"#{@controller.controller_name}##{@controller.action_name}"
end

def hash_presentation(hash)
text = []

hash.each do |key, value|
text << "* **#{key}** : `#{value}`"
end

text.join(" \n")
hash.map { |k, v| "#{k}: #{v}" }.join("\n")
end

def rails_app_name
Expand Down
36 changes: 18 additions & 18 deletions test/exception_notifier/teams_notifier_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ class TeamsNotifierTest < ActiveSupport::TestCase

options = teams_notifier.call ArgumentError.new("foo"), options

body = ActiveSupport::JSON.decode options[:body]
assert body.key? "title"
assert body.key? "sections"
payload = ActiveSupport::JSON.decode options[:body]
assert payload.key? "type"
assert payload.key? "body"

sections = body["sections"]
header = sections[0]
body = payload["body"]
header = body[0]
title = body[1]

assert_equal 2, sections.size
assert_equal "A *ArgumentError* occurred.", header["activityTitle"]
assert_equal "foo", header["activitySubtitle"]
assert_equal 3, body.size
assert_equal "A *ArgumentError* occurred.", title["text"]
end

test "should send notification with create gitlab issue link if specified" do
Expand All @@ -35,12 +35,12 @@ class TeamsNotifierTest < ActiveSupport::TestCase

options = teams_notifier.call ArgumentError.new("foo"), options

body = ActiveSupport::JSON.decode options[:body]
payload = ActiveSupport::JSON.decode options[:body]

potential_action = body["potentialAction"]
potential_action = payload["actions"]
assert_equal 2, potential_action.size
assert_equal "🦊 View in GitLab", potential_action[0]["name"]
assert_equal "🦊 Create Issue in GitLab", potential_action[1]["name"]
assert_equal "🦊 View in GitLab", potential_action[0]["title"]
assert_equal "🦊 Create Issue in GitLab", potential_action[1]["title"]
end

test "should add other HTTParty options to params" do
Expand Down Expand Up @@ -69,19 +69,19 @@ class TeamsNotifierTest < ActiveSupport::TestCase
teams_notifier.instance_variable_set(:@exception, exception)
teams_notifier.instance_variable_set(:@options, {})

message_text = teams_notifier.send(:message_text)
header = message_text["sections"][0]
assert_equal "A *ArgumentError* occurred.", header["activityTitle"]
message_text = teams_notifier.send(:adaptive_card_payload)
header = message_text["body"][1]
assert_equal "A *ArgumentError* occurred.", header["text"]
end

test "should use direct errors count if :accumulated_errors_count option is 5" do
teams_notifier = ExceptionNotifier::TeamsNotifier.new
exception = ArgumentError.new("foo")
teams_notifier.instance_variable_set(:@exception, exception)
teams_notifier.instance_variable_set(:@options, accumulated_errors_count: 5)
message_text = teams_notifier.send(:message_text)
header = message_text["sections"][0]
assert_equal "5 *ArgumentError* occurred.", header["activityTitle"]
message_text = teams_notifier.send(:adaptive_card_payload)
header = message_text["body"][1]
assert_equal "5 *ArgumentError* occurred.", header["text"]
end
end

Expand Down