class BridgeAPI::Client

Constants

ACCOUNT_PATH
ADMIN_PATH
AFFILIATED_SUBACCOUNTS
API_PATH
API_VERSION
AUTHOR_PATH
BATCH_PATH
CLONE_OBJECTS_PATH
COURSE_TEMPLATE_PATH
CUSTOM_FIELD_PATH
DATA_DUMP_DOWNLOAD_PATH
DATA_DUMP_PATH
DUE_DATE_PATH
ENROLLMENT_PATH
GROUPS_PATH
LEARNERS_PATH
LEARNER_ENROLLMENTS
LEARNER_ITEMS_PATH
LEARNER_PATH
LIVE_COURSES_PATH
LTI_TOOLS_PATH
MANAGER_PATH
NEW_TEMPORARY_USERS
PROGRAM_ENROLLMENT_PATH
PROGRAM_PATH
PUBLISH_PATH
RESET_PATH
RESTORE_PATH
RESULT_MAPPING
ROLE_PATH
SESSIONS_PATH
SUB_ACCOUNT_PATH
SUPPORT_PATH
USER_PATH
WEB_CONFERENCE_PATH

Public Class Methods

new(options = {}, &block) click to toggle source
Calls superclass method
# File lib/bridge_api/client.rb, line 58
def initialize(options = {}, &block)
  if BridgeAPI.enforce_rate_limits && has_token_pool?(options)
    options = initialize_from_token_pool(options)
  end
  super
end

Public Instance Methods

apply_rate_limits(response) click to toggle source
# File lib/bridge_api/client.rb, line 143
def apply_rate_limits(response)
  limit = response.headers['x-rate-limit-remaining']
  return if limit.nil?

  BridgeAPI.logger.debug("BRIDGE RATE LIMIT REMAINING: #{limit} for key #{config[:api_key]}")
  self.limit_remaining = limit.to_i
end
convert_tokens(config) click to toggle source
# File lib/bridge_api/client.rb, line 78
def convert_tokens(config)
  return config unless config.has_key?(:api_tokens)
  return config unless config[:api_tokens].is_a?(Array)
  config[:api_keys] ||= {}
  config[:api_tokens].each do |token|
    decoded_token_array = Base64.strict_decode64(token).split(':')
    config[:api_keys][decoded_token_array[0]] = decoded_token_array[1]
  end
  config.delete(:api_tokens)
  config
end
enforce_rate_limits() click to toggle source
# File lib/bridge_api/client.rb, line 127
def enforce_rate_limits
  return unless rate_limit_reached?

  rotate_token! 
  if rate_limit_reached?
    sleep_time = rand(BridgeAPI.max_sleep_seconds)
    sleep_time = BridgeAPI.min_sleep_seconds if sleep_time < BridgeAPI.min_sleep_seconds 
    BridgeAPI.logger.debug("Rate limit reached sleeping for #{sleep_time}")
    sleep(sleep_time)
  end
end
get_next_key(keys, current_key) click to toggle source
# File lib/bridge_api/client.rb, line 111
def get_next_key(keys, current_key)
  keys.delete(current_key)
  usable_key = keys.find do |key|
    limit = rate_limit(key)
    current_key_limit = limit.present? ? limit.fetch('current') : 0
    BridgeAPI.beginning_rate_limit - current_key_limit > BridgeAPI.rate_limit_threshold
  end
  usable_key || keys[rand(keys.length)]
end
has_token_pool?(config) click to toggle source
# File lib/bridge_api/client.rb, line 73
def has_token_pool?(config)
  config[:api_keys].is_a?(Hash) && config[:api_keys].keys.count >= 1 ||
    config[:api_tokens].is_a?(Array) && config[:api_tokens].count >= 1
end
initialize_from_token_pool(config) click to toggle source
# File lib/bridge_api/client.rb, line 90
def initialize_from_token_pool(config)
  config = convert_tokens(config)  
  creds = config[:api_keys].first
  config[:api_key]    ||= creds[0]
  config[:api_secret] ||= creds[1]
  config
end
limit_remaining() click to toggle source
# File lib/bridge_api/client.rb, line 151
def limit_remaining
  if using_master_rate_limit?
    limit = rate_limit(config[:api_key])
    if limit.nil?
      set_rate_limit(config[:api_key], 0)
      limit = { current: 0 }.with_indifferent_access
    end
    limit['current']
  else
    BridgeAPI.rate_limits[config[:api_key]]
  end
