Skip to content

Commit 579fa39

Browse files
committed
feat: add api error reporters
1 parent bd9be92 commit 579fa39

File tree

7 files changed

+202
-13
lines changed

7 files changed

+202
-13
lines changed

lib/pact_broker/api/resources/base_resource.rb

+2-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require 'webmachine'
2+
require 'pact_broker/api/resources/error_handler'
23
require 'pact_broker/services'
34
require 'pact_broker/api/decorators'
45
require 'pact_broker/logging'
@@ -15,17 +16,6 @@ module Resources
1516

1617
class InvalidJsonError < StandardError ; end
1718

18-
class ErrorHandler
19-
20-
include PactBroker::Logging
21-
22-
def self.handle_exception e, response
23-
logger.error e
24-
logger.error e.backtrace
25-
response.body = {:message => e.message, :backtrace => e.backtrace }.to_json
26-
end
27-
end
28-
2919
class BaseResource < Webmachine::Resource
3020

3121
include PactBroker::Services
@@ -90,7 +80,7 @@ def decorator_context options = {}
9080
end
9181

9282
def handle_exception e
93-
PactBroker::Api::Resources::ErrorHandler.handle_exception(e, response)
83+
PactBroker::Api::Resources::ErrorHandler.call(e, request, response)
9484
end
9585

9686
def params
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
require 'webmachine/convert_request_to_rack_env'
2+
require 'pact_broker/configuration'
3+
4+
module PactBroker
5+
module Api
6+
module Resources
7+
class ErrorHandler
8+
9+
include PactBroker::Logging
10+
11+
def self.call e, request, response
12+
logger.error e
13+
logger.error e.backtrace
14+
response.body = {:message => e.message, :backtrace => e.backtrace }.to_json
15+
report e, request
16+
end
17+
18+
def self.report e, request
19+
PactBroker.configuration.api_error_reporters.each do | error_notifier |
20+
begin
21+
error_notifier.call(e, env: Webmachine::ConvertRequestToRackEnv.call(request))
22+
rescue StandardError => e
23+
log_error(e, "Error executing api_error_reporter")
24+
end
25+
end
26+
end
27+
end
28+
end
29+
end
30+
end

lib/pact_broker/configuration.rb

+16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
require 'pact_broker/error'
2+
13
module PactBroker
24

5+
class ConfigurationError < PactBroker::Error; end
6+
37
def self.configuration
48
@@configuration ||= Configuration.default_configuration
59
end
@@ -30,13 +34,15 @@ class Configuration
3034
attr_accessor :enable_public_badge_access, :shields_io_base_url
3135
attr_accessor :webhook_retry_schedule
3236
attr_accessor :disable_ssl_verification
37+
attr_reader :api_error_reporters
3338
attr_writer :logger
3439

3540
def initialize
3641
@before_resource_hook = ->(resource){}
3742
@after_resource_hook = ->(resource){}
3843
@authenticate_with_basic_auth = nil
3944
@authorize = nil
45+
@api_error_reporters = []
4046
end
4147

4248
def logger
@@ -121,6 +127,16 @@ def after_resource &block
121127
end
122128
end
123129

