Kentico/delivery-sdk-ruby

View on GitHub
lib/delivery/client/request_manager.rb

Summary

Maintainability
A
1 hr
Test Coverage
require 'rest-client'

module Kontent
  module Ai
    module Delivery
      class RequestManager
        class << self
          MAX_ATTEMPTS = 6
          MAX_DELAY_SECONDS = 30
          INITIAL_DELAY = 1
          RETRY_WHEN_CODE = [408, 429, 500, 502, 503, 504].freeze
          CODES_WITH_POSSIBLE_RETRY_HEADER = [429, 503].freeze

          def start(query, headers)
            @query = query
            @headers = headers
            @times_run = 1
            @delay = INITIAL_DELAY
            @url = @query.provide_url
            @total_delay = 0
            continue
          end

          private

          def should_retry(potential_response)
            return potential_response if @times_run == MAX_ATTEMPTS ||
                                         !RETRY_WHEN_CODE.include?(potential_response.http_code) ||
                                         !@query.with_retry_policy ||
                                         @total_delay >= MAX_DELAY_SECONDS

            next_delay
            sleep(@delay)
            @total_delay += @delay
            continue
          end

          # Generates a random delay based on times_run, then increases times_run
          def next_delay
            min = 0.8 * INITIAL_DELAY
            max = (1.2 * INITIAL_DELAY) * (2**@times_run)
            @delay = rand(min..max)
            @times_run += 1
          end

          def continue
            if ENV['TEST'] == '1'
              resp = Kontent::Ai::Delivery::Tests::FakeResponder.get_response @query, @url, @headers
              return should_retry(resp) if resp.is_a? Kontent::Ai::Delivery::Responses::ResponseBase

              make_response resp # resp is pure JSON
            else
              begin
                resp = RestClient.get @url, @headers
              rescue RestClient::ExceptionWithResponse => err
                should_retry Kontent::Ai::Delivery::Responses::ResponseBase.new err.http_code, err.response
              rescue RestClient::SSLCertificateNotVerified => err
                should_retry Kontent::Ai::Delivery::Responses::ResponseBase.new 500, err
              rescue SocketError => err
                should_retry Kontent::Ai::Delivery::Responses::ResponseBase.new 500, err.message
              else
                make_response resp
              end
            end
          end

          # Converts a standard REST response based on the type of query.
          #
          # * *Returns*:
          #   - An object derived from the Kontent::Ai::Delivery::Responses::ResponseBase class
          def make_response(response)
            case @query.query_type
            when Kontent::Ai::Delivery::QUERY_TYPE_ITEMS_FEED
              Kontent::Ai::Delivery::Responses::DeliveryItemsFeedResponse.new(
                response.headers,
                response.body,
                @query
              )
            when Kontent::Ai::Delivery::QUERY_TYPE_ITEMS
              respond_item response
            when Kontent::Ai::Delivery::QUERY_TYPE_TYPES
              respond_type response
            when Kontent::Ai::Delivery::QUERY_TYPE_TAXONOMIES
              respond_taxonomy response
            when Kontent::Ai::Delivery::QUERY_TYPE_ELEMENT
              Kontent::Ai::Delivery::Responses::DeliveryElementResponse.new response.headers, response.body
            when Kontent::Ai::Delivery::QUERY_TYPE_LANGUAGES
              Kontent::Ai::Delivery::Responses::DeliveryLanguageListingResponse.new response.headers, response.body
            end
          end

          def respond_type(response)
            if @query.code_name.nil?
              Kontent::Ai::Delivery::Responses::DeliveryTypeListingResponse.new response.headers, response.body
            else
              Kontent::Ai::Delivery::Responses::DeliveryTypeResponse.new response.headers, response.body
            end
          end

          def respond_taxonomy(response)
            if @query.code_name.nil?
              Kontent::Ai::Delivery::Responses::DeliveryTaxonomyListingResponse.new response.headers, response.body
            else
              Kontent::Ai::Delivery::Responses::DeliveryTaxonomyResponse.new response.headers, response.body
            end
          end

          def respond_item(response)
            if @query.code_name.nil?
              Kontent::Ai::Delivery::Responses::DeliveryItemListingResponse.new(
                response.headers,
                response.body,
                @query
              )
            else
              Kontent::Ai::Delivery::Responses::DeliveryItemResponse.new(
                response.headers,
                response.body,
                @query
              )
            end
          end
        end
      end
    end
  end
end