diff --git a/lib/gh.rb b/lib/gh.rb index 40ff114..30a3712 100644 --- a/lib/gh.rb +++ b/lib/gh.rb @@ -14,6 +14,7 @@ module GH autoload :Normalizer, 'gh/normalizer' autoload :Pagination, 'gh/pagination' autoload :Parallel, 'gh/parallel' + autoload :Retry, 'gh/retry' autoload :Remote, 'gh/remote' autoload :Response, 'gh/response' autoload :ResponseXHeaderFormatter, 'gh/response_x_header_formatter' diff --git a/lib/gh/retry.rb b/lib/gh/retry.rb new file mode 100644 index 0000000..857cf45 --- /dev/null +++ b/lib/gh/retry.rb @@ -0,0 +1,41 @@ +require 'gh' + +module GH + # Public: In some cases, the GitHub API can take some number of + # seconds to reflect a change in the system. This class catches 404 + # responses for any GET request and retries the request. + class Retry < Wrapper + DEFAULTS = { + retries: 5, + wait: 1 + } + + attr_accessor :retries, :wait + + def initialize(backend = nil, options = {}) + options = DEFAULTS.merge options + super backend, options + end + + def fetch_resource(key) + begin + decrement_retries! + super key + rescue GH::Error(response_status: 404) => e + retries_remaining? or raise e + sleep wait + fetch_resource key + end + end + + private + + def decrement_retries! + self.retries = self.retries - 1 + end + + def retries_remaining? + retries > 0 + end + end +end diff --git a/spec/retry_spec.rb b/spec/retry_spec.rb new file mode 100644 index 0000000..0615780 --- /dev/null +++ b/spec/retry_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe GH::Retry do + let(:not_finder) do + Class.new(GH::MockBackend) do + def fetch_resource(key) + if key =~ %r{users/not-found} + @requests << key + error = Struct.new(:info).new(response_status: 404) + raise GH::Error.new(error) + end + super(key) + end + end + end + + subject { described_class.new(not_finder.new, retries: 3, wait: 0.1) } + + it 'retries request specified number of times' do + expect { subject['users/not-found'] }.to raise_error(GH::Error) + expect(subject.backend.requests.count).to eq 3 + end + + it 'does not retry when response is successful' do + subject['users/rkh'] + expect(subject.backend.requests.count).to eq 1 + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e93fe3f..1c592f4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -48,7 +48,7 @@ def fetch_resource(key) key_fn = sanitize_filename(key) file = File.expand_path("../payloads/#{key_fn}.yml", __FILE__) @requests << key - result = @data[key] ||= begin + result = @data[key] ||= begin unless File.exist? file res = allow_http { super } FileUtils.mkdir_p File.dirname(file)