exception_notification 4.2.2 → 4.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a5eab5531d309930d48027a778d74f8491a5a995
4
- data.tar.gz: 67d0b4176fb7decc436754ee60e7f4a9053d48f8
3
+ metadata.gz: 2ed3e1aae22db5d3e8005cf160d81b2e4cbf629c
4
+ data.tar.gz: 12fda9fcf321b8b3c5d2fa5eb2a1c35358e03105
5
5
  SHA512:
6
- metadata.gz: 1f92d01eee01a295d84e28bcaff7618edb3a4a626afe56a447974579115d8daf4d3e6d516c1c5bb449b88f53497effafa479c6a46bb614b9cb1eed04f5510211
7
- data.tar.gz: ca3718addeb5deb323a4f73c09d0678534381301a4fc85ac648fef406beafccb844500039c1c805d6f20d1ef897696a6f92f94631a6be25ccbc468899ef69ec8
6
+ metadata.gz: 4cce16554760bb205c3d2f64ce7c4278f9a7e376254989d486132aec26ebe9802cd62e3dcc18eb51cc94f70d58dd40b57263777ac8e9315f18a68b09b1a335a8
7
+ data.tar.gz: 34578cb651e0363e1a58b08ef3bbc70187afc7fb8de9e57bfa0ad3f35e1f93dd639ff4a63b1fe148c014875fafaffea6471895c9882102a99ed0b1127c095902
data/Appraisals CHANGED
@@ -1,8 +1,7 @@
1
- rails_versions = ['~> 4.0.5', '~> 4.1.1', '~> 4.2.0', '~> 5.0.0']
1
+ rails_versions = ['~> 4.0.5', '~> 4.1.1', '~> 4.2.0', '~> 5.0.0', '~> 5.1.0']
2
2
 
3
3
  rails_versions.each do |rails_version|
4
4
  appraise "rails#{rails_version.slice(/\d+\.\d+/).gsub('.', '_')}" do
5
5
  gem 'rails', rails_version
6
- gem "sqlite3"
7
6
  end
8
7
  end
@@ -1,7 +1,22 @@
1
+ == 4.3.0
2
+
3
+ * enhancements
4
+ * Add Microsoft Teams Notifier (by @phaelin)
5
+ * Add SNS notifier (by @FLarra)
6
+ * Add Google Chats notifier (by @renatolond)
7
+ * Align output of section-headers consistently (by @kronn)
8
+ * ExceptionNotifier.notify_exception receives block & pass it to each notifier (by @pocke)
9
+ * Update Travis to latest rubies (by @lostapathy)
10
+
11
+ * bug fixes
12
+ * Replace all before_filter to before_action on readme (by @pastullo)
13
+ * Fix error when using error grouping outside of rails (by @garethcokell)
14
+ * Fix missing MissingController Mattermost class (by @n-rodriguez)
15
+
1
16
  == 4.2.2
2
17
 
3
18
  * enhancements
4
- * Error groupiong (by @Martin91)
19
+ * Error grouping (by @Martin91)
5
20
  * Additional fields for Slack support (by @schurig)
6
21
  * Enterprise HipChat support (by @seanhuber)
7
22
 
@@ -131,7 +146,7 @@
131
146
  * Add normalize_subject option to remove numbers from email so that they thread (by @jjb)
132
147
  * Allow the user to provide a custom message and hash of data (by @jjb)
133
148
  * Add support for configurable background sections and a data partial (by @jeffrafter)
134
- * Include timestamp of exception in notification body
149
+ * Include timestamp of exception in notification body
135
150
  * Add support for rack based session management (by @phoet)
136
151
  * Add ignore_crawlers option to ignore exceptions generated by crawlers
137
152
  * Add verbode_subject option to exclude exception message from subject (by @amishyn)
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  ---
11
11
 
