class ActiveGenie::Clients::BaseClient

def apply_headers(request, headers)

Parameters:
  • headers (Hash) -- Additional headers to include
  • request (Net::HTTP::Request) -- The request object
def apply_headers(request, headers)
  DEFAULT_HEADERS.each do |key, value|
    request[key] = value
  end
  
  headers.each do |key, value|
    request[key.to_s] = value
  end
end

def build_uri(endpoint, params = {})

Returns:
  • (URI) - The constructed URI

Parameters:
  • params (Hash) -- Query parameters
  • endpoint (String) -- The API endpoint
def build_uri(endpoint, params = {})
  base_url = @app_config.api_url
  uri = URI("#{base_url}#{endpoint}")
  
  unless params.empty?
    uri.query = URI.encode_www_form(params)
  end
  
  uri
end

def create_http_client(uri, config)

Returns:
  • (Net::HTTP) - Configured HTTP client

Parameters:
  • config (Hash) -- Configuration options
  • uri (URI) -- The URI for the request
def create_http_client(uri, config)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = (uri.scheme == 'https')
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  http.read_timeout = config.dig(:runtime, :timeout) || DEFAULT_TIMEOUT
  http.open_timeout = config.dig(:runtime, :open_timeout) || DEFAULT_OPEN_TIMEOUT
  http
end

def delete(endpoint, headers: {}, params: {}, config: {})

Returns:
  • (Hash, nil) - The parsed JSON response or nil if empty

Parameters:
  • config (Hash) -- Configuration options including timeout, retries, etc.
  • params (Hash) -- Query parameters for the request
  • headers (Hash) -- Additional headers to include in the request
  • endpoint (String) -- The API endpoint to call
def delete(endpoint, headers: {}, params: {}, config: {})
  uri = build_uri(endpoint, params)
  request = Net::HTTP::Delete.new(uri)
  execute_request(uri, request, headers, config)
end

def execute_request(uri, request, headers, config)

Returns:
  • (Hash, nil) - The parsed JSON response or nil if empty

Parameters:
  • config (Hash) -- Configuration options
  • headers (Hash) -- Additional headers to include
  • request (Net::HTTP::Request) -- The request object
  • uri (URI) -- The URI for the request
def execute_request(uri, request, headers, config)
  start_time = Time.now
  
  # Apply headers
  apply_headers(request, headers)
  
  # Apply retry logic
  retry_with_backoff(config) do
    http = create_http_client(uri, config)
    
    begin
      response = http.request(request)
      
      # Handle common HTTP errors
      case response
      when Net::HTTPSuccess
        parsed_response = parse_response(response)
        
        # Log request details if logging is enabled
        log_request_details(
          uri: uri, 
          method: request.method,
          status: response.code,
          duration: Time.now - start_time,
          response: parsed_response
        )
        
        parsed_response
      when Net::HTTPTooManyRequests
        raise RateLimitError, "Rate limit exceeded: #{response.body}"
      when Net::HTTPClientError, Net::HTTPServerError
        raise ClientError, "HTTP Error #{response.code}: #{response.body}"
      else
        raise ClientError, "Unexpected response: #{response.code} - #{response.body}"
      end
    rescue Timeout::Error, Errno::ETIMEDOUT
      raise TimeoutError, "Request to #{uri} timed out"
    rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, SocketError => e
      raise NetworkError, "Network error: #{e.message}"
    end
  end
end

def get(endpoint, params: {}, headers: {}, config: {})

Returns:
  • (Hash, nil) - The parsed JSON response or nil if empty

Parameters:
  • config (Hash) -- Configuration options including timeout, retries, etc.
  • params (Hash) -- Query parameters for the request
  • headers (Hash) -- Additional headers to include in the request
  • endpoint (String) -- The API endpoint to call
def get(endpoint, params: {}, headers: {}, config: {})
  uri = build_uri(endpoint, params)
  request = Net::HTTP::Get.new(uri)
  execute_request(uri, request, headers, config)
end

def initialize(config)

def initialize(config)
  @app_config = config
end

def log_request_details(details)

Parameters:
  • details (Hash) -- Request and response details
def log_request_details(details)
  return unless defined?(ActiveGenie::Logger)
  
  ActiveGenie::Logger.trace({
    code: :http_request,
    uri: details[:uri].to_s,
    method: details[:method],
    status: details[:status],
    duration: details[:duration],
    response_size: details[:response].to_s.bytesize
  })
end

def parse_response(response)

Returns:
  • (Hash, nil) - Parsed JSON or nil if empty

Parameters:
  • response (Net::HTTPResponse) -- The HTTP response
def parse_response(response)
  return nil if response.body.nil? || response.body.empty?
  
  begin
    JSON.parse(response.body)
  rescue JSON::ParserError => e
    raise ClientError, "Failed to parse JSON response: #{e.message}"
  end
end

def post(endpoint, payload, params: {}, headers: {}, config: {})

Returns:
  • (Hash, nil) - The parsed JSON response or nil if empty

Parameters:
  • config (Hash) -- Configuration options including timeout, retries, etc.
  • headers (Hash) -- Additional headers to include in the request
  • payload (Hash) -- The request body to send
  • endpoint (String) -- The API endpoint to call
def post(endpoint, payload, params: {}, headers: {}, config: {})
  uri = build_uri(endpoint, params)
  request = Net::HTTP::Post.new(uri)
  request.body = payload.to_json
  execute_request(uri, request, headers, config)
end

def put(endpoint, payload, headers: {}, config: {})

Returns:
  • (Hash, nil) - The parsed JSON response or nil if empty

Parameters:
  • config (Hash) -- Configuration options including timeout, retries, etc.
  • headers (Hash) -- Additional headers to include in the request
  • payload (Hash) -- The request body to send
  • endpoint (String) -- The API endpoint to call
def put(endpoint, payload, headers: {}, config: {})
  uri = build_uri(endpoint)
  request = Net::HTTP::Put.new(uri)
  request.body = payload.to_json
  execute_request(uri, request, headers, config)
end

def retry_with_backoff(config = {})

Returns:
  • (Object) - The result of the block

Other tags:
    Yield: - The block to retry

Parameters:
  • config (Hash) -- Configuration options
def retry_with_backoff(config = {})
  max_retries = config.dig(:runtime, :max_retries) || DEFAULT_MAX_RETRIES
  retry_delay = config.dig(:runtime, :retry_delay) || DEFAULT_RETRY_DELAY
  
  retries = 0
  
  begin
    yield
  rescue RateLimitError, NetworkError => e
    if retries < max_retries
      sleep_time = retry_delay * (2 ** retries)
      retries += 1
      
      ActiveGenie::Logger.trace({
        code: :retry_attempt,
        attempt: retries,
        max_retries: max_retries,
        delay: sleep_time,
        error: e.message
      }) if defined?(ActiveGenie::Logger)
      
      sleep(sleep_time)
      retry
    else
      raise
    end
  end
end