end
limit_remaining=(value) click to toggle source
# File lib/bridge_api/client.rb, line 164
def limit_remaining=(value)
  if using_master_rate_limit?
    set_rate_limit(config[:api_key], value)
  else
    BridgeAPI.rate_limits[config[:api_key]] = value
  end
  refresh_stale_tokens
end
rate_limit(key) click to toggle source
# File lib/bridge_api/client.rb, line 179
def rate_limit(key)
  BridgeAPI.master_mutex.synchronize do
    PaulWalker::RateLimit.get(key, key)
  end
end
rate_limit_reached?() click to toggle source
# File lib/bridge_api/client.rb, line 121
def rate_limit_reached?
  return false unless BridgeAPI.enforce_rate_limits && limit_remaining.present?

  limit_remaining < BridgeAPI.rate_limit_threshold
end
refresh_stale_tokens() click to toggle source
# File lib/bridge_api/client.rb, line 185
def refresh_stale_tokens
  return unless using_master_rate_limit?
  return unless config[:api_keys].present?
  api_keys = config[:api_keys].keys
  api_keys.delete(config[:api_key])
  api_keys.each do |key|
    limit = rate_limit(key)
    next unless limit.present?
    if limit['timestamp'].present? && DateTime.parse(limit['timestamp']) < 1.minute.ago
      BridgeAPI.logger.debug("Refreshing: #{key}")
      set_rate_limit(key, 0)
    end
  end
end
request(method, &block) click to toggle source

Override Footrest request for ApiArray support

# File lib/bridge_api/client.rb, line 66
def request(method, &block)
  enforce_rate_limits if rate_limit_reached?
  response = connection.send(method, &block)
  apply_rate_limits(response)
  ApiArray.process_response(response, self, RESULT_MAPPING)
end
rotate_token!() click to toggle source

rotates to the next token in the pool (by order in which they were provided)

# File lib/bridge_api/client.rb, line 99
def rotate_token! 
  return unless config[:api_keys].present?
  old_api_key = config[:api_key]
  keys = config[:api_keys].keys
  return if keys.count <= 1
  key                 = get_next_key(keys, config[:api_key])
  config[:api_key]    = key
  config[:api_secret] = config[:api_keys][key]
  set_connection(config)
  BridgeAPI.logger.debug("ROTATED TO KEY: #{config[:api_key]}")
end
set_connection(config) click to toggle source
# File lib/bridge_api/client.rb, line 200
def set_connection(config)
  config[:logger] = config[:logging] if config[:logging]
  @connection     = Faraday.new(url: config[:prefix]) do |faraday|
    faraday.request :multipart
    faraday.request :url_encoded
    if config[:logger] == true
      faraday.response :logger
    elsif config[:logger]
      faraday.use Faraday::Response::Logger, config[:logger]
    end
    faraday.use Footrest::FollowRedirects, limit: 5 unless config[:follow_redirects] == false
    faraday.adapter Faraday.default_adapter
    faraday.use Footrest::ParseJson, content_type: /\bjson$/
    faraday.use Footrest::RaiseFootrestErrors
    faraday.use Footrest::Pagination
    faraday.headers[:accept]     = 'application/json'
    faraday.headers[:user_agent] = 'Footrest'
    if config[:api_key] && config[:api_secret]
      faraday.headers[:authorization] = 'Basic ' + Base64.strict_encode64("#{config[:api_key]}:#{config[:api_secret]}")
    elsif config[:token]
      faraday.headers[:authorization] = "Bearer #{config[:token]}"
    else
      raise 'No api authorization provided'
    end
  end
end
set_rate_limit(key, limit) click to toggle source
# File lib/bridge_api/client.rb, line 173
def set_rate_limit(key, limit)
  BridgeAPI.master_mutex.synchronize do
    PaulWalker::RateLimit.add(key, key, limit, BridgeAPI.beginning_rate_limit)
  end
end
using_master_rate_limit?() click to toggle source
# File lib/bridge_api/client.rb, line 139
def using_master_rate_limit?
  config[:master_rate_limit].present? || BridgeAPI.master_rate_limit.present?
end