Skip to content

Commit

Permalink
Add support for middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
kyrylo committed Jun 10, 2024
1 parent a2edeb4 commit 369aadc
Show file tree
Hide file tree
Showing 11 changed files with 178 additions and 4 deletions.
2 changes: 2 additions & 0 deletions lib/telebugs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
require_relative "telebugs/backtrace"
require_relative "telebugs/file_cache"
require_relative "telebugs/code_hunk"
require_relative "telebugs/middleware"
require_relative "telebugs/middleware_stack"

module Telebugs
# The general error that this library uses when it wants to raise.
Expand Down
5 changes: 4 additions & 1 deletion lib/telebugs/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class Config
ERROR_API_URL = "https://api.telebugs.com/2024-03-28/errors"

attr_accessor :api_key,
:root_directory
:root_directory,
:middleware

attr_reader :api_url

Expand All @@ -34,6 +35,8 @@ def reset
(defined?(Bundler) && Bundler.root) ||
Dir.pwd
)

@middleware = MiddlewareStack.new
end
end
end
17 changes: 17 additions & 0 deletions lib/telebugs/middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Telebugs
# Represents a middleware that can be used to filter out errors.
# You must inherit from this class and implement the #call method.
class Middleware
DEFAULT_WEIGHT = 0

def weight
DEFAULT_WEIGHT
end

def call(_report)
raise NotImplementedError, "You must implement the #call method"
end
end
end
30 changes: 30 additions & 0 deletions lib/telebugs/middleware_stack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

module Telebugs
# MiddlewareStack represents an ordered array of middleware.
#
# A middleware is an object that responds to <b>#call</b> (typically a Proc or a
# class that implements the call method). The <b>#call</b> method must accept
# exactly one argument: the report object.
#
# When you add a new middleware to the stack, it gets inserted according to its
# <b>weight</b>. Smaller weight means the middleware will be somewhere in the
# beginning of the array. Larger - in the end.
class MiddlewareStack
attr_reader :middlewares

def initialize
@middlewares = []
end

def use(new_middleware)
@middlewares = (@middlewares << new_middleware).sort_by(&:weight).reverse
end

def call(report)
@middlewares.each do |middleware|
middleware.call(report)
end
end
end
end
8 changes: 7 additions & 1 deletion lib/telebugs/notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ def instance

def initialize
@sender = Sender.new
@middleware = Config.instance.middleware
end

def notify(error)
Telebugs::Promise.new(error) do
@sender.send(error)
report = Report.new(error)

@middleware.call(report)
next if report.ignored

@sender.send(report)
end
end
end
Expand Down
2 changes: 2 additions & 0 deletions lib/telebugs/report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ class Report
MAX_REPORT_SIZE = 64000

attr_reader :data
attr_accessor :ignored

def initialize(error)
@ignored = false
@data = {
errors: errors_as_json(error)
}
Expand Down
8 changes: 7 additions & 1 deletion lib/telebugs/sender.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ def send(data)
return JSON.parse(resp.body)
end

raise HTTPError, "#{resp.code_type} (#{resp.code}): #{JSON.parse(resp.body)}"
begin
reason = JSON.parse(resp.body)
rescue JSON::ParserError
nil
end

raise HTTPError, "#{resp.code_type} (#{resp.code}): #{reason}"
end

private
Expand Down
10 changes: 10 additions & 0 deletions test/test_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,14 @@ def test_root_directory

assert_equal "/tmp", Telebugs::Config.instance.root_directory
end

def test_middleware
middleware_class = Class.new(Telebugs::Middleware)

Telebugs.configure do |c|
c.middleware.use middleware_class.new
end

assert_equal 1, Telebugs::Config.instance.middleware.middlewares.size
end
end
57 changes: 57 additions & 0 deletions test/test_middleware_stack.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

require "test_helper"

class TestFilteringMiddleware < Telebugs::Middleware
def call(report)
report.data[:errors][0][:message] = "[FILTERED]"
end

def weight
1
end
end

class TestStartLineMiddleware < Telebugs::Middleware
def call(report)
report.data[:errors][0][:backtrace] = {123 => 456}
end

def weight
10
end
end

class TestMiddlewareStack < Minitest::Test
def test_call
stack = Telebugs::MiddlewareStack.new
stack.use TestFilteringMiddleware.new
stack.use TestStartLineMiddleware.new

report = Telebugs::Report.new(StandardError.new("error message"))
stack.call(report)

assert_equal "[FILTERED]", report.data[:errors][0][:message]
assert_equal({123 => 456}, report.data[:errors][0][:backtrace])
end

def test_weight
stack = Telebugs::MiddlewareStack.new
stack.use TestFilteringMiddleware.new
stack.use TestStartLineMiddleware.new

assert_equal(
[TestStartLineMiddleware, TestFilteringMiddleware],
stack.middlewares.map(&:class)
)

stack = Telebugs::MiddlewareStack.new
stack.use TestStartLineMiddleware.new
stack.use TestFilteringMiddleware.new

assert_equal(
[TestStartLineMiddleware, TestFilteringMiddleware],
stack.middlewares.map(&:class)
)
end
end
35 changes: 34 additions & 1 deletion test/test_notifier.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,50 @@

require "test_helper"

class TestIgnoreMiddleware < Telebugs::Middleware
def call(report)
report.ignored = true
end
end

class TestNotifier < Minitest::Test
def teardown
WebMock.reset!
Telebugs::Config.instance.reset
end

def test_notify_returns_a_promise_that_resolves_to_a_hash
stub_request(:post, Telebugs::Config.instance.api_url)
stub = stub_request(:post, Telebugs::Config.instance.api_url)
.to_return(status: 201, body: {id: "123"}.to_json)

p = Telebugs::Notifier.new.notify(StandardError.new)

assert_equal({"id" => "123"}, p.value)
assert_requested stub
end

def test_notify_returns_a_promise_that_rejects_on_http_error
stub = stub_request(:post, Telebugs::Config.instance.api_url)
.to_return(status: 500)

p = Telebugs::Notifier.new.notify(StandardError.new)

assert_nil p.value
assert_instance_of(Telebugs::HTTPError, p.reason)
assert_requested stub
end

def test_notify_does_not_send_ignored_errors
stub = stub_request(:post, Telebugs::Config.instance.api_url)
.to_return(status: 201, body: {id: "123"}.to_json)

Telebugs.configure do |c|
c.middleware.use TestIgnoreMiddleware.new
end

p = Telebugs::Notifier.new.notify(StandardError.new)
p.wait

refute_requested stub
end
end
8 changes: 8 additions & 0 deletions test/test_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,12 @@ def test_as_json_code
assert_nil backtrace[1][:code]
assert_nil backtrace[2][:code]
end

def test_ignore
r = Telebugs::Report.new(StandardError.new)
refute r.ignored

r.ignored = true
assert r.ignored
end
end

0 comments on commit 369aadc

Please sign in to comment.