bridge_api 0.1.63 → 0.1.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +31 -0
- data/.gitignore +3 -1
- data/lib/bridge_api/client/group.rb +12 -0
- data/lib/bridge_api/client/program.rb +4 -0
- data/lib/bridge_api/client.rb +74 -57
- data/lib/bridge_api/version.rb +1 -1
- data/lib/bridge_api.rb +1 -1
- data/spec/bridge_api/client/enrollment_spec.rb +0 -6
- data/spec/bridge_api/client_spec.rb +8 -7
- data/spec/support/fake_bridge.rb +4 -0
- metadata +16 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e33cd3207fe1494cbc64ea799f77ee399081ad6282283aaa5d577ecbe2b91c1
|
4
|
+
data.tar.gz: f2dea18b065d73fa9fe87598d236af598daa85d90dd884ee4b815c9f818b44d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89c9a1618bba16b9a707de5ca0aa056a0fc7aad2313f5304f5ba49a231be58ae89e54cdfe66e799b8f36e400f89bacac1b9b71c49fa3d40655059ebf91f8d986
|
7
|
+
data.tar.gz: 59c0dc141d8693cd88eb89f9dc1dcf9993beb549548afa4048781bc631c1a93e0b08b94cb9b79197ea0bc413f86b7d4177ab5fa230a6ffd5fb77fa2304429823
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Virtual environments with possible versions infos: https://github.com/actions/virtual-environments
|
2
|
+
|
3
|
+
name: CI
|
4
|
+
on: [push]
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
steps:
|
9
|
+
- uses: actions/checkout@v2 # Step 1: github action that clones the repository
|
10
|
+
- name: Setup Ruby # Step 2 (we gave the name 'Setup Ruby' to this step)
|
11
|
+
uses: ruby/setup-ruby@v1
|
12
|
+
with:
|
13
|
+
ruby-version: 2.5.0
|
14
|
+
- name: Install bundle and gems # Step 4
|
15
|
+
run: |
|
16
|
+
gem install bundler
|
17
|
+
bundle install --jobs 4 --retry 3
|
18
|
+
- name: Run tests # Step 6
|
19
|
+
run: COVERAGE=true bundle exec rspec
|
20
|
+
brakeman:
|
21
|
+
name: runner / brakeman # Step 8
|
22
|
+
runs-on: ubuntu-latest
|
23
|
+
steps:
|
24
|
+
- name: Check out code
|
25
|
+
uses: actions/checkout@v1
|
26
|
+
- name: brakeman
|
27
|
+
uses: reviewdog/action-brakeman@v1
|
28
|
+
with:
|
29
|
+
brakeman_version: 4.8.2
|
30
|
+
github_token: ${{ secrets.github_token }}
|
31
|
+
reporter: github-pr-review # Default is github-pr-check
|
data/.gitignore
CHANGED
@@ -10,6 +10,18 @@ module BridgeAPI
|
|
10
10
|
def update_group(group_id, params = {})
|
11
11
|
put("#{API_PATH}#{AUTHOR_PATH}#{GROUPS_PATH}/#{group_id}", params)
|
12
12
|
end
|
13
|
+
|
14
|
+
def group_relevance(params = {})
|
15
|
+
get("#{API_PATH}#{AUTHOR_PATH}/group_relevance", params)
|
16
|
+
end
|
17
|
+
|
18
|
+
def update_group_relevance(params = {})
|
19
|
+
put("#{API_PATH}#{AUTHOR_PATH}/group_relevance", params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_group_relevance(group_id, params)
|
23
|
+
delete("#{API_PATH}#{AUTHOR_PATH}/group_relevance/#{group_id}", params)
|
24
|
+
end
|
13
25
|
end
|
14
26
|
end
|
15
27
|
end
|
@@ -10,6 +10,10 @@ module BridgeAPI
|
|
10
10
|
def get_program(program_id, params = {})
|
11
11
|
get("#{API_PATH}#{AUTHOR_PATH}#{PROGRAM_PATH}/#{program_id}", params)
|
12
12
|
end
|
13
|
+
|
14
|
+
def complete_program(program_id, params = {})
|
15
|
+
post("#{API_PATH}#{ADMIN_PATH}#{PROGRAM_PATH}/#{program_id}/completions", params)
|
16
|
+
end
|
13
17
|
end
|
14
18
|
end
|
15
19
|
end
|
data/lib/bridge_api/client.rb
CHANGED
@@ -62,13 +62,6 @@ module BridgeAPI
|
|
62
62
|
|
63
63
|
# Override Footrest request for ApiArray support
|
64
64
|
def request(method, &block)
|
65
|
-
if has_token_pool?(config)
|
66
|
-
(config[:api_tokens] || config[:api_keys].keys).size.times do
|
67
|
-
break unless rate_limit_reached?
|
68
|
-
|
69
|
-
rotate_token!
|
70
|
-
end
|
71
|
-
end
|
72
65
|
enforce_rate_limits if rate_limit_reached?
|
73
66
|
response = connection.send(method, &block)
|
74
67
|
apply_rate_limits(response)
|
@@ -80,45 +73,47 @@ module BridgeAPI
|
|
80
73
|
config[:api_tokens].is_a?(Array) && config[:api_tokens].count >= 1
|
81
74
|
end
|
82
75
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
config[:token] ||= config[:api_tokens].first
|
76
|
+
def convert_tokens(config)
|
77
|
+
return config unless config.has_key?(:api_tokens)
|
78
|
+
return config unless config[:api_tokens].is_a?(Array)
|
79
|
+
config[:api_keys] ||= {}
|
80
|
+
config[:api_tokens].each do |token|
|
81
|
+
decoded_token_array = Base64.strict_decode64(token).split(':')
|
82
|
+
config[:api_keys][decoded_token_array[0]] = decoded_token_array[1]
|
91
83
|
end
|
84
|
+
config.delete(:api_tokens)
|
85
|
+
config
|
86
|
+
end
|
87
|
+
|
88
|
+
def initialize_from_token_pool(config)
|
89
|
+
config = convert_tokens(config)
|
90
|
+
creds = config[:api_keys].first
|
91
|
+
config[:api_key] ||= creds[0]
|
92
|
+
config[:api_secret] ||= creds[1]
|
92
93
|
config
|
93
94
|
end
|
94
95
|
|
95
96
|
# rotates to the next token in the pool (by order in which they were provided)
|
96
|
-
def rotate_token!
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
elsif config[:api_tokens].is_a?(Array)
|
107
|
-
keys = config[:api_tokens]
|
108
|
-
return if keys.count <= 1
|
109
|
-
|
110
|
-
token = get_next_key(keys, config[:token])
|
111
|
-
config[:token] = token
|
112
|
-
end
|
113
|
-
BridgeAPI.logger.debug('rotating API Keys')
|
114
|
-
set_connection(config)
|
115
|
-
end
|
97
|
+
def rotate_token!
|
98
|
+
return unless config[:api_keys].present?
|
99
|
+
old_api_key = config[:api_key]
|
100
|
+
keys = config[:api_keys].keys
|
101
|
+
return if keys.count <= 1
|
102
|
+
key = get_next_key(keys, config[:api_key])
|
103
|
+
config[:api_key] = key
|
104
|
+
config[:api_secret] = config[:api_keys][key]
|
105
|
+
set_connection(config)
|
106
|
+
BridgeAPI.logger.debug("ROTATED TO KEY: #{config[:api_key]}")
|
116
107
|
end
|
117
108
|
|
118
109
|
def get_next_key(keys, current_key)
|
119
|
-
|
120
|
-
|
121
|
-
|
110
|
+
keys.delete(current_key)
|
111
|
+
usable_key = keys.find do |key|
|
112
|
+
limit = rate_limit(key)
|
113
|
+
current_key_limit = limit.present? ? limit.fetch('current') : 0
|
114
|
+
BridgeAPI.beginning_rate_limit - current_key_limit > BridgeAPI.rate_limit_threshold
|
115
|
+
end
|
116
|
+
usable_key || keys[rand(keys.length)]
|
122
117
|
end
|
123
118
|
|
124
119
|
def rate_limit_reached?
|
@@ -130,14 +125,13 @@ module BridgeAPI
|
|
130
125
|
def enforce_rate_limits
|
131
126
|
return unless rate_limit_reached?
|
132
127
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
"
|
138
|
-
|
139
|
-
|
140
|
-
sleep(tts)
|
128
|
+
rotate_token!
|
129
|
+
if rate_limit_reached?
|
130
|
+
sleep_time = rand(BridgeAPI.max_sleep_seconds)
|
131
|
+
sleep_time = BridgeAPI.min_sleep_seconds if sleep_time < BridgeAPI.min_sleep_seconds
|
132
|
+
BridgeAPI.logger.debug("Rate limit reached sleeping for #{sleep_time}")
|
133
|
+
sleep(sleep_time)
|
134
|
+
end
|
141
135
|
end
|
142
136
|
|
143
137
|
def using_master_rate_limit?
|
@@ -148,20 +142,18 @@ module BridgeAPI
|
|
148
142
|
limit = response.headers['x-rate-limit-remaining']
|
149
143
|
return if limit.nil?
|
150
144
|
|
151
|
-
BridgeAPI.logger.debug("BRIDGE RATE LIMIT REMAINING: #{limit}")
|
145
|
+
BridgeAPI.logger.debug("BRIDGE RATE LIMIT REMAINING: #{limit} for key #{config[:api_key]}")
|
152
146
|
self.limit_remaining = limit.to_i
|
153
147
|
end
|
154
148
|
|
155
149
|
def limit_remaining
|
156
150
|
if using_master_rate_limit?
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
limit = { current: 0 }.with_indifferent_access
|
162
|
-
end
|
163
|
-
limit['current']
|
151
|
+
limit = rate_limit(config[:api_key])
|
152
|
+
if limit.nil?
|
153
|
+
set_rate_limit(config[:api_key], 0)
|
154
|
+
limit = { current: 0 }.with_indifferent_access
|
164
155
|
end
|
156
|
+
limit['current']
|
165
157
|
else
|
166
158
|
BridgeAPI.rate_limits[config[:api_key]]
|
167
159
|
end
|
@@ -169,12 +161,37 @@ module BridgeAPI
|
|
169
161
|
|
170
162
|
def limit_remaining=(value)
|
171
163
|
if using_master_rate_limit?
|
172
|
-
|
173
|
-
PaulWalker::RateLimit.add(config[:api_key], config[:api_key], value, BridgeAPI.beginning_rate_limit)
|
174
|
-
end
|
164
|
+
set_rate_limit(config[:api_key], value)
|
175
165
|
else
|
176
166
|
BridgeAPI.rate_limits[config[:api_key]] = value
|
177
167
|
end
|
168
|
+
refresh_stale_tokens
|
169
|
+
end
|
170
|
+
|
171
|
+
def set_rate_limit(key, limit)
|
172
|
+
BridgeAPI.master_mutex.synchronize do
|
173
|
+
PaulWalker::RateLimit.add(key, key, limit, BridgeAPI.beginning_rate_limit)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def rate_limit(key)
|
178
|
+
BridgeAPI.master_mutex.synchronize do
|
179
|
+
PaulWalker::RateLimit.get(key, key)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def refresh_stale_tokens
|
184
|
+
return unless using_master_rate_limit?
|
185
|
+
return unless config[:api_keys].present?
|
186
|
+
api_keys = config[:api_keys].keys
|
187
|
+
api_keys.delete(config[:api_key])
|
188
|
+
api_keys.each do |key|
|
189
|
+
limit = rate_limit(key)
|
190
|
+
if limit['timestamp'].present? && DateTime.parse(limit['timestamp']) < 1.minute.ago
|
191
|
+
BridgeAPI.logger.debug("Refreshing: #{key}")
|
192
|
+
set_rate_limit(key, 0)
|
193
|
+
end
|
194
|
+
end
|
178
195
|
end
|
179
196
|
|
180
197
|
def set_connection(config)
|
data/lib/bridge_api/version.rb
CHANGED
data/lib/bridge_api.rb
CHANGED
@@ -27,12 +27,6 @@ describe BridgeAPI::Client::Enrollment do
|
|
27
27
|
expect(enrollments.length).to(eq(2))
|
28
28
|
end
|
29
29
|
|
30
|
-
it 'should update due date for enrollments' do
|
31
|
-
enrollment = [{ end_at: nil }]
|
32
|
-
response = @client.update_enrollment_due_date(1, enrollment)
|
33
|
-
expect(response.status).to(eq(200))
|
34
|
-
end
|
35
|
-
|
36
30
|
it 'should reset an enrollment for a course template' do
|
37
31
|
response = @client.reset_enrollment(1)
|
38
32
|
expect(response.status).to(eq(204))
|
@@ -52,7 +52,7 @@ describe BridgeAPI::Client do
|
|
52
52
|
end
|
53
53
|
|
54
54
|
it 'should return true for tokens' do
|
55
|
-
config = { prefix: 'https://www.fake.com', api_tokens: %w[
|
55
|
+
config = { prefix: 'https://www.fake.com', api_tokens: %w[dGVzdDE6dGVzdDE= dGVzdDI6dGVzdDI=] }
|
56
56
|
client = BridgeAPI::Client.new(config)
|
57
57
|
expect(client.has_token_pool?(config)).to be_truthy
|
58
58
|
end
|
@@ -74,10 +74,10 @@ describe BridgeAPI::Client do
|
|
74
74
|
end
|
75
75
|
|
76
76
|
it 'should take first from tokens' do
|
77
|
-
config = { prefix: 'https://www.fake.com', api_tokens: %w[
|
77
|
+
config = { prefix: 'https://www.fake.com', api_tokens: %w[dGVzdDE6dGVzdDE= dGVzdDI6dGVzdDI=] }
|
78
78
|
client = BridgeAPI::Client.new(config)
|
79
79
|
new_config = client.initialize_from_token_pool(config)
|
80
|
-
expect(new_config[:
|
80
|
+
expect(new_config[:api_key]).to eq('test1')
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
@@ -111,15 +111,16 @@ describe BridgeAPI::Client do
|
|
111
111
|
|
112
112
|
context 'with token pool' do
|
113
113
|
it 'should rotate to the next key' do
|
114
|
-
keys = %w[
|
114
|
+
keys = %w[dGVzdDE6dGVzdDE= dGVzdDI6dGVzdDI=]
|
115
115
|
config = { prefix: 'https://www.fake.com', api_tokens: keys }
|
116
116
|
client = BridgeAPI::Client.new(config)
|
117
|
-
expect(client.config[:
|
117
|
+
expect(client.config[:api_key]).to eq('test1')
|
118
118
|
client.rotate_token!
|
119
|
-
expect(client.config[:
|
119
|
+
expect(client.config[:api_key]).to eq('test2')
|
120
120
|
client.rotate_token!
|
121
|
-
expect(client.config[:
|
121
|
+
expect(client.config[:api_key]).to eq('test1')
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
126
|
+
|
data/spec/support/fake_bridge.rb
CHANGED
metadata
CHANGED
@@ -1,35 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bridge_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.68
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jay Shaffer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.0.0
|
20
17
|
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '1.0'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.0.0
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.0.0
|
30
27
|
- - "~>"
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '1.0'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.0.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: byebug
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,22 +104,22 @@ dependencies:
|
|
104
104
|
name: tilt
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
|
-
- - "~>"
|
108
|
-
- !ruby/object:Gem::Version
|
109
|
-
version: '1.3'
|
110
107
|
- - ">="
|
111
108
|
- !ruby/object:Gem::Version
|
112
109
|
version: 1.3.4
|
110
|
+
- - "~>"
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '1.3'
|
113
113
|
type: :development
|
114
114
|
prerelease: false
|
115
115
|
version_requirements: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
|
-
- - "~>"
|
118
|
-
- !ruby/object:Gem::Version
|
119
|
-
version: '1.3'
|
120
117
|
- - ">="
|
121
118
|
- !ruby/object:Gem::Version
|
122
119
|
version: 1.3.4
|
120
|
+
- - "~>"
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '1.3'
|
123
123
|
- !ruby/object:Gem::Dependency
|
124
124
|
name: webmock
|
125
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -197,6 +197,7 @@ executables: []
|
|
197
197
|
extensions: []
|
198
198
|
extra_rdoc_files: []
|
199
199
|
files:
|
200
|
+
- ".github/workflows/ci.yml"
|
200
201
|
- ".gitignore"
|
201
202
|
- Dockerfile
|
202
203
|
- Gemfile
|
@@ -297,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
297
298
|
- !ruby/object:Gem::Version
|
298
299
|
version: '0'
|
299
300
|
requirements: []
|
300
|
-
rubygems_version: 3.
|
301
|
+
rubygems_version: 3.1.6
|
301
302
|
signing_key:
|
302
303
|
specification_version: 4
|
303
304
|
summary: Bridge API
|