Class: BridgeAPI::Client

Inherits:
Footrest::Client
  • Object
show all
Defined in:
lib/bridge_api/client.rb,
lib/bridge_api/client/role.rb,
lib/bridge_api/client/user.rb,
lib/bridge_api/client/group.rb,
lib/bridge_api/client/account.rb,
lib/bridge_api/client/manager.rb,
lib/bridge_api/client/program.rb,
lib/bridge_api/client/data_dump.rb,
lib/bridge_api/client/enrollment.rb,
lib/bridge_api/client/affiliation.rb,
lib/bridge_api/client/live_course.rb,
lib/bridge_api/client/sub_account.rb,
lib/bridge_api/client/clone_object.rb,
lib/bridge_api/client/custom_field.rb,
lib/bridge_api/client/learner_item.rb,
lib/bridge_api/client/course_template.rb,
lib/bridge_api/client/program_enrollment.rb,
lib/bridge_api/client/live_course_session.rb,
lib/bridge_api/client/live_course_enrollment.rb

Defined Under Namespace

Modules: Account, Affiliation, CloneObject, CourseTemplate, CustomField, DataDump, Enrollment, Group, LearnerItem, LiveCourse, LiveCourseEnrollment, LiveCourseSession, Manager, Program, ProgramEnrollment, Role, SubAccount, User

Constant Summary collapse

DATA_DUMP_DOWNLOAD_PATH =
'/data_dumps/download'
DATA_DUMP_PATH =
'/data_dumps'
COURSE_TEMPLATE_PATH =
'/course_templates'
ENROLLMENT_PATH =
'/enrollments'
LTI_TOOLS_PATH =
'/lti_tools'
PROGRAM_PATH =
'/programs'
PROGRAM_ENROLLMENT_PATH =
'/learners'
USER_PATH =
'/users'
GROUPS_PATH =
'/groups'
MANAGER_PATH =
'/managers'
ADMIN_PATH =
'/admin'
AUTHOR_PATH =
'/author'
LEARNER_PATH =
'/learner'
LEARNERS_PATH =
'/learners'
LEARNER_ITEMS_PATH =
'/learner_items'
CUSTOM_FIELD_PATH =
'/custom_fields'
SUB_ACCOUNT_PATH =
'/sub_accounts'
SUPPORT_PATH =
'/support'
ACCOUNT_PATH =
'/accounts'
CLONE_OBJECTS_PATH =
'/clone_objects'
API_VERSION =
1
API_PATH =
'/api'
ROLE_PATH =
'/roles'
AFFILIATED_SUBACCOUNTS =
'/affiliated_sub_accounts'
BATCH_PATH =
'/batch'
LIVE_COURSES_PATH =
'/live_courses'
SESSIONS_PATH =
'/sessions'
PUBLISH_PATH =
'/publish'
WEB_CONFERENCE_PATH =
'/web_conference'
RESTORE_PATH =
'/restore'
DUE_DATE_PATH =
'/due_date'
RESET_PATH =
'/reset'
RESULT_MAPPING =
{}

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &block) ⇒ Client

Returns a new instance of Client.



56
57
58
59
60
61
# File 'lib/bridge_api/client.rb', line 56

def initialize(options = {}, &block)
  if BridgeAPI.enforce_rate_limits && has_token_pool?(options)
    options = initialize_from_token_pool(options)
  end
  super
end

Instance Method Details

#apply_rate_limits(response) ⇒ Object



147
148
149
150
151
152
153
# File 'lib/bridge_api/client.rb', line 147

def apply_rate_limits(response)
  limit = response.headers['x-rate-limit-remaining']
  return if limit.nil?

  BridgeAPI.logger.debug("BRIDGE RATE LIMIT REMAINING: #{limit}")
  self.limit_remaining = limit.to_i
end

#enforce_rate_limitsObject



130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/bridge_api/client.rb', line 130