12
- The Exception Notification gem provides a set of [notifiers](#notifiers) for sending notifications when errors occur in a Rack/Rails application. The built-in notifiers can deliver notifications by [email](#email-notifier), [Campfire](#campfire-notifier), [HipChat](#hipchat-notifier), [Slack](#slack-notifier), [Mattermost](#mattermost-notifier), [IRC](#irc-notifier) or via custom [WebHooks](#webhook-notifier).
12
+ The Exception Notification gem provides a set of [notifiers](#notifiers) for sending notifications when errors occur in a Rack/Rails application. The built-in notifiers can deliver notifications by [email](#email-notifier), [Campfire](#campfire-notifier), [HipChat](#hipchat-notifier), [Slack](#slack-notifier), [Mattermost](#mattermost-notifier), [Teams](#teams-notifier), [IRC](#irc-notifier), [Amazon SNS](#amazon-sns-notifier), [Google Chat](#google-chat-notifier) or via custom [WebHooks](#webhook-notifier).
13
13
 
14
14
  There's a great [Railscast about Exception Notification](http://railscasts.com/episodes/104-exception-notifications-revised) you can see that may help you getting started.
15
15
 
@@ -56,7 +56,7 @@ Save the current user in the `request` using a controller callback.
56
56
 
57
57
  ```ruby
58
58
  class ApplicationController < ActionController::Base
59
- before_filter :prepare_exception_notifier
59
+ before_action :prepare_exception_notifier
60
60
  private
61
61
  def prepare_exception_notifier
62
62
  request.env["exception_notifier.exception_data"] = {
@@ -90,6 +90,9 @@ ExceptionNotification relies on notifiers to deliver notifications when errors o
90
90
  * [IRC notifier](#irc-notifier)
91
91
  * [Slack notifier](#slack-notifier)
92
92
  * [Mattermost notifier](#mattermost-notifier)
93
+ * [Teams notifier](#teams-notifier)
94
+ * [Amazon SNS](#amazon-sns-notifier)
95
+ * [Google Chat notifier](#google-chat-notifier)
93
96
  * [WebHook notifier](#webhook-notifier)
94
97
 
95
98
  But, you also can easily implement your own [custom notifier](#custom-notifier).
@@ -220,7 +223,7 @@ If your new section requires information that isn't available by default, make s
220
223
 
221
224
  ```ruby
222
225
  class ApplicationController < ActionController::Base
223
- before_filter :log_additional_data
226
+ before_action :log_additional_data
224
227
  ...
225
228
  protected
226
229
  def log_additional_data
@@ -542,7 +545,7 @@ The slack notification will include any data saved under `env["exception_notifie
542
545
  An example of how to send the server name to Slack in Rails (put this code in application_controller.rb):
543
546
 
544
547
  ```ruby
545
- before_filter :set_notification
548
+ before_action :set_notification
546
549
 
547
550
  def set_notification
548
551
  request.env['exception_notifier.exception_data'] = {"server" => request.env['SERVER_NAME']}
@@ -608,7 +611,7 @@ Contains additional payload for a message (e.g avatar, attachments, etc). See [s
608
611
 
609
612
  Contains additional fields that will be added to the attachement. See [Slack documentation](https://api.slack.com/docs/message-attachments).
610
613
 
611
- ## Mattermost notifier
614
+ ### Mattermost notifier
612
615
 
613
616
  Post notification in a mattermost channel via [incoming webhook](http://docs.mattermost.com/developer/webhooks-incoming.html)
614
617
 
@@ -723,6 +726,128 @@ Url of your gitlab or github with your organisation name for issue creation link
723
726
 
724
727
  Your application name used for issue creation link. Defaults to ``` Rails.application.class.parent_name.underscore```.
725
728
 
729
+ ### Google Chat Notifier
730
+
731
+ Post notifications in a Google Chats channel via [incoming webhook](https://developers.google.com/hangouts/chat/how-tos/webhooks)
732
+
733
+ Add the [HTTParty](https://github.com/jnunemaker/httparty) gem to your `Gemfile`:
734
+
735
+ ```ruby
736
+ gem 'httparty'
737
+ ```
738
+
739
+ To configure it, you **need** to set the `webhook_url` option.
740
+
741
+ ```ruby
742
+ Rails.application.config.middleware.use ExceptionNotification::Rack,
743
+ :google_chat => {
744
+ :webhook_url => 'https://chat.googleapis.com/v1/spaces/XXXXXXXX/messages?key=YYYYYYYYYYYYY&token=ZZZZZZZZZZZZ'
745
+ }
746
+ ```
747
+
748
+ ##### webhook_url
749
+
750
+ *String, required*
751
+
752
+ The Incoming WebHook URL on Google Chats.
753
+
754
+ ##### app_name
755
+
756
+ *String, optional*
757
+
758
+ Your application name, shown in the notification. Defaults to `Rails.application.class.parent_name.underscore`.
759
+
760
+ ### Amazon SNS Notifier
761
+
762
+ Notify all exceptions Amazon - Simple Notification Service: [SNS](https://aws.amazon.com/sns/).
763
+
764
+ #### Usage
765
+
766
+ Add the [aws-sdk-sns](https://github.com/aws/aws-sdk-ruby/tree/master/gems/aws-sdk-sns) gem to your `Gemfile`:
767
+
768
+ ```ruby
769
+ gem 'aws-sdk-sns', '~> 1.5'
770
+ ```
771
+
772
+ To configure it, you **need** to set 3 required options for aws: `region`, `access_key_id` and `secret_access_key`, and one more option for sns: `topic_arn`.
773
+
774
+ ```ruby
775
+ Rails.application.config.middleware.use ExceptionNotification::Rack,
776
+ sns: {
777
+ region: 'us-east-x',
778
+ access_key_id: 'access_key_id',
779
+ secret_access_key: 'secret_access_key',
780
+ topic_arn: 'arn:aws:sns:us-east-x:XXXX:my-topic'
781
+ }
782
+ ```
783
+
784
+ ##### sns_prefix
785
+ *String, optional *
786
+
787
+ Prefix in the notification subject, by default: "[Error]"
788
+
789
+ ##### backtrace_lines
790
+ *Integer, optional *
791
+
792
+ Number of backtrace lines to be displayed in the notification message. By default: 10
793
+
794
+ #### Note:
795
+ * You may need to update your previous `aws-sdk-*` gems in order to setup `aws-sdk-sns` correctly.
796
+ * If you need any further information about the available regions or any other SNS related topic consider: [SNS faqs](https://aws.amazon.com/sns/faqs/)
797
+
798
+ ### Teams notifier
799
+
800
+ Post notification in a Microsoft Teams channel via [Incoming Webhook Connector](https://docs.microsoft.com/en-us/outlook/actionable-messages/actionable-messages-via-connectors)
801
+ Just add the [HTTParty](https://github.com/jnunemaker/httparty) gem to your `Gemfile`:
802
+
803
+ ```ruby
804
+ gem 'httparty'
805
+ ```
806
+
807
+ To configure it, you **need** to set the `webhook_url` option.
808
+ If you are using GitLab for issue tracking, you can specify `git_url` as follows to add a *Create issue* button in your notification.
809
+ By default this will use your Rails application name to match the git repository. If yours differs, you can specify `app_name`.
810
+ By that same notion, you may also set a `jira_url` to get a button that will send you to the New Issue screen in Jira.
811
+
812
+ ```ruby
813
+ Rails.application.config.middleware.use ExceptionNotification::Rack,
814
+ :email => {
815
+ :email_prefix => "[PREFIX] ",
816
+ :sender_address => %{"notifier" <notifier@example.com>},
817
+ :exception_recipients => %w{exceptions@example.com}
818
+ },
819
+ :teams => {
820
+ :webhook_url => 'https://outlook.office.com/webhook/your-guid/IncomingWebhook/team-guid',
821
+ :git_url => 'https://your-gitlab.com/Group/Project',
822
+ :jira_url => 'https://your-jira.com'
823
+ }
824
+ ```
825
+
826
+ #### Options
827
+
828
+ ##### webhook_url
829
+
830
+ *String, required*
831
+
832
+ The Incoming WebHook URL on mattermost.
833
+
834
+ ##### git_url
835
+
836
+ *String, optional*
837
+
838
+ Url of your gitlab or github with your organisation name for issue creation link (Eg: `github.com/aschen`). Defaults to nil and doesn't add link to the notification.
839
+
840
+ ##### jira_url
841
+
842
+ *String, optional*
843
+
844
+ Url of your Jira instance, adds button for Create Issue screen. Defaults to nil and doesn't add a button to the card.
845
+
846
+ ##### app_name
847
+
848
+ *String, optional*
849
+
850
+ Your application name used for git issue creation link. Defaults to `Rails.application.class.parent_name.underscore`.
726
851
 
727
852
  ### WebHook notifier
728
853
 
@@ -1,8 +1,8 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'exception_notification'
3
- s.version = '4.2.2'
3
+ s.version = '4.3.0'
4
4
  s.authors = ["Jamis Buck", "Josh Peek"]
5
- s.date = %q{2017-08-12}
5
+ s.date = %q{2018-11-22}
6
6
  s.summary = "Exception notification for Rails apps"
7
7
  s.homepage = "https://smartinez87.github.io/exception_notification/"
8
8
  s.email = "smartinez87@gmail.com"
@@ -32,4 +32,5 @@ Gem::Specification.new do |s|
32
32
  s.add_development_dependency "hipchat", ">= 1.0.0"
33
33
  s.add_development_dependency "carrier-pigeon", ">= 0.7.0"
34
34
  s.add_development_dependency "slack-notifier", ">= 1.0.0"
35
+ s.add_development_dependency "aws-sdk-sns", "~> 1"
35
36
  end
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 4.0.5"
6
- gem "sqlite3"
7
6
 
8
7
  gemspec :path => "../"
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 4.1.1"
6
- gem "sqlite3"
7
6
 
8
7
  gemspec :path => "../"
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 4.2.0"
6
- gem "sqlite3"
7
6
 
8
7
  gemspec :path => "../"
@@ -3,6 +3,5 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "rails", "~> 5.0.0"
6
- gem "sqlite3"
7
6
 
8
7
  gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rails", "~> 5.1.0"
6
+
7
+ gemspec :path => "../"
@@ -17,6 +17,9 @@ module ExceptionNotifier
17
17
  autoload :IrcNotifier, 'exception_notifier/irc_notifier'
18
18
  autoload :SlackNotifier, 'exception_notifier/slack_notifier'
19
19
  autoload :MattermostNotifier, 'exception_notifier/mattermost_notifier'
20
+ autoload :TeamsNotifier, 'exception_notifier/teams_notifier'
21
+ autoload :SnsNotifier, 'exception_notifier/sns_notifier'
22
+ autoload :GoogleChatNotifier, 'exception_notifier/google_chat_notifier'
20
23
 
21
24
  class UndefinedNotifierError < StandardError; end
22
25
 
@@ -42,7 +45,7 @@ module ExceptionNotifier
42
45
  self.testing_mode = true
43
46
  end
44
47
 
45
- def notify_exception(exception, options={})
48
+ def notify_exception(exception, options={}, &block)
46
49
  return false if ignored_exception?(options[:ignore_exceptions], exception)
47
50
  return false if ignored?(exception, options)
48
51
  if error_grouping
@@ -52,7 +55,7 @@ module ExceptionNotifier
52
55
 
53
56
  selected_notifiers = options.delete(:notifiers) || notifiers
54
57
  [*selected_notifiers].each do |notifier|
55
- fire_notification(notifier, exception, options.dup)
58
+ fire_notification(notifier, exception, options.dup, &block)
56
59
  end
57
60
  true
58
61
  end
@@ -104,12 +107,14 @@ module ExceptionNotifier
104
107
  end
105
108
 
106
109
  def ignored_exception?(ignore_array, exception)
107
- (Array(ignored_exceptions) + Array(ignore_array)).map(&:to_s).include?(exception.class.name)
110
+ all_ignored_exceptions = (Array(ignored_exceptions) + Array(ignore_array)).map(&:to_s)
111
+ exception_ancestors = exception.class.ancestors.map(&:to_s)
112
+ !(all_ignored_exceptions & exception_ancestors).empty?
108
113
  end
109
114
 
110
- def fire_notification(notifier_name, exception, options)
115
+ def fire_notification(notifier_name, exception, options, &block)
111
116
  notifier = registered_exception_notifier(notifier_name)
112
- notifier.call(exception, options)
117
+ notifier.call(exception, options, &block)
113
118
  rescue Exception => e
114
119
  raise e if @@testing_mode
115
120
 
@@ -60,8 +60,8 @@ module ExceptionNotifier
60
60
 
61
61
  def compose_subject
62
62
  subject = "#{@options[:email_prefix]}"
63
- subject << "(#{@options[:accumulated_errors_count]} times) " if @options[:accumulated_errors_count].to_i > 1
64
- subject << "#{@kontroller.controller_name}##{@kontroller.action_name}" if @kontroller && @options[:include_controller_and_action_names_in_subject]
63
+ subject << "(#{@options[:accumulated_errors_count]} times)" if @options[:accumulated_errors_count].to_i > 1
64
+ subject << "#{@kontroller.controller_name} #{@kontroller.action_name}" if @kontroller && @options[:include_controller_and_action_names_in_subject]
65
65
  subject << " (#{@exception.class})"
66
66
  subject << " #{@exception.message.inspect}" if @options[:verbose_subject]
67
67
  subject = EmailNotifier.normalize_digits(subject) if @options[:normalize_subject]
@@ -75,17 +75,17 @@ module ExceptionNotifier
75
75
  end
76
76
 
77
77
  helper_method :inspect_object
78
-
78
+
79
79
  def truncate(string, max)
80
80
  string.length > max ? "#{string[0...max]}..." : string
81
81
  end
82
-
82
+
83
83
  def inspect_object(object)
84
84
  case object
85
85
  when Hash, Array
86
86
  truncate(object.inspect, 300)
87
87
  else
88
- object.to_s
88
+ object.to_s
89
89
  end
90
90
  end
91
91
 
@@ -0,0 +1,136 @@
1
+ require 'action_dispatch'
2
+ require 'active_support/core_ext/time'
3
+
4
+ module ExceptionNotifier
5
+ class GoogleChatNotifier
6
+ include ExceptionNotifier::BacktraceCleaner
7
+
8
+ class MissingController
9
+ def method_missing(*args, &block)
10
+ end
11
+ end
12
+
13
+ attr_accessor :httparty
14
+
15
+ def initialize(options = {})
16
+ super()
17
+ @default_options = options
18
+ @httparty = HTTParty
19
+ end
20
+
21
+ def call(exception, options = {})
22
+ @options = options.merge(@default_options)
23
+ @exception = exception
24
+ @backtrace = exception.backtrace ? clean_backtrace(exception) : nil
25
+
26
+ @env = @options.delete(:env)
27
+
28
+ @application_name = @options.delete(:app_name) || Rails.application.class.parent_name.underscore
29
+
30
+ @webhook_url = @options.delete(:webhook_url)
31
+ raise ArgumentError.new "You must provide 'webhook_url' parameter." unless @webhook_url
32
+
33
+ unless @env.nil?
34
+ @controller = @env['action_controller.instance'] || MissingController.new
35
+
36
+ request = ActionDispatch::Request.new(@env)
37
+
38
+ @request_items = { url: request.original_url,
39
+ http_method: request.method,
40
+ ip_address: request.remote_ip,
41
+ parameters: request.filtered_parameters,
42
+ timestamp: Time.current }
43
+ else
44
+ @controller = @request_items = nil
45
+ end
46
+
47
+
48
+ @options[:body] = payload.to_json
49
+ @options[:headers] ||= {}
50
+ @options[:headers].merge!({ 'Content-Type' => 'application/json' })
51
+
52
+ @httparty.post(@webhook_url, @options)
53
+ end
54
+
55
+ private
56
+
57
+ def payload
58
+ {
59
+ text: exception_text
60
+ }
61
+ end
62
+
63
+ def header
64
+ errors_count = @options[:accumulated_errors_count].to_i
65
+ text = ['']
66
+
67
+ text << "Application: *#{@application_name}*"
68
+ text << "#{errors_count > 1 ? errors_count : 'An'} *#{@exception.class}* occured" + if @controller then " in *#{controller_and_method}*." else "." end
69
+
70
+ text
71
+ end
72
+
73
+ def exception_text
74
+ text = []
75
+
76
+ text << header
77
+ text << ''
78
+
79
+ text << "⚠️ Error 500 in #{Rails.env} ⚠️"
80
+ text << "*#{@exception.message.gsub('`', %q('))}*"
81
+
82
+ if @request_items
83
+ text << ''
84
+ text += message_request
85
+ end
86
+
87
+ if @backtrace
88
+ text << ''
89
+ text += message_backtrace
90
+ end
91
+
92
+ text.join("\n")
93
+ end
94
+
95
+ def message_request
96
+ text = []
97
+
98
+ text << "*Request:*"
99
+ text << "```"
100
+ text << hash_presentation(@request_items)
101
+ text << "```"
102
+
103
+ text
104
+ end
105
+
106
+ def hash_presentation(hash)
107
+ text = []
108
+
109
+ hash.each do |key, value|
110
+ text << "* #{key} : #{value}"
111
+ end
112
+
113
+ text.join("\n")
114
+ end
115
+
116
+ def message_backtrace(size = 3)
117
+ text = []
118
+
119
+ size = @backtrace.size < size ? @backtrace.size : size
120
+ text << "*Backtrace:*"
121
+ text << "```"
122
+ size.times { |i| text << "* " + @backtrace[i] }
123
+ text << "```"
124
+
125
+ text
126
+ end
127
+
128
+ def controller_and_method
129
+ if @controller
130
+ "#{@controller.controller_name}##{@controller.action_name}"
131
+ else
132
+ ""
133
+ end
134
+ end
135
+ end
136
+ end
@@ -5,6 +5,11 @@ module ExceptionNotifier
5
5
  class MattermostNotifier
6
6
  include ExceptionNotifier::BacktraceCleaner
7
7
 
8
+ class MissingController
9
+ def method_missing(*args, &block)
10
+ end
11
+ end
12
+
8
13
  attr_accessor :httparty
9
14
 
10
15
  def initialize(options = {})
@@ -52,7 +52,7 @@ module ExceptionNotifier
52
52
  else
53
53
  backtrace_based_key = "exception:#{Zlib.crc32("#{exception.class.name}\npath:#{exception.backtrace.try(:first)}")}"
54
54
 
55
- if count = Rails.cache.read(backtrace_based_key)
55
+ if count = error_grouping_cache.read(backtrace_based_key)
56
56
  accumulated_errors_count = count + 1
57
57
  save_error_count(backtrace_based_key, accumulated_errors_count)
58
58
  else
@@ -0,0 +1,79 @@
1
+ module ExceptionNotifier
2
+ class SnsNotifier < BaseNotifier
3
+ def initialize(options)
4
+ super
5
+
6
+ raise ArgumentError.new "You must provide 'region' option" unless options[:region]
7
+ raise ArgumentError.new "You must provide 'access_key_id' option" unless options[:access_key_id]
8
+ raise ArgumentError.new "You must provide 'secret_access_key' option" unless options[:secret_access_key]
9
+
10
+ @notifier = Aws::SNS::Client.new(
11
+ region: options[:region],
12
+ access_key_id: options[:access_key_id],
13
+ secret_access_key: options[:secret_access_key]
14
+ )
15
+ @options = default_options.merge(options)
16
+ end
17
+
18
+ def call(exception, custom_opts = {})
19
+ custom_options = options.merge(custom_opts)
20
+
21
+ subject = build_subject(exception, custom_options)
22
+ message = build_message(exception, custom_options)
23
+
24
+ notifier.publish(
25
+ topic_arn: custom_options[:topic_arn],
26
+ message: message,
27
+ subject: subject
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :notifier, :options
34
+
35
+ def build_subject(exception, options)
36
+ subject = "#{options[:sns_prefix]} - "
37
+ subject << accumulated_exception_name(exception, options)
38
+ subject << " occurred"
39
+ subject.length > 120 ? subject[0...120] + "..." : subject
40
+ end
41
+
42
+ def build_message(exception, options)
43
+ exception_name = accumulated_exception_name(exception, options)
44
+
45
+ if options[:env].nil?
46
+ text = "#{exception_name} occured in background\n"
47
+ else
48
+ env = options[:env]
49
+
50
+ kontroller = env['action_controller.instance']
51
+ request = "#{env['REQUEST_METHOD']} <#{env['REQUEST_URI']}>"
52
+
53
+ text = "#{exception_name} occurred while #{request}"
54
+ text += " was processed by #{kontroller.controller_name}##{kontroller.action_name}\n" if kontroller
55
+ end
56
+
57
+ text += "Exception: #{exception.message}\n"
58
+ text += "Hostname: #{Socket.gethostname}\n"
59
+
60
+ if exception.backtrace
61
+ formatted_backtrace = "#{exception.backtrace.first(options[:backtrace_lines]).join("\n")}"
62
+ text += "Backtrace:\n#{formatted_backtrace}\n"
63
+ end
64
+ end
65
+
66
+ def accumulated_exception_name(exception, options)
67
+ errors_count = options[:accumulated_errors_count].to_i
68
+ measure_word = errors_count > 1 ? errors_count : (exception.class.to_s =~ /^[aeiou]/i ? 'An' : 'A')
69
+ "#{measure_word} #{exception.class.to_s}"
70
+ end
71
+
72
+ def default_options
73
+ {
74
+ sns_prefix: '[ERROR]',
75
+ backtrace_lines: 10
76
+ }
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,179 @@
1
+ require 'action_dispatch'
2
+ require 'active_support/core_ext/time'
3
+
4
+ module ExceptionNotifier
5
+ class TeamsNotifier < BaseNotifier
6
+ include ExceptionNotifier::BacktraceCleaner
7
+
8
+ class MissingController
9
+ def method_missing(*args, &block)
10
+ end
11
+ end
12
+
13
+ attr_accessor :httparty
14
+
15
+ def initialize(options = {})
16
+ super
17
+ @default_options = options
18
+ @httparty = HTTParty
19
+ end
20
+
21
+ def call(exception, options={})
22
+ @options = options.merge(@default_options)
23
+ @exception = exception
24
+ @backtrace = exception.backtrace ? clean_backtrace(exception) : nil
25
+
26
+ @env = @options.delete(:env)
27
+
28
+ @application_name = @options.delete(:app_name) || Rails.application.class.parent_name.underscore
29
+ @gitlab_url = @options.delete(:git_url)
30
+ @jira_url = @options.delete(:jira_url)
31
+
32
+ @webhook_url = @options.delete(:webhook_url)
33
+ raise ArgumentError.new "You must provide 'webhook_url' parameter." unless @webhook_url
34
+
35
+ unless @env.nil?
36
+ @controller = @env['action_controller.instance'] || MissingController.new
37
+
38
+ request = ActionDispatch::Request.new(@env)
39
+
40
+ @request_items = { url: request.original_url,
41
+ http_method: request.method,
42
+ ip_address: request.remote_ip,
43
+ parameters: request.filtered_parameters,
44
+ timestamp: Time.current }
45
+
46
+ if request.session["warden.user.user.key"]
47
+ current_user = User.find(request.session["warden.user.user.key"][0][0])
48
+ @request_items.merge!({ current_user: { id: current_user.id, email: current_user.email } })
49
+ end
50
+ else
51
+ @controller = @request_items = nil
52
+ end
53
+
54
+ payload = message_text
55
+
56
+ @options[:body] = payload.to_json
57
+ @options[:headers] ||= {}
58
+ @options[:headers].merge!({ 'Content-Type' => 'application/json' })
59
+ @options[:debug_output] = $stdout
60
+
61
+ @httparty.post(@webhook_url, @options)
62
+ end
63
+
64
+ private
65
+
66
+ def message_text
67
+ errors_count = @options[:accumulated_errors_count].to_i
68
+
69
+ text = {
70
+ "@type" => "MessageCard",
71
+ "@context" => "http://schema.org/extensions",
72
+ "summary" => "#{@application_name} Exception Alert",
73
+ "title" => "⚠️ Exception Occurred in #{Rails.env} ⚠️",
74
+ "sections" => [
75
+ {
76
+ "activityTitle" => "#{errors_count > 1 ? errors_count : 'A'} *#{@exception.class}* occurred" + if @controller then " in *#{controller_and_method}*." else "." end,
77
+ "activitySubtitle" => "#{@exception.message}"
78
+ }
79
+ ],
80
+ "potentialAction" => []
81
+ }
82
+
83
+ text['sections'].push details
84
+ text['potentialAction'].push gitlab_view_link unless @gitlab_url.nil?
85
+ text['potentialAction'].push gitlab_issue_link unless @gitlab_url.nil?
86
+ text['potentialAction'].push jira_issue_link unless @jira_url.nil?
87
+
88
+ text
89
+ end
90
+
91
+ def details
92
+ details = {
93
+ "title" => "Details",
94
+ "facts" => []
95
+ }
96
+
97
+ details['facts'].push message_request unless @request_items.nil?
98
+ details['facts'].push message_backtrace unless @backtrace.nil?
99
+
100
+ details
101
+ end
102
+
103
+ def message_request
104
+ {
105
+ "name" => "Request",
106
+ "value" => "#{hash_presentation(@request_items)}\n "
107
+ }
108
+ end
109
+
110
+ def message_backtrace(size = 3)
111
+ text = []
112
+ size = @backtrace.size < size ? @backtrace.size : size
113
+ text << "```"
114
+ size.times { |i| text << "* " + @backtrace[i] }
115
+ text << "```"
116
+
117
+ {
118
+ "name" => "Backtrace",
119
+ "value" => "#{text.join(" \n")}"
120
+ }
121
+ end
122
+
123
+ def gitlab_view_link
124
+ {
125
+ "@type" => "ViewAction",
126
+ "name" => "🦊 View in GitLab",
127
+ "target" => [
128
+ "#{@gitlab_url}/#{@application_name}"
129
+ ]
130
+ }
131
+ end
132
+
133
+ def gitlab_issue_link
134
+ link = [@gitlab_url, @application_name, "issues", "new"].join("/")
135
+ params = {
136
+ "issue[title]" => ["[BUG] Error 500 :",
137
+ controller_and_method,
138
+ "(#{@exception.class})",
139
+ @exception.message].compact.join(" ")
140
+ }.to_query
141
+
142
+ {
143
+ "@type" => "ViewAction",
144
+ "name" => "🦊 Create Issue in GitLab",
145
+ "target" => [
146
+ "#{link}/?#{params}"
147
+ ]
148
+ }
149
+ end
150
+
151
+ def jira_issue_link
152
+ {
153
+ "@type" => "ViewAction",
154
+ "name" => "🐞 Create Issue in Jira",
155
+ "target" => [
156
+ "#{@jira_url}/secure/CreateIssue!default.jspa"
157
+ ]
158
+ }
159
+ end
160
+
161
+ def controller_and_method
162
+ if @controller
163
+ "#{@controller.controller_name}##{@controller.action_name}"
164
+ else
165
+ ""
166
+ end
167
+ end
168
+
169
+ def hash_presentation(hash)
170
+ text = []
171
+
172
+ hash.each do |key, value|
173
+ text << "* **#{key}** : `#{value}`"
174
+ end
175
+
176
+ text.join(" \n")
177
+ end
178
+ end
179
+ end
@@ -3,12 +3,12 @@
3
3
  <%= @exception.message %>
4
4
  <%= @backtrace.first %>
5
5
 
6
- <% sections = @sections.map do |section|
7
- summary = render(section).strip
8
- unless summary.blank?
9
- title = render("title", :title => section).strip
10
- "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
11
- end
12
- end.join
13
- %>
14
- <%= raw sections %>
6
+ <% sections = @sections.map do |section|
7
+ summary = render(section).strip
8
+ unless summary.blank?
9
+ title = render("title", :title => section).strip
10
+ "#{title}\n\n#{summary.gsub(/^/, " ")}\n\n"
11
+ end
12
+ end.join
13
+ %>
14
+ <%= raw sections %>
@@ -0,0 +1,128 @@
1
+ require 'test_helper'
2
+ require 'httparty'
3
+
4
+ class GoogleChatNotifierTest < ActiveSupport::TestCase
5
+
6
+ test "should send notification if properly configured" do
7
+ options = {
8
+ :webhook_url => 'http://localhost:8000'
9
+ }
10
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
11
+ google_chat_notifier.httparty = FakeHTTParty.new
12
+
13
+ options = google_chat_notifier.call ArgumentError.new("foo"), options
14
+
15
+ body = ActiveSupport::JSON.decode options[:body]
16
+ assert body.has_key? 'text'
17
+
18
+ text = body['text'].split("\n")
19
+ assert_equal 6, text.size
20
+ assert_equal 'Application: *dummy*', text[1]
21
+ assert_equal 'An *ArgumentError* occured.', text[2]
22
+ assert_equal '*foo*', text[5]
23
+ end
24
+
25
+ test "should use 'An' for exceptions count if :accumulated_errors_count option is nil" do
26
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
27
+ exception = ArgumentError.new("foo")
28
+ google_chat_notifier.instance_variable_set(:@exception, exception)
29
+ google_chat_notifier.instance_variable_set(:@options, {})
30
+
31
+ assert_includes google_chat_notifier.send(:header), "An *ArgumentError* occured."
32
+ end
33
+
34
+ test "shoud use direct errors count if :accumulated_errors_count option is 5" do
35
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
36
+ exception = ArgumentError.new("foo")
37
+ google_chat_notifier.instance_variable_set(:@exception, exception)
38
+ google_chat_notifier.instance_variable_set(:@options, { accumulated_errors_count: 5 })
39
+
40
+ assert_includes google_chat_notifier.send(:header), "5 *ArgumentError* occured."
41
+ end
42
+
43
+ test "Message request should be formatted as hash" do
44
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
45
+ request_items = { url: 'http://test.address',
46
+ http_method: :get,
47
+ ip_address: '127.0.0.1',
48
+ parameters: '{"id"=>"foo"}',
49
+ timestamp: Time.parse('2018-08-13 12:13:24 UTC') }
50
+ google_chat_notifier.instance_variable_set(:@request_items, request_items)
51
+
52
+ message_request = google_chat_notifier.send(:message_request).join("\n")
53
+ assert_includes message_request, '* url : http://test.address'
54
+ assert_includes message_request, '* http_method : get'
55
+ assert_includes message_request, '* ip_address : 127.0.0.1'
56
+ assert_includes message_request, '* parameters : {"id"=>"foo"}'
57
+ assert_includes message_request, '* timestamp : 2018-08-13 12:13:24 UTC'
58
+ end
59
+
60
+ test 'backtrace with less than 3 lines should be displayed fully' do
61
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
62
+
63
+ backtrace = ["app/controllers/my_controller.rb:53:in `my_controller_params'", "app/controllers/my_controller.rb:34:in `update'"]
64
+ google_chat_notifier.instance_variable_set(:@backtrace, backtrace)
65
+
66
+ message_backtrace = google_chat_notifier.send(:message_backtrace).join("\n")
67
+ assert_includes message_backtrace, "* app/controllers/my_controller.rb:53:in `my_controller_params'"
68
+ assert_includes message_backtrace, "* app/controllers/my_controller.rb:34:in `update'"
69
+ end
70
+
71
+ test 'backtrace with more than 3 lines should display only top 3 lines' do
72
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
73
+
74
+ backtrace = ["app/controllers/my_controller.rb:99:in `specific_function'", "app/controllers/my_controller.rb:70:in `specific_param'", "app/controllers/my_controller.rb:53:in `my_controller_params'", "app/controllers/my_controller.rb:34:in `update'"]
75
+ google_chat_notifier.instance_variable_set(:@backtrace, backtrace)
76
+
77
+ message_backtrace = google_chat_notifier.send(:message_backtrace).join("\n")
78
+ assert_includes message_backtrace, "* app/controllers/my_controller.rb:99:in `specific_function'"
79
+ assert_includes message_backtrace, "* app/controllers/my_controller.rb:70:in `specific_param'"
80
+ assert_includes message_backtrace, "* app/controllers/my_controller.rb:53:in `my_controller_params'"
81
+ assert_not_includes message_backtrace, "* app/controllers/my_controller.rb:34:in `update'"
82
+ end
83
+
84
+ test 'Get text with backtrace and request info' do
85
+ google_chat_notifier = ExceptionNotifier::GoogleChatNotifier.new
86
+
87
+ backtrace = ["app/controllers/my_controller.rb:53:in `my_controller_params'", "app/controllers/my_controller.rb:34:in `update'"]
88
+ google_chat_notifier.instance_variable_set(:@backtrace, backtrace)
89
+
90
+ request_items = { url: 'http://test.address',
91
+ http_method: :get,
92
+ ip_address: '127.0.0.1',
93
+ parameters: '{"id"=>"foo"}',
94
+ timestamp: Time.parse('2018-08-13 12:13:24 UTC') }
95
+ google_chat_notifier.instance_variable_set(:@request_items, request_items)
96
+
97
+ google_chat_notifier.instance_variable_set(:@options, {accumulated_errors_count: 0})
98
+
99
+ google_chat_notifier.instance_variable_set(:@application_name, 'dummy')
100
+
101
+ exception = ArgumentError.new("foo")
102
+ google_chat_notifier.instance_variable_set(:@exception, exception)
103
+
104
+ text = google_chat_notifier.send(:exception_text)
105
+ expected_text = %q(
106
+ Application: *dummy*
107
+ An *ArgumentError* occured.
108
+
109
+ ⚠️ Error 500 in test ⚠️
110
+ *foo*
111
+
112
+ *Request:*
113
+ ```
114
+ * url : http://test.address
115
+ * http_method : get
116
+ * ip_address : 127.0.0.1
117
+ * parameters : {"id"=>"foo"}
118
+ * timestamp : 2018-08-13 12:13:24 UTC
119
+ ```
120
+
121
+ *Backtrace:*
122
+ ```
123
+ * app/controllers/my_controller.rb:53:in `my_controller_params'
124
+ * app/controllers/my_controller.rb:34:in `update'
125
+ ```)
126
+ assert_equal text, expected_text
127
+ end
128
+ end
@@ -5,7 +5,7 @@ class ErrorGroupTest < ActiveSupport::TestCase
5
5
  setup do
6
6
  module TestModule
7
7
  include ExceptionNotifier::ErrorGrouping
8
- @@error_grouping_cache = ActiveSupport::Cache::FileStore.new("test/dummy/tmp/cache")
8
+ @@error_grouping_cache = ActiveSupport::Cache::FileStore.new("test/dummy/tmp/non_default_location")
9
9
  end
10
10
 
11
11
  @exception = RuntimeError.new("ERROR")
@@ -0,0 +1,126 @@
1
+ require 'test_helper'
2
+ require 'aws-sdk-sns'
3
+
4
+ class SnsNotifierTest < ActiveSupport::TestCase
5
+ def setup
6
+ @exception = fake_exception
7
+ @exception.stubs(:class).returns('MyException')
8
+ @exception.stubs(:backtrace).returns(fake_backtrace)
9
+ @exception.stubs(:message).returns("undefined method 'method=' for Empty")
10
+ @options = {
11
+ access_key_id: 'my-access_key_id',
12
+ secret_access_key: 'my-secret_access_key',
13
+ region: 'us-east',
14
+ topic_arn: 'topicARN',
15
+ sns_prefix: '[App Exception]',
16
+ }
17
+ Socket.stubs(:gethostname).returns('example.com')
18
+ end
19
+
20
+ # initialize
21
+
22
+ test 'should initialize aws notifier with received params' do
23
+ Aws::SNS::Client.expects(:new).with(
24
+ region: 'us-east',
25
+ access_key_id: 'my-access_key_id',
26
+ secret_access_key: 'my-secret_access_key'
27
+ )
28
+
29
+ ExceptionNotifier::SnsNotifier.new(@options)
30
+ end
31
+
32
+ test 'should raise an exception if region is not received' do
33
+ @options[:region] = nil
34
+
35
+ error = assert_raises ArgumentError do
36
+ ExceptionNotifier::SnsNotifier.new(@options)
37
+ end
38
+ assert_equal "You must provide 'region' option", error.message
39
+ end
40
+
41
+ test 'should raise an exception on publish if access_key_id is not received' do
42
+ @options[:access_key_id] = nil
43
+ error = assert_raises ArgumentError do
44
+ ExceptionNotifier::SnsNotifier.new(@options)
45
+ end
46
+
47
+ assert_equal "You must provide 'access_key_id' option", error.message
48
+ end
49
+
50
+ test 'should raise an exception on publish if secret_access_key is not received' do
51
+ @options[:secret_access_key] = nil
52
+ error = assert_raises ArgumentError do
53
+ ExceptionNotifier::SnsNotifier.new(@options)
54
+ end
55
+
56
+ assert_equal "You must provide 'secret_access_key' option", error.message
57
+ end
58
+
59
+ # call
60
+
61
+ test 'should send a sns notification in background' do
62
+ Aws::SNS::Client.any_instance.expects(:publish).with(
63
+ {
64
+ topic_arn: "topicARN",
65
+ message: "3 MyException occured in background\n"\
66
+ "Exception: undefined method 'method=' for Empty\n"\
67
+ "Hostname: example.com\n"\
68
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
69
+ subject: "[App Exception] - 3 MyException occurred"
70
+ })
71
+
72
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
73
+ sns_notifier.call(@exception, { accumulated_errors_count: 3 })
74
+ end
75
+
76
+ test 'should send a sns notification with controller#action information' do
77
+ ExamplesController.any_instance.stubs(:action_name).returns('index')
78
+
79
+ Aws::SNS::Client.any_instance.expects(:publish).with(
80
+ {
81
+ topic_arn: "topicARN",
82
+ message: "A MyException occurred while GET </examples> "\
83
+ "was processed by examples#index\n"\
84
+ "Exception: undefined method 'method=' for Empty\n"\
85
+ "Hostname: example.com\n"\
86
+ "Backtrace:\n#{fake_backtrace.join("\n")}\n",
87
+ subject: "[App Exception] - A MyException occurred"
88
+ })
89
+
90
+ sns_notifier = ExceptionNotifier::SnsNotifier.new(@options)
91
+ sns_notifier.call(@exception,
92
+ env: {
93
+ 'REQUEST_METHOD' => 'GET',
94
+ 'REQUEST_URI' => '/examples',
95
+ 'action_controller.instance' => ExamplesController.new
96
+ }
97
+ )
98
+ end
99
+
100
+ private
101
+
102
+ class ExamplesController < ActionController::Base; end
103
+
104
+ def fake_exception
105
+ begin
106
+ 1 / 0
107
+ rescue Exception => e
108
+ e
109
+ end
110
+ end
111
+
112
+ def fake_exception_without_backtrace
113
+ StandardError.new('my custom error')
114
+ end
115
+
116
+ def fake_backtrace
117
+ [
118
+ 'backtrace line 1',
119
+ 'backtrace line 2',
120
+ 'backtrace line 3',
121
+ 'backtrace line 4',
122
+ 'backtrace line 5',
123
+ 'backtrace line 6'
124
+ ]
125
+ end
126
+ end
@@ -0,0 +1,93 @@
1
+ require 'test_helper'
2
+ require 'httparty'
3
+
4
+ class TeamsNotifierTest < ActiveSupport::TestCase
5
+
6
+ test "should send notification if properly configured" do
7
+ options = {
8
+ :webhook_url => 'http://localhost:8000'
9
+ }
10
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
11
+ teams_notifier.httparty = FakeHTTParty.new
12
+
13
+ options = teams_notifier.call ArgumentError.new("foo"), options
14
+
15
+ body = ActiveSupport::JSON.decode options[:body]
16
+ assert body.has_key? 'title'
17
+ assert body.has_key? 'sections'
18
+
19
+ sections = body['sections']
20
+ header = sections[0]
21
+
22
+ assert_equal 2, sections.size
23
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
24
+ assert_equal 'foo', header['activitySubtitle']
25
+ end
26
+
27
+ test "should send notification with create gitlab issue link if specified" do
28
+ options = {
29
+ :webhook_url => 'http://localhost:8000',
30
+ :git_url => 'github.com/aschen'
31
+ }
32
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
33
+ teams_notifier.httparty = FakeHTTParty.new
34
+
35
+ options = teams_notifier.call ArgumentError.new("foo"), options
36
+
37
+ body = ActiveSupport::JSON.decode options[:body]
38
+
39
+ potential_action = body['potentialAction']
40
+ assert_equal 2, potential_action.size
41
+ assert_equal '🦊 View in GitLab', potential_action[0]['name']
42
+ assert_equal '🦊 Create Issue in GitLab', potential_action[1]['name']
43
+ end
44
+
45
+ test 'should add other HTTParty options to params' do
46
+ options = {
47
+ :webhook_url => 'http://localhost:8000',
48
+ :username => "Test Bot",
49
+ :avatar => 'http://site.com/icon.png',
50
+ :basic_auth => {
51
+ :username => 'clara',
52
+ :password => 'password'
53
+ }
54
+ }
55
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
56
+ teams_notifier.httparty = FakeHTTParty.new
57
+
58
+ options = teams_notifier.call ArgumentError.new("foo"), options
59
+
60
+ assert options.has_key? :basic_auth
61
+ assert 'clara', options[:basic_auth][:username]
62
+ assert 'password', options[:basic_auth][:password]
63
+ end
64
+
65
+ test "should use 'A' for exceptions count if :accumulated_errors_count option is nil" do
66
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
67
+ exception = ArgumentError.new("foo")
68
+ teams_notifier.instance_variable_set(:@exception, exception)
69
+ teams_notifier.instance_variable_set(:@options, {})
70
+
71
+ message_text = teams_notifier.send(:message_text)
72
+ header = message_text['sections'][0]
73
+ assert_equal 'A *ArgumentError* occurred.', header['activityTitle']
74
+ end
75
+
76
+ test "should use direct errors count if :accumulated_errors_count option is 5" do
77
+ teams_notifier = ExceptionNotifier::TeamsNotifier.new
78
+ exception = ArgumentError.new("foo")
79
+ teams_notifier.instance_variable_set(:@exception, exception)
80
+ teams_notifier.instance_variable_set(:@options, { accumulated_errors_count: 5 })
81
+ message_text = teams_notifier.send(:message_text)
82
+ header = message_text['sections'][0]
83
+ assert_equal '5 *ArgumentError* occurred.', header['activityTitle']
84
+ end
85
+ end
86
+
87
+ class FakeHTTParty
88
+
89
+ def post(url, options)
90
+ return options
91
+ end
92
+
93
+ end
@@ -110,6 +110,35 @@ class ExceptionNotifierTest < ActiveSupport::TestCase
110
110
  assert_equal @notifier_calls, 1
111
111
  end
112
112
 
113
+ test "should not send notification if subclass of one of ignored exceptions" do
114
+ ExceptionNotifier.register_exception_notifier(:test, @test_notifier)
115
+
116
+ class StandardErrorSubclass < StandardError
117
+ end
118
+
119
+ exception = StandardErrorSubclass.new
120
+
121
+ ExceptionNotifier.notify_exception(exception, {:notifiers => :test})
122
+ assert_equal @notifier_calls, 1
123
+
124
+ ExceptionNotifier.notify_exception(exception, {:notifiers => :test, :ignore_exceptions => 'StandardError' })
125
+ assert_equal @notifier_calls, 1
126
+ end
127
+
128
+ test "should call received block" do
129
+ @block_called = false
130
+ notifier = lambda { |exception, options, &block| block.call }
131
+ ExceptionNotifier.register_exception_notifier(:test, notifier)
132
+
133
+ exception = ExceptionOne.new
134
+
135
+ ExceptionNotifier.notify_exception(exception) do
136
+ @block_called = true
137
+ end
138
+
139
+ assert @block_called
140
+ end
141
+
113
142
  test "should not call group_error! or send_notification? if error_grouping false" do
114
143
  exception = StandardError.new
115
144
  ExceptionNotifier.expects(:group_error!).never
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exception_notification
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.2
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-08-12 00:00:00.000000000 Z
12
+ date: 2018-11-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: actionmailer
@@ -231,6 +231,20 @@ dependencies:
231
231
  - - ">="
232
232
  - !ruby/object:Gem::Version
233
233
  version: 1.0.0
234
+ - !ruby/object:Gem::Dependency
235
+ name: aws-sdk-sns
236
+ requirement: !ruby/object:Gem::Requirement
237
+ requirements:
238
+ - - "~>"
239
+ - !ruby/object:Gem::Version
240
+ version: '1'
241
+ type: :development
242
+ prerelease: false
243
+ version_requirements: !ruby/object:Gem::Requirement
244
+ requirements:
245
+ - - "~>"
246
+ - !ruby/object:Gem::Version
247
+ version: '1'
234
248
  description:
235
249
  email: smartinez87@gmail.com
236
250
  executables: []
@@ -256,6 +270,7 @@ files:
256
270
  - gemfiles/rails4_1.gemfile
257
271
  - gemfiles/rails4_2.gemfile
258
272
  - gemfiles/rails5_0.gemfile
273
+ - gemfiles/rails5_1.gemfile
259
274
  - lib/exception_notification.rb
260
275
  - lib/exception_notification/rack.rb
261
276
  - lib/exception_notification/rails.rb
@@ -265,6 +280,7 @@ files:
265
280
  - lib/exception_notifier/base_notifier.rb
266
281
  - lib/exception_notifier/campfire_notifier.rb
267
282
  - lib/exception_notifier/email_notifier.rb
283
+ - lib/exception_notifier/google_chat_notifier.rb
268
284
  - lib/exception_notifier/hipchat_notifier.rb
269
285
  - lib/exception_notifier/irc_notifier.rb
270
286
  - lib/exception_notifier/mattermost_notifier.rb
@@ -272,6 +288,8 @@ files:
272
288
  - lib/exception_notifier/modules/error_grouping.rb
273
289
  - lib/exception_notifier/notifier.rb
274
290
  - lib/exception_notifier/slack_notifier.rb
291
+ - lib/exception_notifier/sns_notifier.rb
292
+ - lib/exception_notifier/teams_notifier.rb
275
293
  - lib/exception_notifier/views/exception_notifier/_backtrace.html.erb
276
294
  - lib/exception_notifier/views/exception_notifier/_backtrace.text.erb
277
295
  - lib/exception_notifier/views/exception_notifier/_data.html.erb
@@ -346,12 +364,15 @@ files:
346
364
  - test/exception_notification/rack_test.rb
347
365
  - test/exception_notifier/campfire_notifier_test.rb
348
366
  - test/exception_notifier/email_notifier_test.rb
367
+ - test/exception_notifier/google_chat_notifier_test.rb
349
368
  - test/exception_notifier/hipchat_notifier_test.rb
350
369
  - test/exception_notifier/irc_notifier_test.rb
351
370
  - test/exception_notifier/mattermost_notifier_test.rb
352
371
  - test/exception_notifier/modules/error_grouping_test.rb
353
372
  - test/exception_notifier/sidekiq_test.rb
354
373
  - test/exception_notifier/slack_notifier_test.rb
374
+ - test/exception_notifier/sns_notifier_test.rb
375
+ - test/exception_notifier/teams_notifier_test.rb
355
376
  - test/exception_notifier/webhook_notifier_test.rb
356
377
  - test/exception_notifier_test.rb
357
378
  - test/test_helper.rb
@@ -375,7 +396,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
375
396
  version: 1.8.11
376
397
  requirements: []
377
398
  rubyforge_project:
378
- rubygems_version: 2.5.1
399
+ rubygems_version: 2.5.2
379
400
  signing_key:
380
401
  specification_version: 4
381
402
  summary: Exception notification for Rails apps
@@ -435,12 +456,15 @@ test_files:
435
456
  - test/exception_notification/rack_test.rb
436
457
  - test/exception_notifier/campfire_notifier_test.rb
437
458
  - test/exception_notifier/email_notifier_test.rb
459
+ - test/exception_notifier/google_chat_notifier_test.rb
438
460
  - test/exception_notifier/hipchat_notifier_test.rb
439
461
  - test/exception_notifier/irc_notifier_test.rb
440
462
  - test/exception_notifier/mattermost_notifier_test.rb
441
463
  - test/exception_notifier/modules/error_grouping_test.rb
442
464
  - test/exception_notifier/sidekiq_test.rb
443
465
  - test/exception_notifier/slack_notifier_test.rb
466
+ - test/exception_notifier/sns_notifier_test.rb
467
+ - test/exception_notifier/teams_notifier_test.rb
444
468
  - test/exception_notifier/webhook_notifier_test.rb
445
469
  - test/exception_notifier_test.rb
446
470
  - test/test_helper.rb