130+
def add_api_error_reporter &block
131+
if block_given?
132+
unless block.arity == 2
133+
raise ConfigurationError.new("api_error_notfifier block must accept two arguments, 'error' and 'options'")
134+
end
135+
@api_error_reporters << block
136+
nil
137+
end
138+
end
139+
124140
def enable_badge_resources= enable_badge_resources
125141
puts "Pact Broker configuration property `enable_badge_resources` is deprecated. Please use `enable_public_badge_access`"
126142
self.enable_public_badge_access = enable_badge_resources
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module Webmachine
2+
class ConvertRequestToRackEnv
3+
def self.call(request)
4+
env = {
5+
'REQUEST_METHOD' => request.method.upcase,
6+
'CONTENT_TYPE' => request.headers['Content-Type'],
7+
'PATH_INFO' => request.uri.path,
8+
'QUERY_STRING' => request.uri.query || "",
9+
'SERVER_NAME' => request.uri.host,
10+
'SERVER_PORT' => request.uri.port.to_s,
11+
'SCRIPT_NAME' => '',
12+
'rack.url_scheme' => request.uri.scheme,
13+
'rack.input' => request.body.to_io ? StringIO.new(request.body.to_s) : nil
14+
}
15+
http_headers = request.headers.each do | key, value |
16+
env[convert_http_header_name_to_rack_header_name(key)] = value
17+
end
18+
env
19+
end
20+
21+
def self.convert_http_header_name_to_rack_header_name(http_header_name)
22+
if http_header_name.downcase == 'content-type' || http_header_name.downcase == 'content-length'
23+
http_header_name.upcase.gsub('-', '_')
24+
else
25+
"HTTP_" + http_header_name.upcase.gsub('-', '_')
26+
end
27+
end
28+
end
29+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
require 'pact_broker/api/resources/error_handler'
2+
3+
module PactBroker
4+
module Api
5+
module Resources
6+
describe ErrorHandler do
7+
describe "call" do
8+
let(:error) { PactBroker::Error.new('test error') }
9+
let(:thing) { double('thing', call: nil, another_call: nil) }
10+
let(:options) { { env: env } }
11+
let(:request) { double('request' ) }
12+
let(:response) { double('response', :body= => nil) }
13+
let(:env) { double('env') }
14+
15+
subject { ErrorHandler.call(error, request, response) }
16+
17+
before do
18+
allow(Webmachine::ConvertRequestToRackEnv).to receive(:call).and_return(env)
19+
PactBroker.configuration.add_api_error_reporter do | error, options |
20+
thing.call(error, options)
21+
end
22+
23+
PactBroker.configuration.add_api_error_reporter do | error, options |
24+
thing.another_call(error, options)
25+
end
26+
end
27+
28+
it "invokes the api error reporters" do
29+
expect(thing).to receive(:call).with(error, options)
30+
expect(thing).to receive(:another_call).with(error, options)
31+
subject
32+
end
33+
34+
context "when the error reporter raises an error itself" do
35+
class TestError < StandardError; end
36+
37+
before do
38+
expect(thing).to receive(:call).and_raise(TestError.new)
39+
end
40+
41+
it "logs the error" do
42+
expect(PactBroker.logger).to receive(:error).at_least(1).times
43+
subject
44+
end
45+
46+
it "does not propagate the error" do
47+
expect(thing).to receive(:another_call)
48+
subject
49+
end
50+
end
51+
end
52+
end
53+
end
54+
end
55+
end

spec/lib/pact_broker/configuration_spec.rb

+19-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ module PactBroker
4040
end
4141

4242
describe "load_from_database!" do
43-
let(:configuration) { PactBroker::Configuration.new}
43+
let(:configuration) { PactBroker::Configuration.new }
4444

4545
before do
4646
PactBroker::Config::Setting.create(name: 'use_case_sensitive_resource_names', type: 'string', value: 'foo')
@@ -51,6 +51,24 @@ module PactBroker
5151
expect(configuration.use_case_sensitive_resource_names).to eq "foo"
5252
end
5353
end
54+
55+
describe "add_api_error_reporter" do
56+
let(:configuration) { PactBroker::Configuration.new }
57+
let(:block) { Proc.new{ | error, options | } }
58+
59+
it "should add the error notifier " do
60+
configuration.add_api_error_reporter(&block)
61+
expect(configuration.api_error_reporters.first).to eq block
62+
end
63+
64+
context "with a proc with the wrong number of arguments" do
65+
let(:block) { Proc.new{ | error | } }
66+
67+
it "raises an error" do
68+
expect { configuration.add_api_error_reporter(&block) }.to raise_error PactBroker::ConfigurationError
69+
end
70+
end
71+
end
5472
end
5573
end
5674
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
require 'webmachine/convert_request_to_rack_env'
2+
require 'webmachine/request'
3+
4+
module Webmachine
5+
describe ConvertRequestToRackEnv do
6+
7+
let(:rack_env) do
8+
{
9+
"rack.input"=>StringIO.new('foo'),
10+
"REQUEST_METHOD"=>"POST",
11+
"SERVER_NAME"=>"example.org",
12+
"SERVER_PORT"=>"80",
13+
"QUERY_STRING"=>"",
14+
"PATH_INFO"=>"/foo",
15+
"rack.url_scheme"=>"http",
16+
"SCRIPT_NAME"=>"",
17+
"CONTENT_LENGTH"=>"0",
18+
"HTTP_HOST"=>"example.org",
19+
"CONTENT_TYPE"=>"application/x-www-form-urlencoded",
20+
}
21+
end
22+
23+
let(:headers) do
24+
Webmachine::Headers.from_cgi(rack_env)
25+
end
26+
27+
let(:rack_req) { ::Rack::Request.new(rack_env) }
28+
let(:webmachine_request) do
29+
Webmachine::Request.new(rack_req.request_method,
30+
rack_req.url,
31+
headers,
32+
Webmachine::Adapters::Rack::RequestBody.new(rack_req),
33+
nil,
34+
nil
35+
)
36+
end
37+
38+
subject { ConvertRequestToRackEnv.call(webmachine_request) }
39+
40+
describe ".call" do
41+
it "" do
42+
expected_env = rack_env.dup
43+
expected_env.delete('rack.input')
44+
actual_env = subject
45+
actual_rack_input = actual_env.delete('rack.input')
46+
expect(subject).to eq expected_env
47+
expect(actual_rack_input.string).to eq 'foo'
48+
end
49+
end
50+
end
51+
end

0 commit comments

Comments
 (0)