From 46b65e554195daa74f70d2841435ad79db6e226d Mon Sep 17 00:00:00 2001 From: markgibbons Date: Mon, 28 Jan 2019 12:58:10 -0800 Subject: [PATCH] Add support to handle 429 responses from a rate limited gitlab instance Gitlab may respond with a 429 error and a message of 'Retry later'. The response body in this case has content-type 'text/plain'. This change adds support for 429 errors and processes the body of responses with content formated as 'text/plain'. Add tests for the changes to Gitlab:Error:ResponseError.build_error_message --- .rubocop.yml | 3 +++ lib/gitlab/error.rb | 16 +++++++++++++++- lib/gitlab/request.rb | 1 + spec/gitlab/error_spec.rb | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.rubocop.yml b/.rubocop.yml index cbdd2965b..f976bda6c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -14,6 +14,9 @@ Metrics/BlockLength: Exclude: - 'spec/**/*' +Metrics/CyclomaticComplexity: + Max: 15 + Style/Documentation: Enabled: false diff --git a/lib/gitlab/error.rb b/lib/gitlab/error.rb index 6fc048db4..d6baae611 100644 --- a/lib/gitlab/error.rb +++ b/lib/gitlab/error.rb @@ -40,7 +40,7 @@ def response_message # # @return [String] def build_error_message - parsed_response = @response.parsed_response + parsed_response = classified_response message = check_error_keys(parsed_response) "Server responded with code #{@response.code}, message: " \ "#{handle_message(message)}. " \ @@ -54,6 +54,17 @@ def check_error_keys(resp) key ? resp.send(key) : resp end + # Parse the body based on the classification of the body content type + # + # @return parsed response + def classified_response + if @response.respond_to?('headers') + @response.headers['content-type'] == 'text/plain' ? { message: @response.to_s } : @response.parsed_response + else + @response.parsed_response + end + end + # Handle error response message in case of nested hashes def handle_message(message) case message @@ -90,6 +101,9 @@ class Conflict < ResponseError; end # Raised when API endpoint returns the HTTP status code 422. class Unprocessable < ResponseError; end + # Raised when API endpoint returns the HTTP status code 429. + class TooManyRequests < ResponseError; end + # Raised when API endpoint returns the HTTP status code 500. class InternalServerError < ResponseError; end diff --git a/lib/gitlab/request.rb b/lib/gitlab/request.rb index cb7a59a23..be8f84d77 100644 --- a/lib/gitlab/request.rb +++ b/lib/gitlab/request.rb @@ -58,6 +58,7 @@ def validate(response) when 405 then Error::MethodNotAllowed when 409 then Error::Conflict when 422 then Error::Unprocessable + when 429 then Error::TooManyRequests when 500 then Error::InternalServerError when 502 then Error::BadGateway when 503 then Error::ServiceUnavailable diff --git a/spec/gitlab/error_spec.rb b/spec/gitlab/error_spec.rb index 26e3a524d..f5f1538a9 100644 --- a/spec/gitlab/error_spec.rb +++ b/spec/gitlab/error_spec.rb @@ -34,3 +34,21 @@ end end end + +describe Gitlab::Error::ResponseError do + before do + @request_double = double('request', base_uri: 'https://gitlab.com/api/v3', path: '/foo', options: {}) + end + + it 'Builds an error message from text' do + headers = { 'content-type' => 'text/plain' } + response_double = double('response', body: 'Retry later', to_s: 'Retry text', parsed_response: { message: 'Retry hash' }, code: 429, options: {}, headers: headers, request: @request_double) + expect(described_class.new(response_double).send(:build_error_message)).to match(/Retry text/) + end + + it 'Builds an error message from parsed json' do + headers = { 'content-type' => 'application/json' } + response_double = double('response', body: 'Retry later', to_s: 'Retry text', parsed_response: { message: 'Retry hash' }, code: 429, options: {}, headers: headers, request: @request_double) + expect(described_class.new(response_double).send(:build_error_message)).to match(/Retry hash/) + end +end