bridge_api 0.1.63 → 0.1.68
Sign up to get free protection for your applications and to get access to all the features.
- 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
|