From 1e13005fb54c186b05cd63153c7ce05afee1110a Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 Nov 2024 15:58:05 +1100 Subject: [PATCH 01/11] Enable Bugsnag testing in any Rails env with ENV var --- config/initializers/bugsnag.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/config/initializers/bugsnag.rb b/config/initializers/bugsnag.rb index bd90c70699..572681f1db 100644 --- a/config/initializers/bugsnag.rb +++ b/config/initializers/bugsnag.rb @@ -6,5 +6,9 @@ Bugsnag.configure do |config| config.logger = Logger.new(STDOUT) config.logger.level = Logger::ERROR end - config.notify_release_stages = %w(production staging) + + # If you want to notify Bugsnag in dev or test then set the env var: + # spring stop + # BUGSNAG=true ./bin/rails console + config.notify_release_stages = %w(production staging) unless ENV["BUGSNAG"] end From 1b4efd216485ae21798d1a7330fc91300d699512 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 14 Nov 2024 15:30:41 +1100 Subject: [PATCH 02/11] Revert "Notify bugsnag on 404 errors" This reverts commit 2eec3d625a8f1c0b06560cbdc67b0e1646d03b3c. We started tracking 404 errors out of interest without an actual problem to solve. Now we face rate-limiting in our Bugsnag account. And we didn't use these 404 reports in the two years this code was active. We don't even act on all 500 errors. So while our resources are so constrained, let's keep our focus on the severe errors and user reports and ignore the rest. 404 errors are mostly generated by vulnerability scanners. --- app/controllers/errors_controller.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index 262325f029..f071ae64f5 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -4,11 +4,6 @@ class ErrorsController < ApplicationController layout "errors" def not_found - Bugsnag.notify("404") do |event| - event.severity = "info" - - event.add_metadata(:request, :env, request.env) - end render status: :not_found, formats: :html end From 97d41c230ea86028e0914821ec7b21878d3405b4 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 14 Nov 2024 16:08:55 +1100 Subject: [PATCH 03/11] Add simpler Bugsnag wrapper --- app/services/alert.rb | 31 +++++++++++++++++++++++++++++++ spec/services/alert_spec.rb | 25 +++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 app/services/alert.rb create mode 100644 spec/services/alert_spec.rb diff --git a/app/services/alert.rb b/app/services/alert.rb new file mode 100644 index 0000000000..7bd981c56e --- /dev/null +++ b/app/services/alert.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +# A handy wrapper around error reporting libraries like Bugsnag. +# +# Bugsnag's API is great for general purpose but overly complex for our use. +# It also changes over time and we often make mistakes using it. So this class +# aims at: +# +# * Abstracting from Bugsnag, open for other services. +# * Simpler interface to reduce user error. +# * Central place to update Bugsnag API usage when it changes. +# +class Alert + # Alert Bugsnag with additional metadata to appear in tabs. + # + # Alert.raise( + # "Invalid order during checkout", + # { + # order: { number: "ABC123", state: "awaiting_return" }, + # env: { referer: "example.com" } + # } + # ) + def self.raise(error, metadata, &block) + Bugsnag.notify(error) do |payload| + metadata.each do |name, data| + payload.add_metadata(name, data) + end + block.call(payload) + end + end +end diff --git a/spec/services/alert_spec.rb b/spec/services/alert_spec.rb new file mode 100644 index 0000000000..4df7c66ea9 --- /dev/null +++ b/spec/services/alert_spec.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Alert do + it "notifies Bugsnag" do + expect(Bugsnag).to receive(:notify).with("hey") + + Alert.raise("hey") + end + + it "adds context" do + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( + :order, { number: "ABC123" } + ) + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( + :env, { referer: "example.com" } + ) + + Alert.raise( + "hey", + { order: { number: "ABC123" }, env: { referer: "example.com" } } + ) + end +end From 73a1698aad4233ff30e27a13faa89f45823dfa06 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 19 Nov 2024 17:26:59 +1100 Subject: [PATCH 04/11] Add live test for Bugsnag Needs human to review Bugsnag account. --- app/services/alert.rb | 2 +- .../reaches_the_Bugsnag_service_for_real.yml | 291 ++++++++++++++++++ spec/services/alert_spec.rb | 23 ++ spec/support/vcr_setup.rb | 1 + 4 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/vcr_cassettes/Alert/reaches_the_Bugsnag_service_for_real.yml diff --git a/app/services/alert.rb b/app/services/alert.rb index 7bd981c56e..d43eae9b27 100644 --- a/app/services/alert.rb +++ b/app/services/alert.rb @@ -20,7 +20,7 @@ class Alert # env: { referer: "example.com" } # } # ) - def self.raise(error, metadata, &block) + def self.raise(error, metadata = {}, &block) Bugsnag.notify(error) do |payload| metadata.each do |name, data| payload.add_metadata(name, data) diff --git a/spec/fixtures/vcr_cassettes/Alert/reaches_the_Bugsnag_service_for_real.yml b/spec/fixtures/vcr_cassettes/Alert/reaches_the_Bugsnag_service_for_real.yml new file mode 100644 index 0000000000..7f541b8a07 --- /dev/null +++ b/spec/fixtures/vcr_cassettes/Alert/reaches_the_Bugsnag_service_for_real.yml @@ -0,0 +1,291 @@ +--- +http_interactions: +- request: + method: post + uri: https://notify.bugsnag.com/ + body: + encoding: UTF-8 + string: '{"apiKey":"","notifier":{"name":"Ruby Bugsnag + Notifier","version":"6.26.4","url":"https://www.bugsnag.com"},"payloadVersion":"4.0","events":[{"app":{"version":null,"releaseStage":"test","type":"rails"},"breadcrumbs":[{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.385Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.387Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.389Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.390Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.437Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.492Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.527Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.546Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.585Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.593Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.796Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.814Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.886Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"Spree::Country Exists?","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.888Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"Spree::Country Exists?","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.890Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.890Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"Spree::Country Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"name\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.891Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.894Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.901Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"SCHEMA","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.904Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"Spree::Preference Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"key\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.905Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"Spree::Country Load","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","binds":"{\"iso\":\"?\",\"LIMIT\":\"?\"}","connection_id":42100},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"Read + cache","type":"process","metaData":{"hit":true,"event_name":"cache_read.active_support","event_id":"4779ed9ba580ae3d3290"},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"Read + cache","type":"process","metaData":{"hit":true,"event_name":"cache_read.active_support","event_id":"4779ed9ba580ae3d3290"},"timestamp":"2024-11-20T04:51:11.933Z"},{"name":"ActiveRecord + SQL query","type":"process","metaData":{"name":"TRANSACTION","event_name":"sql.active_record","event_id":"4779ed9ba580ae3d3290","connection_id":42100},"timestamp":"2024-11-20T04:51:11.970Z"}],"device":{"hostname":"blackbox","runtimeVersions":{"ruby":"3.1.4","rails":"7.0.8","rack":"2.2.9"},"time":"2024-11-20T04:51:11.978Z"},"exceptions":[{"errorClass":"RuntimeError","message":"Testing + Bugsnag from RSpec","stacktrace":[{"lineNumber":129,"file":"gems/bugsnag-6.26.4/lib/bugsnag/report.rb","method":"initialize","code":{"126":" self.configuration + = passed_configuration","127":"","128":" @original_error = exception","129":" self.raw_exceptions + = generate_raw_exceptions(exception)","130":" self.exceptions = generate_exception_list","131":" @errors + = generate_error_list","132":""}},{"lineNumber":90,"file":"gems/bugsnag-6.26.4/lib/bugsnag.rb","method":"new","code":{"87":"","88":" exception + = NIL_EXCEPTION_DESCRIPTION if exception.nil?","89":"","90":" report + = Report.new(exception, configuration, auto_notify)","91":"","92":" # + If this is an auto_notify we yield the block before the any middleware is + run","93":" begin"}},{"lineNumber":90,"file":"gems/bugsnag-6.26.4/lib/bugsnag.rb","method":"notify","code":{"87":"","88":" exception + = NIL_EXCEPTION_DESCRIPTION if exception.nil?","89":"","90":" report + = Report.new(exception, configuration, auto_notify)","91":"","92":" # + If this is an auto_notify we yield the block before the any middleware is + run","93":" begin"}},{"lineNumber":29,"inProject":true,"file":"app/services/alert.rb","method":"raise","code":{"26":" # }","27":" # )","28":" def + self.raise(error, metadata = {}, &block)","29":" Bugsnag.notify(error) + do |payload|","30":" metadata.each do |name, data|","31":" payload.add_metadata(name, + data)","32":" end"}},{"lineNumber":34,"inProject":true,"file":"spec/services/alert_spec.rb","method":"block + (2 levels) in
","code":{"31":" config.delivery_method = :synchronous","32":" end","33":"","34":" Alert.raise(","35":" \"Testing + Bugsnag from RSpec\",","36":" { RSpec: { file: __FILE__ }, env: { BUGSNAG: + ENV[\"BUGSNAG\"] } }","37":" )"}},{"lineNumber":263,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"260":" begin","261":" run_before_example","262":" RSpec.current_scope + = :example","263":" @example_group_instance.instance_exec(self, + &@example_block)","264":"","265":" if pending?","266":" Pending.mark_fixed! + self"}},{"lineNumber":263,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block + in run","code":{"260":" begin","261":" run_before_example","262":" RSpec.current_scope + = :example","263":" @example_group_instance.instance_exec(self, + &@example_block)","264":"","265":" if pending?","266":" Pending.mark_fixed! + self"}},{"lineNumber":511,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block + in with_around_and_singleton_context_hooks","code":{"508":" def with_around_and_singleton_context_hooks","509":" singleton_context_hooks_host + = example_group_instance.singleton_class","510":" singleton_context_hooks_host.run_before_context_hooks(example_group_instance)","511":" with_around_example_hooks + { yield }","512":" ensure","513":" singleton_context_hooks_host.run_after_context_hooks(example_group_instance)","514":" end"}},{"lineNumber":468,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"block + in with_around_example_hooks","code":{"465":"","466":" def with_around_example_hooks","467":" RSpec.current_scope + = :before_example_hook","468":" hooks.run(:around, :example, self) + { yield }","469":" rescue Support::AllExceptionsExceptOnesWeMustNotRescue + => e","470":" set_exception(e)","471":" end"}},{"lineNumber":486,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + in run","code":{"483":" case position","484":" when + :before then run_example_hooks_for(example_or_group, :before, :reverse_each)","485":" when + :after then run_example_hooks_for(example_or_group, :after, :each)","486":" when + :around then run_around_example_hooks_for(example_or_group) { yield }","487":" end","488":" end","489":" end"}},{"lineNumber":626,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + in run_around_example_hooks_for","code":{"623":"","624":" return + yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy`","625":"","626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call"}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" # + Calls the proc and notes that the example has been executed.","350":" def + call(*args, &block)","351":" @executed = true","352":" @proc.call(*args, + &block)","353":" end","354":" alias run call","355":""}},{"lineNumber":75,"file":"gems/rspec-rails-6.1.2/lib/rspec/rails/adapters.rb","method":"block + (2 levels) in ","code":{"72":"","73":" group.around + do |example|","74":" before_setup","75":" example.run","76":" after_teardown","77":" end","78":" end"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" # + @private","388":" class AroundHook < Hook","389":" def execute_with(example, + procsy)","390":" example.instance_exec(procsy, &block)","391":" return + if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description} + did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + (2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" # + Calls the proc and notes that the example has been executed.","350":" def + call(*args, &block)","351":" @executed = true","352":" @proc.call(*args, + &block)","353":" end","354":" alias run call","355":""}},{"lineNumber":153,"inProject":true,"file":"spec/base_spec_helper.rb","method":"block + (3 levels) in
","code":{"150":"","151":" # Reset locale for all specs.","152":" config.around(:each) + do |example|","153":" I18n.with_locale(:en_AU) { example.run }","154":" end","155":"","156":" # + Reset all feature toggles to prevent leaking."}},{"lineNumber":351,"file":"gems/i18n-1.14.5/lib/i18n.rb","method":"with_locale","code":{"348":" current_locale + = self.locale","349":" self.locale = tmp_locale","350":" begin","351":" yield","352":" ensure","353":" self.locale + = current_locale","354":" end"}},{"lineNumber":153,"inProject":true,"file":"spec/base_spec_helper.rb","method":"block + (2 levels) in
","code":{"150":"","151":" # Reset locale for all specs.","152":" config.around(:each) + do |example|","153":" I18n.with_locale(:en_AU) { example.run }","154":" end","155":"","156":" # + Reset all feature toggles to prevent leaking."}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" # + @private","388":" class AroundHook < Hook","389":" def execute_with(example, + procsy)","390":" example.instance_exec(procsy, &block)","391":" return + if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description} + did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + (2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" # + Calls the proc and notes that the example has been executed.","350":" def + call(*args, &block)","351":" @executed = true","352":" @proc.call(*args, + &block)","353":" end","354":" alias run call","355":""}},{"lineNumber":39,"file":"gems/webmock-3.23.1/lib/webmock/rspec.rb","method":"block + (2 levels) in
","code":{"36":" end","37":"","38":" config.around(:each) + do |example|","39":" example.run","40":" WebMock.reset!","41":" end","42":"}"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" # + @private","388":" class AroundHook < Hook","389":" def execute_with(example, + procsy)","390":" example.instance_exec(procsy, &block)","391":" return + if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description} + did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + (2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" # + Calls the proc and notes that the example has been executed.","350":" def + call(*args, &block)","351":" @executed = true","352":" @proc.call(*args, + &block)","353":" end","354":" alias run call","355":""}},{"lineNumber":124,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"block + in run","code":{"121":" example.metadata[:retry_exceptions] ||= []","122":"","123":" example.clear_exception","124":" ex.run","125":"","126":" self.attempts + += 1","127":""}},{"lineNumber":110,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"loop","code":{"107":" def + run","108":" example = current_example","109":"","110":" loop do","111":" if + attempts > 0","112":" RSpec.configuration.formatters.each { |f| f.retry(example) + if f.respond_to? :retry }","113":" if verbose_retry?"}},{"lineNumber":110,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"run","code":{"107":" def + run","108":" example = current_example","109":"","110":" loop do","111":" if + attempts > 0","112":" RSpec.configuration.formatters.each { |f| f.retry(example) + if f.respond_to? :retry }","113":" if verbose_retry?"}},{"lineNumber":12,"file":"gems/rspec-retry-0.6.2/lib/rspec_ext/rspec_ext.rb","method":"run_with_retry","code":{"9":"","10":" class + Procsy","11":" def run_with_retry(opts = {})","12":" RSpec::Retry.new(self, + opts).run","13":" end","14":"","15":" def attempts"}},{"lineNumber":37,"file":"gems/rspec-retry-0.6.2/lib/rspec/retry.rb","method":"block + (2 levels) in setup","code":{"34":" config.add_setting :retry_callback, + :default => nil","35":"","36":" config.around(:each) do |ex|","37":" ex.run_with_retry","38":" end","39":" end","40":" end"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":457,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"instance_exec","code":{"454":"","455":" # + @private","456":" def instance_exec(*args, &block)","457":" @example_group_instance.instance_exec(*args, + &block)","458":" end","459":"","460":" private"}},{"lineNumber":390,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"execute_with","code":{"387":" # + @private","388":" class AroundHook < Hook","389":" def execute_with(example, + procsy)","390":" example.instance_exec(procsy, &block)","391":" return + if procsy.executed?","392":" Pending.mark_skipped!(example,","393":" \"#{hook_description} + did not execute the example\")"}},{"lineNumber":628,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"block + (2 levels) in run_around_example_hooks_for","code":{"625":"","626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call","630":" end","631":""}},{"lineNumber":352,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"call","code":{"349":" # + Calls the proc and notes that the example has been executed.","350":" def + call(*args, &block)","351":" @executed = true","352":" @proc.call(*args, + &block)","353":" end","354":" alias run call","355":""}},{"lineNumber":629,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"run_around_example_hooks_for","code":{"626":" initial_procsy + = Example::Procsy.new(example) { yield }","627":" hooks.inject(initial_procsy) + do |procsy, around_hook|","628":" procsy.wrap { around_hook.execute_with(example, + procsy) }","629":" end.call","630":" end","631":"","632":" if + respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class)"}},{"lineNumber":486,"file":"gems/rspec-core-3.13.0/lib/rspec/core/hooks.rb","method":"run","code":{"483":" case + position","484":" when :before then run_example_hooks_for(example_or_group, + :before, :reverse_each)","485":" when :after then run_example_hooks_for(example_or_group, + :after, :each)","486":" when :around then run_around_example_hooks_for(example_or_group) + { yield }","487":" end","488":" end","489":" end"}},{"lineNumber":468,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"with_around_example_hooks","code":{"465":"","466":" def + with_around_example_hooks","467":" RSpec.current_scope = :before_example_hook","468":" hooks.run(:around, + :example, self) { yield }","469":" rescue Support::AllExceptionsExceptOnesWeMustNotRescue + => e","470":" set_exception(e)","471":" end"}},{"lineNumber":511,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"with_around_and_singleton_context_hooks","code":{"508":" def + with_around_and_singleton_context_hooks","509":" singleton_context_hooks_host + = example_group_instance.singleton_class","510":" singleton_context_hooks_host.run_before_context_hooks(example_group_instance)","511":" with_around_example_hooks + { yield }","512":" ensure","513":" singleton_context_hooks_host.run_after_context_hooks(example_group_instance)","514":" end"}},{"lineNumber":259,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example.rb","method":"run","code":{"256":" if + skipped?","257":" Pending.mark_pending! self, skip","258":" elsif + !RSpec.configuration.dry_run?","259":" with_around_and_singleton_context_hooks + do","260":" begin","261":" run_before_example","262":" RSpec.current_scope + = :example"}},{"lineNumber":646,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"block + in run_examples","code":{"643":" next if RSpec.world.wants_to_quit","644":" instance + = new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)","646":" succeeded + = example.run(instance, reporter)","647":" if !succeeded && reporter.fail_fast_limit_met?","648":" RSpec.world.wants_to_quit + = true","649":" end"}},{"lineNumber":642,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"map","code":{"639":"","640":" # + @private","641":" def self.run_examples(reporter)","642":" ordering_strategy.order(filtered_examples).map + do |example|","643":" next if RSpec.world.wants_to_quit","644":" instance + = new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)"}},{"lineNumber":642,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"run_examples","code":{"639":"","640":" # + @private","641":" def self.run_examples(reporter)","642":" ordering_strategy.order(filtered_examples).map + do |example|","643":" next if RSpec.world.wants_to_quit","644":" instance + = new(example.inspect_output)","645":" set_ivars(instance, before_context_ivars)"}},{"lineNumber":607,"file":"gems/rspec-core-3.13.0/lib/rspec/core/example_group.rb","method":"run","code":{"604":" begin","605":" RSpec.current_scope + = :before_context_hook","606":" run_before_context_hooks(new(''before(:context) + hook'')) if should_run_context_hooks","607":" result_for_this_group + = run_examples(reporter)","608":" results_for_descendants = ordering_strategy.order(children).map + { |child| child.run(reporter) }.all?","609":" result_for_this_group + && results_for_descendants","610":" rescue Pending::SkipDeclaredInExample + => ex"}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block + (3 levels) in run_specs","code":{"118":" return @configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map + { |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"map","code":{"118":" return + @configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map + { |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":121,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block + (2 levels) in run_specs","code":{"118":" return @configuration.failure_exit_code","119":" end","120":"","121":" example_groups.map + { |g| g.run(reporter) }.all?","122":" end","123":" end","124":""}},{"lineNumber":2091,"file":"gems/rspec-core-3.13.0/lib/rspec/core/configuration.rb","method":"with_suite_hooks","code":{"2088":" begin","2089":" RSpec.current_scope + = :before_suite_hook","2090":" run_suite_hooks(\"a `before(:suite)` + hook\", @before_suite_hooks)","2091":" yield","2092":" ensure","2093":" RSpec.current_scope + = :after_suite_hook","2094":" run_suite_hooks(\"an `after(:suite)` + hook\", @after_suite_hooks)"}},{"lineNumber":116,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"block + in run_specs","code":{"113":" def run_specs(example_groups)","114":" examples_count + = @world.example_count(example_groups)","115":" examples_passed = @configuration.reporter.report(examples_count) + do |reporter|","116":" @configuration.with_suite_hooks do","117":" if + examples_count == 0 && @configuration.fail_if_no_examples","118":" return + @configuration.failure_exit_code","119":" end"}},{"lineNumber":74,"file":"gems/rspec-core-3.13.0/lib/rspec/core/reporter.rb","method":"report","code":{"71":" def + report(expected_example_count)","72":" start(expected_example_count)","73":" begin","74":" yield + self","75":" ensure","76":" finish","77":" end"}},{"lineNumber":115,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run_specs","code":{"112":" # failed.","113":" def + run_specs(example_groups)","114":" examples_count = @world.example_count(example_groups)","115":" examples_passed + = @configuration.reporter.report(examples_count) do |reporter|","116":" @configuration.with_suite_hooks + do","117":" if examples_count == 0 && @configuration.fail_if_no_examples","118":" return + @configuration.failure_exit_code"}},{"lineNumber":89,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run","code":{"86":" setup(err, + out)","87":" return @configuration.reporter.exit_early(exit_code) if + RSpec.world.wants_to_quit","88":"","89":" run_specs(@world.ordered_example_groups).tap + do","90":" persist_example_statuses","91":" end","92":" end"}},{"lineNumber":71,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"run","code":{"68":" if + options.options[:runner]","69":" options.options[:runner].call(options, + err, out)","70":" else","71":" new(options).run(err, out)","72":" end","73":" end","74":""}},{"lineNumber":45,"file":"gems/rspec-core-3.13.0/lib/rspec/core/runner.rb","method":"invoke","code":{"42":" # + code.","43":" def self.invoke","44":" disable_autorun!","45":" status + = run(ARGV, $stderr, $stdout).to_i","46":" exit(status) if status != + 0","47":" end","48":""}},{"lineNumber":4,"file":"gems/rspec-core-3.13.0/exe/rspec","method":"
","code":{"1":"#!/usr/bin/env + ruby","2":"","3":"require ''rspec/core''","4":"RSpec::Core::Runner.invoke"}},{"lineNumber":18,"file":"gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb","method":"load","code":{"15":"","16":" def + call","17":" ::RSpec.configuration.start_time = Time.now if defined?(::RSpec.configuration.start_time)","18":" load + Gem.bin_path(gem_name, exec_name)","19":" end","20":" end","21":""}},{"lineNumber":18,"file":"gems/spring-commands-rspec-1.0.4/lib/spring/commands/rspec.rb","method":"call","code":{"15":"","16":" def + call","17":" ::RSpec.configuration.start_time = Time.now if defined?(::RSpec.configuration.start_time)","18":" load + Gem.bin_path(gem_name, exec_name)","19":" end","20":" end","21":""}},{"lineNumber":38,"file":"gems/spring-4.2.1/lib/spring/command_wrapper.rb","method":"call","code":{"35":"","36":" def + call","37":" if command.respond_to?(:call)","38":" command.call","39":" else","40":" load + exec","41":" end"}},{"lineNumber":226,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"block + in serve","code":{"223":" invoke_after_fork_callbacks","224":" shush_backtraces","225":"","226":" command.call","227":" }","228":"","229":" disconnect_database"}},{"lineNumber":190,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"fork","code":{"187":" Rails.application.reloader.reload!","188":" end","189":"","190":" pid + = fork {","191":" # Make sure to close other clients otherwise their + graceful termination","192":" # will be impossible due to reference + from this fork.","193":" @clients.each_key { |c| c.close if c != client + }"}},{"lineNumber":190,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"serve","code":{"187":" Rails.application.reloader.reload!","188":" end","189":"","190":" pid + = fork {","191":" # Make sure to close other clients otherwise their + graceful termination","192":" # will be impossible due to reference + from this fork.","193":" @clients.each_key { |c| c.close if c != client + }"}},{"lineNumber":153,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"block + in run","code":{"150":" if terminating? || watcher_stale? || preload_failed?","151":" exit","152":" else","153":" serve + manager.recv_io(UNIXSocket)","154":" end","155":" end","156":" end"}},{"lineNumber":147,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"loop","code":{"144":" state + :running","145":" manager.puts","146":"","147":" loop do","148":" IO.select + [manager, @interrupt.first]","149":"","150":" if terminating? || watcher_stale? + || preload_failed?"}},{"lineNumber":147,"file":"gems/spring-4.2.1/lib/spring/application.rb","method":"run","code":{"144":" state + :running","145":" manager.puts","146":"","147":" loop do","148":" IO.select + [manager, @interrupt.first]","149":"","150":" if terminating? || watcher_stale? + || preload_failed?"}},{"lineNumber":25,"file":"gems/spring-4.2.1/lib/spring/application/boot.rb","method":"","code":{"19":" app.spawn_env,","20":" ].compact","21":" \"spring + app | #{attributes.join(\" | \")}\"","22":"end","23":"","24":"app.eager_preload + if ENV.delete(\"SPRING_PRELOAD\") == \"1\"","25":"app.run"}},{"lineNumber":1,"file":"-e","method":"
","code":null}]}],"featureFlags":[],"metaData":{"RSpec":{"file":"/home/maikel/code/openfoodnetwork/spec/services/alert_spec.rb"},"env":{}},"severity":"warning","severityReason":{"type":"handledException"},"unhandled":false,"user":{}}]}' + headers: + Bugsnag-Api-Key: + - "" + Bugsnag-Payload-Version: + - '4.0' + Bugsnag-Sent-At: + - '2024-11-20T04:51:11.998Z' + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Access-Control-Allow-Origin: + - "*" + Bugsnag-Event-Id: + - 673d6ac0010cfda42d8e0000 + Date: + - Wed, 20 Nov 2024 04:51:12 GMT + Content-Length: + - '2' + Content-Type: + - text/plain; charset=utf-8 + Via: + - 1.1 google + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + body: + encoding: UTF-8 + string: OK + recorded_at: Wed, 20 Nov 2024 04:51:12 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/services/alert_spec.rb b/spec/services/alert_spec.rb index 4df7c66ea9..c38b0034ff 100644 --- a/spec/services/alert_spec.rb +++ b/spec/services/alert_spec.rb @@ -22,4 +22,27 @@ RSpec.describe Alert do { order: { number: "ABC123" }, env: { referer: "example.com" } } ) end + + it "reaches the Bugsnag service for real", :vcr do + # You need to have a valid Bugsnag API key to record this test. + # And after recording, you need to check the Bugsnag account for the right + # data. + + original_config = nil + Bugsnag.configure do |config| + original_config = config.dup + config.notify_release_stages = ["test"] + config.delivery_method = :synchronous + end + + Alert.raise( + "Testing Bugsnag from RSpec", + { RSpec: { file: __FILE__ }, env: { BUGSNAG: ENV.fetch("BUGSNAG", nil) } } + ) + + Bugsnag.configure do |config| + config.notify_release_stages = original_config.notify_release_stages + config.delivery_method = original_config.delivery_method + end + end end diff --git a/spec/support/vcr_setup.rb b/spec/support/vcr_setup.rb index d9958751a6..ad44f367fa 100644 --- a/spec/support/vcr_setup.rb +++ b/spec/support/vcr_setup.rb @@ -11,6 +11,7 @@ VCR.configure do |config| # Filter sensitive environment variables %w[ + BUGSNAG_API_KEY STRIPE_INSTANCE_SECRET_KEY STRIPE_CUSTOMER STRIPE_ACCOUNT From 0d8df5d2a8fcdaf6303eb74d4c36b8216055eaee Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 Nov 2024 16:23:45 +1100 Subject: [PATCH 05/11] Replace Bugsnag calls with Alert.raise --- .../admin/connected_apps_controller.rb | 2 +- app/controllers/api/v0/base_controller.rb | 2 +- app/controllers/api/v1/base_controller.rb | 2 +- app/controllers/concerns/order_completion.rb | 4 +- app/controllers/concerns/order_stock_check.rb | 2 +- .../spree/admin/payments_controller.rb | 2 +- .../spree/admin/products_controller.rb | 2 +- app/jobs/backorder_job.rb | 2 +- app/jobs/report_job.rb | 2 +- app/jobs/stock_sync_job.rb | 4 +- app/jobs/subscription_confirm_job.rb | 4 +- app/models/calculator/default_tax.rb | 2 +- app/models/enterprise.rb | 2 +- app/models/spree/image.rb | 2 +- app/models/spree/order.rb | 2 +- app/models/spree/tax_rate.rb | 2 +- app/models/spree/variant.rb | 2 +- app/models/stripe_account.rb | 2 +- app/models/variant_override.rb | 8 ++-- app/services/place_proxy_order.rb | 4 +- app/services/sets/product_set.rb | 2 +- lib/open_food_network/error_logger.rb | 2 +- spec/services/alert_spec.rb | 41 +++++++++++++------ 23 files changed, 57 insertions(+), 42 deletions(-) diff --git a/app/controllers/admin/connected_apps_controller.rb b/app/controllers/admin/connected_apps_controller.rb index a5308d4984..a89743079e 100644 --- a/app/controllers/admin/connected_apps_controller.rb +++ b/app/controllers/admin/connected_apps_controller.rb @@ -77,7 +77,7 @@ module Admin def log_and_notify_exception(exception) Rails.logger.error exception.inspect - Bugsnag.notify(exception) + Alert.raise(exception) end def vine_params_empty? diff --git a/app/controllers/api/v0/base_controller.rb b/app/controllers/api/v0/base_controller.rb index 40bc17a06a..f3e44a29b2 100644 --- a/app/controllers/api/v0/base_controller.rb +++ b/app/controllers/api/v0/base_controller.rb @@ -66,7 +66,7 @@ module Api end def error_during_processing(exception) - Bugsnag.notify(exception) + Alert.raise(exception) render(json: { exception: exception.message }, status: :unprocessable_entity) && return diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index 2cf59888fb..f1e5e992fb 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -52,7 +52,7 @@ module Api end def error_during_processing(exception) - Bugsnag.notify(exception) + Alert.raise(exception) if Rails.env.development? || Rails.env.test? render status: :unprocessable_entity, diff --git a/app/controllers/concerns/order_completion.rb b/app/controllers/concerns/order_completion.rb index d98faf149b..d3bd24a793 100644 --- a/app/controllers/concerns/order_completion.rb +++ b/app/controllers/concerns/order_completion.rb @@ -50,7 +50,7 @@ module OrderCompletion end def order_invalid! - Bugsnag.notify("Notice: invalid order loaded during checkout") do |payload| + Alert.raise("Notice: invalid order loaded during checkout") do |payload| payload.add_metadata :order, :order, @order end @@ -92,7 +92,7 @@ module OrderCompletion end def notify_failure(error = RuntimeError.new(order_processing_error)) - Bugsnag.notify(error) do |payload| + Alert.raise(error) do |payload| payload.add_metadata :order, @order end flash[:error] = order_processing_error if flash.blank? diff --git a/app/controllers/concerns/order_stock_check.rb b/app/controllers/concerns/order_stock_check.rb index 7b94876533..4d2d79fffa 100644 --- a/app/controllers/concerns/order_stock_check.rb +++ b/app/controllers/concerns/order_stock_check.rb @@ -20,7 +20,7 @@ module OrderStockCheck def check_order_cycle_expiry return unless current_order_cycle&.closed? - Bugsnag.notify("Notice: order cycle closed during checkout completion") do |payload| + Alert.raise("Notice: order cycle closed during checkout completion") do |payload| payload.add_metadata :order, :order, current_order end current_order.empty! diff --git a/app/controllers/spree/admin/payments_controller.rb b/app/controllers/spree/admin/payments_controller.rb index 0afaa5227c..f2709a3f85 100644 --- a/app/controllers/spree/admin/payments_controller.rb +++ b/app/controllers/spree/admin/payments_controller.rb @@ -60,7 +60,7 @@ module Spree end rescue StandardError => e logger.error e.message - Bugsnag.notify(e) + Alert.raise(e) flash[:error] = e.message ensure redirect_to request.referer diff --git a/app/controllers/spree/admin/products_controller.rb b/app/controllers/spree/admin/products_controller.rb index 788d019dce..e00fce21c0 100644 --- a/app/controllers/spree/admin/products_controller.rb +++ b/app/controllers/spree/admin/products_controller.rb @@ -213,7 +213,7 @@ module Spree end def notify_bugsnag(error, product, variant) - Bugsnag.notify(error) do |report| + Alert.raise(error) do |report| report.add_metadata(:product, { product: product.attributes, variant: variant.attributes }) report.add_metadata(:product, :product_error, product.errors.first) unless product.valid? diff --git a/app/jobs/backorder_job.rb b/app/jobs/backorder_job.rb index 73e3f51eba..565e637305 100644 --- a/app/jobs/backorder_job.rb +++ b/app/jobs/backorder_job.rb @@ -19,7 +19,7 @@ class BackorderJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the checkout. So let's report them # separately: - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata(:order, :order, order) end end diff --git a/app/jobs/report_job.rb b/app/jobs/report_job.rb index 0367ddd259..e99eccae00 100644 --- a/app/jobs/report_job.rb +++ b/app/jobs/report_job.rb @@ -22,7 +22,7 @@ class ReportJob < ApplicationJob broadcast_result(channel, format, blob) if channel rescue StandardError => e - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata :report, { report_class:, user:, params:, format: } diff --git a/app/jobs/stock_sync_job.rb b/app/jobs/stock_sync_job.rb index 009bdb5b5e..a2a2fe6dd3 100644 --- a/app/jobs/stock_sync_job.rb +++ b/app/jobs/stock_sync_job.rb @@ -16,7 +16,7 @@ class StockSyncJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the shopping. So let's report them # separately: - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata(:order, :order, order) end end @@ -29,7 +29,7 @@ class StockSyncJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the shopping. So let's report them # separately: - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata(:order, :order, order) end end diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index 2182671049..da5d388db9 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -55,7 +55,7 @@ class SubscriptionConfirmJob < ApplicationJob if order.errors.any? send_failed_payment_email(order) else - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata :order, :order, order end send_failed_payment_email(order, e.message) @@ -108,7 +108,7 @@ class SubscriptionConfirmJob < ApplicationJob record_and_log_error(:failed_payment, order, error_message) SubscriptionMailer.failed_payment_email(order).deliver_now rescue StandardError => e - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata :subscription_data, { order:, error_message: } end end diff --git a/app/models/calculator/default_tax.rb b/app/models/calculator/default_tax.rb index b6c8b1d3c2..c1cb7a4bcd 100644 --- a/app/models/calculator/default_tax.rb +++ b/app/models/calculator/default_tax.rb @@ -28,7 +28,7 @@ module Calculator # In theory it should never be called any more after this has been deployed. # If the message below doesn't show up in Bugsnag, we can safely delete this method and all # the related methods below it. - Bugsnag.notify("Calculator::DefaultTax was called with legacy tax calculations") + Alert.raise("Calculator::DefaultTax was called with legacy tax calculations") calculator = OpenFoodNetwork::EnterpriseFeeCalculator.new(order.distributor, order.order_cycle) diff --git a/app/models/enterprise.rb b/app/models/enterprise.rb index dfee85fef6..15d7fb33f2 100644 --- a/app/models/enterprise.rb +++ b/app/models/enterprise.rb @@ -481,7 +481,7 @@ class Enterprise < ApplicationRecord image_variant_url_for(image.variant(name)) rescue StandardError => e - Bugsnag.notify "Enterprise ##{id} #{image.try(:name)} error: #{e.message}" + Alert.raise "Enterprise ##{id} #{image.try(:name)} error: #{e.message}" Rails.logger.error(e.message) nil diff --git a/app/models/spree/image.rb b/app/models/spree/image.rb index 45859a6b45..350b1c5b4c 100644 --- a/app/models/spree/image.rb +++ b/app/models/spree/image.rb @@ -34,7 +34,7 @@ module Spree image_variant_url_for(variant(size)) rescue StandardError => e - Bugsnag.notify "Product ##{viewable_id} Image ##{id} error: #{e.message}" + Alert.raise "Product ##{viewable_id} Image ##{id} error: #{e.message}" Rails.logger.error(e.message) self.class.default_image_url(size) diff --git a/app/models/spree/order.rb b/app/models/spree/order.rb index 1e167f8e92..d5c3a00579 100644 --- a/app/models/spree/order.rb +++ b/app/models/spree/order.rb @@ -531,7 +531,7 @@ module Spree # because an outdated shipping fee is not as bad as a lost payment. # And the shipping fee is already up-to-date when this error occurs. # https://github.com/openfoodfoundation/openfoodnetwork/issues/3924 - Bugsnag.notify(e) do |report| + Alert.raise(e) do |report| report.add_metadata(:order, attributes) report.add_metadata(:shipment, shipment.attributes) report.add_metadata(:shipment_in_db, Spree::Shipment.find_by(id: shipment.id).attributes) diff --git a/app/models/spree/tax_rate.rb b/app/models/spree/tax_rate.rb index 588fe91985..239a6d9072 100644 --- a/app/models/spree/tax_rate.rb +++ b/app/models/spree/tax_rate.rb @@ -106,7 +106,7 @@ module Spree calculator.compute(item) else # Tax refund should not be possible with the way our production server are configured - Bugsnag.notify( + Alert.raise( "Notice: Tax refund should not be possible, please check the default zone and " \ "the tax rate zone configuration" ) do |payload| diff --git a/app/models/spree/variant.rb b/app/models/spree/variant.rb index fefd41ac0d..26f22cdddf 100644 --- a/app/models/spree/variant.rb +++ b/app/models/spree/variant.rb @@ -293,7 +293,7 @@ module Spree end def ensure_unit_value - Bugsnag.notify("Trying to set unit_value to NaN") if unit_value&.nan? + Alert.raise("Trying to set unit_value to NaN") if unit_value&.nan? return unless (variant_unit == "items" && unit_value.nil?) || unit_value&.nan? self.unit_value = 1.0 diff --git a/app/models/stripe_account.rb b/app/models/stripe_account.rb index c0b507f316..260a29270e 100644 --- a/app/models/stripe_account.rb +++ b/app/models/stripe_account.rb @@ -15,7 +15,7 @@ class StripeAccount < ApplicationRecord destroy && Stripe::OAuth.deauthorize(stripe_user_id:) rescue Stripe::OAuth::OAuthError => e - Bugsnag.notify( + Alert.raise( e, stripe_account: stripe_user_id, enterprise_id: diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 79a28508f2..34bd6ac984 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -48,8 +48,8 @@ class VariantOverride < ApplicationRecord def move_stock!(quantity) unless stock_overridden? - Bugsnag.notify RuntimeError.new "Attempting to move stock of a VariantOverride " \ - "without a count_on_hand specified." + Alert.raise RuntimeError.new "Attempting to move stock of a VariantOverride " \ + "without a count_on_hand specified." return end @@ -73,8 +73,8 @@ class VariantOverride < ApplicationRecord self.attributes = { on_demand: false, count_on_hand: default_stock } save else - Bugsnag.notify RuntimeError.new "Attempting to reset stock level for a variant " \ - "with no default stock level." + Alert.raise RuntimeError.new "Attempting to reset stock level for a variant " \ + "with no default stock level." end end self diff --git a/app/services/place_proxy_order.rb b/app/services/place_proxy_order.rb index c6f942d3b0..14ad693cd5 100644 --- a/app/services/place_proxy_order.rb +++ b/app/services/place_proxy_order.rb @@ -24,7 +24,7 @@ class PlaceProxyOrder send_placement_email rescue StandardError => e summarizer.record_and_log_error(:processing, order, e.message) - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata :order, :order, order end end @@ -56,7 +56,7 @@ class PlaceProxyOrder true rescue StandardError => e - Bugsnag.notify(e) do |payload| + Alert.raise(e) do |payload| payload.add_metadata(:proxy_order, { subscription:, proxy_order: }) end false diff --git a/app/services/sets/product_set.rb b/app/services/sets/product_set.rb index c6a4521ab7..156f73f875 100644 --- a/app/services/sets/product_set.rb +++ b/app/services/sets/product_set.rb @@ -145,7 +145,7 @@ module Sets end def notify_bugsnag(error, product, variant, variant_attributes) - Bugsnag.notify(error) do |report| + Alert.raise(error) do |report| report.add_metadata( :product_set, { product: product.attributes, variant_attributes:, variant: variant.attributes } ) diff --git a/lib/open_food_network/error_logger.rb b/lib/open_food_network/error_logger.rb index 51ea1e715e..f028fde39b 100644 --- a/lib/open_food_network/error_logger.rb +++ b/lib/open_food_network/error_logger.rb @@ -9,7 +9,7 @@ module OpenFoodNetwork # If Bugsnag is configured, it will notify it. It would be nice to implement # some kind of fallback. def self.notify(error) - Bugsnag.notify(error) + Alert.raise(error) end end end diff --git a/spec/services/alert_spec.rb b/spec/services/alert_spec.rb index c38b0034ff..35db21cbf1 100644 --- a/spec/services/alert_spec.rb +++ b/spec/services/alert_spec.rb @@ -3,6 +3,24 @@ require 'spec_helper' RSpec.describe Alert do + around do |example| + original_config = nil + Bugsnag.configure do |config| + original_config = config.dup + config.api_key ||= "dummy-key" + config.notify_release_stages = ["test"] + config.delivery_method = :synchronous + end + + example.run + + Bugsnag.configure do |config| + config.api_key ||= original_config.api_key + config.notify_release_stages = original_config.notify_release_stages + config.delivery_method = original_config.delivery_method + end + end + it "notifies Bugsnag" do expect(Bugsnag).to receive(:notify).with("hey") @@ -23,26 +41,23 @@ RSpec.describe Alert do ) end + it "is compatible with Bugsnag API" do + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( + :order, { number: "ABC123" } + ) + + Alert.raise("hey") do |payload| + payload.add_metadata(:order, { number: "ABC123" }) + end + end + it "reaches the Bugsnag service for real", :vcr do # You need to have a valid Bugsnag API key to record this test. # And after recording, you need to check the Bugsnag account for the right # data. - - original_config = nil - Bugsnag.configure do |config| - original_config = config.dup - config.notify_release_stages = ["test"] - config.delivery_method = :synchronous - end - Alert.raise( "Testing Bugsnag from RSpec", { RSpec: { file: __FILE__ }, env: { BUGSNAG: ENV.fetch("BUGSNAG", nil) } } ) - - Bugsnag.configure do |config| - config.notify_release_stages = original_config.notify_release_stages - config.delivery_method = original_config.delivery_method - end end end From 61e7c1db070b4ac42ca88f2e413a9ed378c40b5d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 Nov 2024 16:26:48 +1100 Subject: [PATCH 06/11] Replace obsolete ErrorLogger --- app/controllers/user_registrations_controller.rb | 3 +-- lib/open_food_network/error_logger.rb | 15 --------------- .../user_registrations_controller_spec.rb | 2 +- spec/lib/open_food_network/error_logger_spec.rb | 16 ---------------- 4 files changed, 2 insertions(+), 34 deletions(-) delete mode 100644 lib/open_food_network/error_logger.rb delete mode 100644 spec/lib/open_food_network/error_logger_spec.rb diff --git a/app/controllers/user_registrations_controller.rb b/app/controllers/user_registrations_controller.rb index 3593b20b5a..01e819af3d 100644 --- a/app/controllers/user_registrations_controller.rb +++ b/app/controllers/user_registrations_controller.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'open_food_network/error_logger' require "spree/core/controller_helpers/auth" require "spree/core/controller_helpers/common" require "spree/core/controller_helpers/order" @@ -37,7 +36,7 @@ class UserRegistrationsController < Devise::RegistrationsController end end rescue StandardError => e - OpenFoodNetwork::ErrorLogger.notify(e) + Alert.raise(e) render_error(message: I18n.t('unknown_error', scope: I18N_SCOPE)) end diff --git a/lib/open_food_network/error_logger.rb b/lib/open_food_network/error_logger.rb deleted file mode 100644 index f028fde39b..0000000000 --- a/lib/open_food_network/error_logger.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -# Our error logging API currently wraps Bugsnag. -# It makes us more flexible if we wanted to replace Bugsnag or change logging -# behaviour. -module OpenFoodNetwork - module ErrorLogger - # Tries to escalate the error to a developer. - # If Bugsnag is configured, it will notify it. It would be nice to implement - # some kind of fallback. - def self.notify(error) - Alert.raise(error) - end - end -end diff --git a/spec/controllers/user_registrations_controller_spec.rb b/spec/controllers/user_registrations_controller_spec.rb index 23aa1e220a..da2c29cc45 100644 --- a/spec/controllers/user_registrations_controller_spec.rb +++ b/spec/controllers/user_registrations_controller_spec.rb @@ -27,7 +27,7 @@ RSpec.describe UserRegistrationsController, type: :controller do it "returns error when emailing fails" do allow(Spree::UserMailer).to receive(:confirmation_instructions).and_raise("Some error") - expect(OpenFoodNetwork::ErrorLogger).to receive(:notify) + expect(Alert).to receive(:raise) post :create, xhr: true, params: { spree_user: user_params, use_route: :spree } diff --git a/spec/lib/open_food_network/error_logger_spec.rb b/spec/lib/open_food_network/error_logger_spec.rb deleted file mode 100644 index a2335ee1d9..0000000000 --- a/spec/lib/open_food_network/error_logger_spec.rb +++ /dev/null @@ -1,16 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'open_food_network/error_logger' - -module OpenFoodNetwork - RSpec.describe ErrorLogger do - let(:error) { StandardError.new("Test") } - - it "notifies Bugsnag" do - expect(Bugsnag).to receive(:notify).with(error) - - ErrorLogger.notify(error) - end - end -end From 6a8cc410d2ff24388453b6b624548ddbfadaec6d Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Wed, 20 Nov 2024 17:13:10 +1100 Subject: [PATCH 07/11] Replace broken order data Bugsnag expects a string as value, not an ActiveRecord object. The result was just "filtered" data, not showing any of the order's attributes. Since wanting to share the order data seems a common pattern, I added a convenience method for it. --- app/controllers/concerns/order_completion.rb | 8 ++------ app/controllers/concerns/order_stock_check.rb | 4 +--- app/jobs/backorder_job.rb | 4 +--- app/jobs/stock_sync_job.rb | 8 ++------ app/services/alert.rb | 7 +++++++ app/services/place_proxy_order.rb | 4 +--- spec/services/alert_spec.rb | 17 +++++++++++++++++ 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/app/controllers/concerns/order_completion.rb b/app/controllers/concerns/order_completion.rb index d3bd24a793..ca67d4a84d 100644 --- a/app/controllers/concerns/order_completion.rb +++ b/app/controllers/concerns/order_completion.rb @@ -50,9 +50,7 @@ module OrderCompletion end def order_invalid! - Alert.raise("Notice: invalid order loaded during checkout") do |payload| - payload.add_metadata :order, :order, @order - end + Alert.raise_with_record("Notice: invalid order loaded during checkout", @order) flash[:error] = t('checkout.order_not_loaded') redirect_to main_app.shop_path @@ -92,9 +90,7 @@ module OrderCompletion end def notify_failure(error = RuntimeError.new(order_processing_error)) - Alert.raise(error) do |payload| - payload.add_metadata :order, @order - end + Alert.raise_with_record(error, @order) flash[:error] = order_processing_error if flash.blank? end diff --git a/app/controllers/concerns/order_stock_check.rb b/app/controllers/concerns/order_stock_check.rb index 4d2d79fffa..911bedb124 100644 --- a/app/controllers/concerns/order_stock_check.rb +++ b/app/controllers/concerns/order_stock_check.rb @@ -20,9 +20,7 @@ module OrderStockCheck def check_order_cycle_expiry return unless current_order_cycle&.closed? - Alert.raise("Notice: order cycle closed during checkout completion") do |payload| - payload.add_metadata :order, :order, current_order - end + Alert.raise_with_record("Notice: order cycle closed during checkout completion", current_order) current_order.empty! current_order.set_order_cycle! nil diff --git a/app/jobs/backorder_job.rb b/app/jobs/backorder_job.rb index 565e637305..a6276ee95f 100644 --- a/app/jobs/backorder_job.rb +++ b/app/jobs/backorder_job.rb @@ -19,9 +19,7 @@ class BackorderJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the checkout. So let's report them # separately: - Alert.raise(e) do |payload| - payload.add_metadata(:order, :order, order) - end + Alert.raise_with_record(e, order) end def perform(order) diff --git a/app/jobs/stock_sync_job.rb b/app/jobs/stock_sync_job.rb index a2a2fe6dd3..781afd59a9 100644 --- a/app/jobs/stock_sync_job.rb +++ b/app/jobs/stock_sync_job.rb @@ -16,9 +16,7 @@ class StockSyncJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the shopping. So let's report them # separately: - Alert.raise(e) do |payload| - payload.add_metadata(:order, :order, order) - end + Alert.raise_with_record(e, order) end def self.sync_linked_catalogs_now(order) @@ -29,9 +27,7 @@ class StockSyncJob < ApplicationJob rescue StandardError => e # Errors here shouldn't affect the shopping. So let's report them # separately: - Alert.raise(e) do |payload| - payload.add_metadata(:order, :order, order) - end + Alert.raise_with_record(e, order) end def self.catalog_ids(order) diff --git a/app/services/alert.rb b/app/services/alert.rb index d43eae9b27..d030554298 100644 --- a/app/services/alert.rb +++ b/app/services/alert.rb @@ -28,4 +28,11 @@ class Alert block.call(payload) end end + + def self.raise_with_record(error, record, &) + metadata = { + record.class.name => record&.attributes || { record_was_nil: true } + } + self.raise(error, metadata, &) + end end diff --git a/app/services/place_proxy_order.rb b/app/services/place_proxy_order.rb index 14ad693cd5..189d4dc0ec 100644 --- a/app/services/place_proxy_order.rb +++ b/app/services/place_proxy_order.rb @@ -24,9 +24,7 @@ class PlaceProxyOrder send_placement_email rescue StandardError => e summarizer.record_and_log_error(:processing, order, e.message) - Alert.raise(e) do |payload| - payload.add_metadata :order, :order, order - end + Alert.raise_with_record(e, order) end private diff --git a/spec/services/alert_spec.rb b/spec/services/alert_spec.rb index 35db21cbf1..3c0eb123d4 100644 --- a/spec/services/alert_spec.rb +++ b/spec/services/alert_spec.rb @@ -51,6 +51,23 @@ RSpec.describe Alert do end end + it "sends ActiveRecord objects" do + order = Spree::Order.new(number: "ABC123") + + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( + "Spree::Order", hash_including("number" => "ABC123") + ) + + Alert.raise_with_record("Wrong order", order) + end + + it "notifies Bugsnag when ActiveRecord object is missing" do + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( + "NilClass", { record_was_nil: true } + ) + Alert.raise_with_record("Wrong order", nil) + end + it "reaches the Bugsnag service for real", :vcr do # You need to have a valid Bugsnag API key to record this test. # And after recording, you need to check the Bugsnag account for the right From af33fc357e51940fc9219a1ab1b5e1ceb1f72026 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 Nov 2024 15:25:53 +1100 Subject: [PATCH 08/11] Use shorter Alert syntax I still think that some of these objects won't be visible in Bugsnag but I don't want to test any more on cases that were broken before and may not be relevant now. --- app/jobs/report_job.rb | 6 +----- app/jobs/subscription_confirm_job.rb | 4 +--- app/services/place_proxy_order.rb | 4 +--- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/jobs/report_job.rb b/app/jobs/report_job.rb index e99eccae00..58c6142386 100644 --- a/app/jobs/report_job.rb +++ b/app/jobs/report_job.rb @@ -22,11 +22,7 @@ class ReportJob < ApplicationJob broadcast_result(channel, format, blob) if channel rescue StandardError => e - Alert.raise(e) do |payload| - payload.add_metadata :report, { - report_class:, user:, params:, format: - } - end + Alert.raise(e, { report: { report_class:, user:, params:, format: } }) broadcast_error(channel) end diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index da5d388db9..a0a4f918d5 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -108,8 +108,6 @@ class SubscriptionConfirmJob < ApplicationJob record_and_log_error(:failed_payment, order, error_message) SubscriptionMailer.failed_payment_email(order).deliver_now rescue StandardError => e - Alert.raise(e) do |payload| - payload.add_metadata :subscription_data, { order:, error_message: } - end + Alert.raise(e, { subscription_data: { order:, error_message: } }) end end diff --git a/app/services/place_proxy_order.rb b/app/services/place_proxy_order.rb index 189d4dc0ec..ed3c24dc14 100644 --- a/app/services/place_proxy_order.rb +++ b/app/services/place_proxy_order.rb @@ -54,9 +54,7 @@ class PlaceProxyOrder true rescue StandardError => e - Alert.raise(e) do |payload| - payload.add_metadata(:proxy_order, { subscription:, proxy_order: }) - end + Alert.raise(e, { proxy_order: { subscription:, proxy_order: } }) false end From 9f859f420db9eb19f8a80c28ef2331ad7c592438 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 Nov 2024 15:28:08 +1100 Subject: [PATCH 09/11] Remove unnecessary error creation --- app/models/variant_override.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/variant_override.rb b/app/models/variant_override.rb index 34bd6ac984..16a668700e 100644 --- a/app/models/variant_override.rb +++ b/app/models/variant_override.rb @@ -48,8 +48,8 @@ class VariantOverride < ApplicationRecord def move_stock!(quantity) unless stock_overridden? - Alert.raise RuntimeError.new "Attempting to move stock of a VariantOverride " \ - "without a count_on_hand specified." + Alert.raise "Attempting to move stock of a VariantOverride " \ + "without a count_on_hand specified." return end @@ -73,8 +73,8 @@ class VariantOverride < ApplicationRecord self.attributes = { on_demand: false, count_on_hand: default_stock } save else - Alert.raise RuntimeError.new "Attempting to reset stock level for a variant " \ - "with no default stock level." + Alert.raise "Attempting to reset stock level for a variant " \ + "with no default stock level." end end self From 14e7c571028efa1e79e8b41c69f9453511173aae Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 21 Nov 2024 16:17:27 +1100 Subject: [PATCH 10/11] Deactivate some specs on CI Somehow Bugsnag doesn't report in CI environment and I have no idea how to circumvent that. And I don't want to spend more time on this. --- spec/services/alert_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/services/alert_spec.rb b/spec/services/alert_spec.rb index 3c0eb123d4..2e8723fd8f 100644 --- a/spec/services/alert_spec.rb +++ b/spec/services/alert_spec.rb @@ -28,6 +28,8 @@ RSpec.describe Alert do end it "adds context" do + pending "Bugsnag calls in CI" if ENV.fetch("CI", false) + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( :order, { number: "ABC123" } ) @@ -42,6 +44,8 @@ RSpec.describe Alert do end it "is compatible with Bugsnag API" do + pending "Bugsnag calls in CI" if ENV.fetch("CI", false) + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( :order, { number: "ABC123" } ) @@ -52,6 +56,8 @@ RSpec.describe Alert do end it "sends ActiveRecord objects" do + pending "Bugsnag calls in CI" if ENV.fetch("CI", false) + order = Spree::Order.new(number: "ABC123") expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( @@ -62,6 +68,8 @@ RSpec.describe Alert do end it "notifies Bugsnag when ActiveRecord object is missing" do + pending "Bugsnag calls in CI" if ENV.fetch("CI", false) + expect_any_instance_of(Bugsnag::Report).to receive(:add_metadata).with( "NilClass", { record_was_nil: true } ) From c41c15b895de264ddf94b3a8b9958fdaca1e3d46 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Tue, 3 Dec 2024 13:02:08 +1100 Subject: [PATCH 11/11] Fix missed Alert call with order object --- app/jobs/subscription_confirm_job.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/jobs/subscription_confirm_job.rb b/app/jobs/subscription_confirm_job.rb index a0a4f918d5..16f1fd0e6c 100644 --- a/app/jobs/subscription_confirm_job.rb +++ b/app/jobs/subscription_confirm_job.rb @@ -55,9 +55,7 @@ class SubscriptionConfirmJob < ApplicationJob if order.errors.any? send_failed_payment_email(order) else - Alert.raise(e) do |payload| - payload.add_metadata :order, :order, order - end + Alert.raise_with_record(e, order) send_failed_payment_email(order, e.message) end end