diff --git a/.github/workflows/maze-runner.yml b/.github/workflows/maze-runner.yml index e2e27582..0c31db33 100644 --- a/.github/workflows/maze-runner.yml +++ b/.github/workflows/maze-runner.yml @@ -127,7 +127,7 @@ jobs: strategy: fail-fast: false matrix: - ruby-version: ['2.7', '3.3'] + ruby-version: ['2.7', '3.3.0'] # TODO: change back to '3.3' after https://github.com/ruby/ruby/pull/10619 is released rails-version: ['6', '7', '_integrations'] include: - ruby-version: '2.5' @@ -135,7 +135,7 @@ jobs: exclude: - ruby-version: '2.7' rails-version: '6' - - ruby-version: '3.3' + - ruby-version: '3.3.0' # TODO: change back to '3.3' after https://github.com/ruby/ruby/pull/10619 is released rails-version: '_integrations' uses: ./.github/workflows/run-maze-runner.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 111dc799..f8b5bec1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,16 @@ Changelog ========= +## v6.27.0 (23 May 2024) + +### Enhancements + +* Include the Warden scope in user metadata + | [#821](https://github.com/bugsnag/bugsnag-ruby/pull/821) + | [javierjulio](https://github.com/javierjulio) +* Add a block variant of `add_on_error` + | [#824](https://github.com/bugsnag/bugsnag-ruby/pull/824) + ## v6.26.4 (25 March 2024) ### Fixes diff --git a/VERSION b/VERSION index 30bfaeb1..9cd1a39f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.26.4 +6.27.0 diff --git a/lib/bugsnag.rb b/lib/bugsnag.rb index 5d002a90..950f1726 100644 --- a/lib/bugsnag.rb +++ b/lib/bugsnag.rb @@ -309,6 +309,20 @@ def leave_breadcrumb(name, meta_data={}, type=Bugsnag::Breadcrumbs::MANUAL_BREAD configuration.breadcrumbs << breadcrumb unless breadcrumb.ignore? end + ## + # Add the given block to the list of on_error callbacks + # + # The on_error callbacks will be called when an error is captured or reported + # and are passed a {Bugsnag::Report} object + # + # Returning false from an on_error callback will cause the error to be ignored + # and will prevent any remaining callbacks from being called + # + # @return [void] + def on_error(&block) + configuration.on_error(&block) + end + ## # Add the given callback to the list of on_error callbacks # diff --git a/lib/bugsnag/configuration.rb b/lib/bugsnag/configuration.rb index 36dda53d..bcf64c81 100644 --- a/lib/bugsnag/configuration.rb +++ b/lib/bugsnag/configuration.rb @@ -569,6 +569,20 @@ def disable_sessions @enable_sessions = false end + ## + # Add the given block to the list of on_error callbacks + # + # The on_error callbacks will be called when an error is captured or reported + # and are passed a {Bugsnag::Report} object + # + # Returning false from an on_error callback will cause the error to be ignored + # and will prevent any remaining callbacks from being called + # + # @return [void] + def on_error(&block) + middleware.use(block) + end + ## # Add the given callback to the list of on_error callbacks # diff --git a/lib/bugsnag/middleware/warden_user.rb b/lib/bugsnag/middleware/warden_user.rb index 59c6af0d..bb9b8432 100644 --- a/lib/bugsnag/middleware/warden_user.rb +++ b/lib/bugsnag/middleware/warden_user.rb @@ -23,7 +23,10 @@ def call(report) # Extract useful user information user = {} user_object = env["warden"].user({:scope => best_scope, :run_callbacks => false}) rescue nil + if user_object + user[:warden_scope] = best_scope + # Build the user info for this scope COMMON_USER_FIELDS.each do |field| user[field] = user_object.send(field) if user_object.respond_to?(field) diff --git a/spec/integrations/warden_user_spec.rb b/spec/integrations/warden_user_spec.rb index 03b0b5fd..1a228d9a 100644 --- a/spec/integrations/warden_user_spec.rb +++ b/spec/integrations/warden_user_spec.rb @@ -6,33 +6,100 @@ user = double allow(user).to receive_messages( - :email => "TEST_EMAIL", - :name => "TEST_NAME", - :created_at => "TEST_NOW" + email: "TEST_EMAIL", + name: "TEST_NAME", + created_at: "TEST_NOW", ) warden = double allow(warden).to receive(:user).with({ - :scope => "user", - :run_callbacks => false + scope: "user", + run_callbacks: false, }).and_return(user) report = double("Bugsnag::Report") + expect(report).to receive(:request_data).exactly(3).times.and_return({ + rack_env: { + "warden" => warden, + "rack.session" => { + "warden.user.user.key" => "TEST_USER", + } + } + }) + + expect(report).to receive(:user=).with({ + email: "TEST_EMAIL", + name: "TEST_NAME", + created_at: "TEST_NOW", + warden_scope: "user", + }) + + expect(callback).to receive(:call).with(report) + + middleware = Bugsnag::Middleware::WardenUser.new(callback) + middleware.call(report) + end + + it "sets the scope to the 'best scope'" do + callback = double + + user = double + allow(user).to receive_messages( + email: "TEST_EMAIL", + name: "TEST_NAME", + created_at: "TEST_NOW", + ) + + warden = double + allow(warden).to receive(:user).with({ + scope: "admin", + run_callbacks: false, + }).and_return(user) + + report = double("Bugsnag::Report") expect(report).to receive(:request_data).exactly(3).times.and_return({ :rack_env => { "warden" => warden, "rack.session" => { - "warden.user.user.key" => "TEST_USER" + "warden.user.admin.key" => "TEST_USER" } } }) expect(report).to receive(:user=).with({ - :email => "TEST_EMAIL", - :name => "TEST_NAME", - :created_at => "TEST_NOW" + email: "TEST_EMAIL", + name: "TEST_NAME", + created_at: "TEST_NOW", + warden_scope: "admin", + }) + + expect(callback).to receive(:call).with(report) + + middleware = Bugsnag::Middleware::WardenUser.new(callback) + middleware.call(report) + end + + it "doesn't set the user if the user object is empty" do + callback = double + + warden = double + allow(warden).to receive(:user).with({ + scope: "user", + run_callbacks: false, + }).and_return(nil) + + report = double("Bugsnag::Report") + expect(report).to receive(:request_data).exactly(3).times.and_return({ + :rack_env => { + "warden" => warden, + "rack.session" => { + "warden.user.user.key" => "TEST_USER" + } + } }) + expect(report).not_to receive(:user=) + expect(callback).to receive(:call).with(report) middleware = Bugsnag::Middleware::WardenUser.new(callback) diff --git a/spec/on_error_spec.rb b/spec/on_error_spec.rb index 15bcdb5a..798befd1 100644 --- a/spec/on_error_spec.rb +++ b/spec/on_error_spec.rb @@ -18,6 +18,20 @@ end) end + it "accepts a block" do + Bugsnag.on_error {|report| report.add_tab(:important, { hello: "world" }) } + Bugsnag.on_error {|report| report.add_tab(:significant, { hey: "earth" }) } + + Bugsnag.notify(RuntimeError.new("Oh no!")) + + expect(Bugsnag).to(have_sent_notification do |payload, _headers| + event = get_event_from_payload(payload) + + expect(event["metaData"]["important"]).to eq({ "hello" => "world" }) + expect(event["metaData"]["significant"]).to eq({ "hey" => "earth" }) + end) + end + it "can add callbacks in a configure block" do callback1 = proc {|report| report.add_tab(:important, { hello: "world" }) } callback2 = proc {|report| report.add_tab(:significant, { hey: "earth" }) } @@ -25,6 +39,9 @@ Bugsnag.configure do |config| config.add_on_error(callback1) config.add_on_error(callback2) + config.on_error do |report| + report.add_tab(:critical, { hi: "planet" }) + end end Bugsnag.notify(RuntimeError.new("Oh no!")) @@ -34,6 +51,7 @@ expect(event["metaData"]["important"]).to eq({ "hello" => "world" }) expect(event["metaData"]["significant"]).to eq({ "hey" => "earth" }) + expect(event["metaData"]["critical"]).to eq({ "hi" => "planet" }) end) end @@ -56,6 +74,27 @@ end) end + it "can remove an already registered block" do + callback1 = proc {|report| report.add_tab(:important, { hello: "world" }) } + callback2 = proc {|report| report.add_tab(:significant, { hey: "earth" }) } + + Bugsnag.add_on_error(callback1) + + # pass callback2 as a block so that it can be removed + Bugsnag.on_error(&callback2) + + Bugsnag.remove_on_error(callback2) + + Bugsnag.notify(RuntimeError.new("Oh no!")) + + expect(Bugsnag).to(have_sent_notification do |payload, _headers| + event = get_event_from_payload(payload) + + expect(event["metaData"]["important"]).to eq({ "hello" => "world" }) + expect(event["metaData"]["significant"]).to be_nil + end) + end + it "can remove all registered callbacks" do callback1 = proc {|report| report.add_tab(:important, { hello: "world" }) } callback2 = proc {|report| report.add_tab(:significant, { hey: "earth" }) }