diff --git a/lib/auth0/mixins/httpproxy.rb b/lib/auth0/mixins/httpproxy.rb index f4446c47..128fdeb0 100644 --- a/lib/auth0/mixins/httpproxy.rb +++ b/lib/auth0/mixins/httpproxy.rb @@ -1,6 +1,8 @@ -require "addressable/uri" -require "retryable" -require_relative "../exception.rb" +# frozen_string_literal: true + +require 'addressable/uri' +require 'retryable' +require_relative '../exception' module Auth0 module Mixins @@ -8,6 +10,7 @@ module Mixins # for now, if you want to feel free to use your own http client module HTTPProxy attr_accessor :headers, :base_uri, :timeout, :retry_count + DEFAULT_RETRIES = 3 MAX_ALLOWED_RETRIES = 10 MAX_REQUEST_RETRY_JITTER = 250 @@ -16,10 +19,10 @@ module HTTPProxy BASE_DELAY = 100 # proxying requests from instance methods to HTTP class methods - %i(get post post_file post_form put patch delete delete_with_body).each do |method| + %i[get post post_file post_form put patch delete delete_with_body].each do |method| define_method(method) do |uri, body = {}, extra_headers = {}| - body = body.delete_if { |_, v| v.nil? } - token = get_token() + body = body.dup.delete_if { |_, v| v.nil? } if body.is_a?(Hash) + token = get_token authorization_header(token) unless token.nil? request_with_retry(method, uri, body, extra_headers) end @@ -27,8 +30,8 @@ module HTTPProxy def retry_options sleep_timer = lambda do |attempt| - wait = BASE_DELAY * (2**attempt-1) # Exponential delay with each subsequent request attempt. - wait += rand(wait+1..wait+MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window. + wait = BASE_DELAY * (2**attempt - 1) # Exponential delay with each subsequent request attempt. + wait += rand(wait + 1..wait + MAX_REQUEST_RETRY_JITTER) # Add jitter to the delay window. wait = [MAX_REQUEST_RETRY_DELAY, wait].min # Cap delay at MAX_REQUEST_RETRY_DELAY. wait = [MIN_REQUEST_RETRY_DELAY, wait].max # Ensure delay is no less than MIN_REQUEST_RETRY_DELAY. wait / 1000.to_f.round(2) # convert ms to seconds @@ -55,6 +58,7 @@ def url(path) def add_headers(h = {}) raise ArgumentError, 'Headers must be an object which responds to #to_hash' unless h.respond_to?(:to_hash) + @headers ||= {} @headers.merge!(h.to_hash) end @@ -72,28 +76,29 @@ def request_with_retry(method, uri, body = {}, extra_headers = {}) end def request(method, uri, body = {}, extra_headers = {}) - result = if method == :get - @headers ||= {} - get_headers = @headers.merge({params: body}).merge(extra_headers) - call(:get, encode_uri(uri), timeout, get_headers) - elsif method == :delete - @headers ||= {} - delete_headers = @headers.merge({ params: body }) - call(:delete, encode_uri(uri), timeout, delete_headers) - elsif method == :delete_with_body - call(:delete, encode_uri(uri), timeout, headers, body.to_json) - elsif method == :post_file - body.merge!(multipart: true) - # Ignore the default Content-Type headers and let the HTTP client define them - post_file_headers = headers.except('Content-Type') if headers != nil - # Actual call with the altered headers - call(:post, encode_uri(uri), timeout, post_file_headers, body) - elsif method == :post_form - form_post_headers = headers.except('Content-Type') if headers != nil - call(:post, encode_uri(uri), timeout, form_post_headers, body.compact) - else - call(method, encode_uri(uri), timeout, headers, body.to_json) - end + result = case method + when :get + @headers ||= {} + get_headers = @headers.merge({ params: body }).merge(extra_headers) + call(:get, encode_uri(uri), timeout, get_headers) + when :delete + @headers ||= {} + delete_headers = @headers.merge({ params: body }) + call(:delete, encode_uri(uri), timeout, delete_headers) + when :delete_with_body + call(:delete, encode_uri(uri), timeout, headers, body.to_json) + when :post_file + body.merge!(multipart: true) + # Ignore the default Content-Type headers and let the HTTP client define them + post_file_headers = headers.except('Content-Type') unless headers.nil? + # Actual call with the altered headers + call(:post, encode_uri(uri), timeout, post_file_headers, body) + when :post_form + form_post_headers = headers.except('Content-Type') unless headers.nil? + call(:post, encode_uri(uri), timeout, form_post_headers, body.compact) + else + call(method, encode_uri(uri), timeout, headers, body.to_json) + end case result.code when 200...226 then safe_parse_json(result.body) @@ -101,7 +106,8 @@ def request(method, uri, body = {}, extra_headers = {}) when 401 then raise Auth0::Unauthorized.new(result.body, code: result.code, headers: result.headers) when 403 then raise Auth0::AccessDenied.new(result.body, code: result.code, headers: result.headers) when 404 then raise Auth0::NotFound.new(result.body, code: result.code, headers: result.headers) - when 429 then raise Auth0::RateLimitEncountered.new(result.body, code: result.code, headers: result.headers) + when 429 then raise Auth0::RateLimitEncountered.new(result.body, code: result.code, + headers: result.headers) when 500 then raise Auth0::ServerError.new(result.body, code: result.code, headers: result.headers) else raise Auth0::Unsupported.new(result.body, code: result.code, headers: result.headers) end @@ -118,9 +124,9 @@ def call(method, url, timeout, headers, body = nil) rescue RestClient::Exception => e case e when RestClient::RequestTimeout - raise Auth0::RequestTimeout.new(e.message) + raise Auth0::RequestTimeout, e.message else - return e.response + e.response end end end diff --git a/spec/lib/auth0/mixins/httpproxy_spec.rb b/spec/lib/auth0/mixins/httpproxy_spec.rb index 40211896..760a584a 100644 --- a/spec/lib/auth0/mixins/httpproxy_spec.rb +++ b/spec/lib/auth0/mixins/httpproxy_spec.rb @@ -279,6 +279,19 @@ def expected_payload(method, overrides = {}) expect { @instance.send(http_method, '/test') }.not_to raise_error end + it "should handle array parameters for #{http_method} method" do + array_data = ['param1', 'param2'] + if http_method == :post_form + expected_params = expected_payload(http_method, { payload: array_data }) + else + expected_params = expected_payload(http_method, { payload: array_data.to_json }) + end + + expect(RestClient::Request).to receive(:execute).with(expected_params) + .and_return(StubResponse.new({}, true, 200)) + expect { @instance.send(http_method, '/test', array_data) }.not_to raise_error + end + it 'should not raise exception if data returned not in json format (should be fixed in v2)' do allow(RestClient::Request).to receive(:execute).with(expected_payload(http_method)) .and_return(StubResponse.new('Some random text here', true, 200))