def enforce_rate_limits
  return unless rate_limit_reached?

  tts     = ((BridgeAPI.beginning_rate_limit - limit_remaining) / 5).ceil
  tts     = BridgeAPI.min_sleep_seconds if tts < BridgeAPI.min_sleep_seconds
  tts     = BridgeAPI.max_sleep_seconds if tts > BridgeAPI.max_sleep_seconds
  message = "Bridge API rate limit minimum #{BridgeAPI.rate_limit_threshold} reached for key: '#{config[:api_key]}'. "\
    "Sleeping for #{tts} second(s) to catch up ~zzZZ~. "\
    "Limit Remaining: #{limit_remaining}"
  BridgeAPI.logger.debug(message)
  sleep(tts)
end

#get_next_key(keys, current_key) ⇒ Object



118
119
120
121
122
# File 'lib/bridge_api/client.rb', line 118

def get_next_key(keys, current_key)
  i = keys.index(current_key) || -1
  i = (i + 2) > keys.count ? 0 : (i + 1)
  keys[i]
end

#has_token_pool?(config) ⇒ Boolean

Returns:

  • (Boolean)


78
79
80
81
# File 'lib/bridge_api/client.rb', line 78

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) ⇒ Object

Since a pool is passed in, initialize first token with the first token passed in



84
85
86
87
88
89
90
91
92
93
# File 'lib/bridge_api/client.rb', line 84

def initialize_from_token_pool(config)
  if config[:api_keys].is_a?(Hash)
    creds = config[:api_keys].first
    config[:api_key]    ||= creds[0]
    config[:api_secret] ||= creds[1]
  elsif config[:api_tokens].is_a?(Array)
    config[:token] ||= config[:api_tokens].first
  end
  config
end

#limit_remainingObject



155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/bridge_api/client.rb', line 155

def limit_remaining
  if using_master_rate_limit?
    BridgeAPI.master_mutex.synchronize do
      limit = PaulWalker::RateLimit.get(config[:api_key], config[:api_key])
      if limit.nil?
        PaulWalker::RateLimit.add(config[:api_key], config[:api_key], 0, BridgeAPI.beginning_rate_limit)
        limit = { current: 0 }.with_indifferent_access
      end
      limit['current']
    end
  else
    BridgeAPI.rate_limits[config[:api_key]]
  end
end

#limit_remaining=(value) ⇒ Object



170
171
172
173
174
175
176
177
178
# File 'lib/bridge_api/client.rb', line 170

def limit_remaining=(value)
  if using_master_rate_limit?
    BridgeAPI.master_mutex.synchronize do
      PaulWalker::RateLimit.add(config[:api_key], config[:api_key], value, BridgeAPI.beginning_rate_limit)
    end
  else
    BridgeAPI.rate_limits[config[:api_key]] = value
  end
end

#rate_limit_reached?Boolean

Returns:

  • (Boolean)


124
125
126
127
128
# File 'lib/bridge_api/client.rb', line 124

def rate_limit_reached?
  return false unless BridgeAPI.enforce_rate_limits && limit_remaining.present?

  limit_remaining < BridgeAPI.rate_limit_threshold
end

#request(method, &block) ⇒ Object

Override Footrest request for ApiArray support



64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/bridge_api/client.rb', line 64

def request(method, &block)
  if has_token_pool?(config)
    (config[:api_tokens] || config[:api_keys].keys).size.times do
      break unless rate_limit_reached?

      rotate_token!
    end
  end
  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!Object

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



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/bridge_api/client.rb', line 96

def rotate_token!
  BridgeAPI.master_mutex.synchronize do
    old_api_key = config[:api_key]
    if config[:api_keys].is_a?(Hash)
      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]
    elsif config[:api_tokens].is_a?(Array)
      keys = config[:api_tokens]
      return if keys.count <= 1

      token = get_next_key(keys, config[:token])
      config[:token] = token
    end
    BridgeAPI.logger.debug('rotating API Keys')
    set_connection(config)
  end
end

#set_connection(config) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/bridge_api/client.rb', line 180

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

#using_master_rate_limit?Boolean

Returns:

  • (Boolean)


143
144
145
# File 'lib/bridge_api/client.rb', line 143

def using_master_rate_limit?
  config[:master_rate_limit].present? || BridgeAPI.master_rate_limit.present?
end