exception_notification 4.2.2 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
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