bridge_api 0.1.66 → 0.2.0
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 +1 -1
- data/Gemfile.lock +20 -17
- data/bridge_api.gemspec +2 -1
- 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/test_helper.rb +3 -0
- metadata +18 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f7777215ef9e217d636798e5de92127e20b1506f37d7846d34cc7d435cc4ffe
|
|
4
|
+
data.tar.gz: 17c5e2693f9b6db371058568776c8b0c6efb09d214f694a93a19e502b5223960
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8040ca103e5f27865337b03ceb8d761eff6b209f89c2405ec5bdcfaa842f8acc66cce2b365dfc9270b06e867c84d2dcc6a55dbd339314e8f87cf868d034860c6
|
|
7
|
+
data.tar.gz: cbb40a9001208af3be7056d95ad35696e682667acfcdb8dcd576a715099cca5091eb7725b3fdd85b386a896d34e6c01983e32351fa27f7d8b80fe87e9c934189
|
data/.github/workflows/ci.yml
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
bridge_api (0.
|
|
5
|
-
faraday (~> 0.
|
|
4
|
+
bridge_api (0.2.0)
|
|
5
|
+
faraday (~> 0.17.3)
|
|
6
6
|
faraday_middleware (>= 0.12.2)
|
|
7
7
|
footrest (>= 0.5.1)
|
|
8
8
|
paul_walker (>= 0.1.1)
|
|
@@ -10,35 +10,37 @@ PATH
|
|
|
10
10
|
GEM
|
|
11
11
|
remote: https://rubygems.org/
|
|
12
12
|
specs:
|
|
13
|
-
activesupport (
|
|
13
|
+
activesupport (7.0.3)
|
|
14
14
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
15
|
-
i18n (>=
|
|
16
|
-
minitest (
|
|
17
|
-
tzinfo (~>
|
|
15
|
+
i18n (>= 1.6, < 2)
|
|
16
|
+
minitest (>= 5.1)
|
|
17
|
+
tzinfo (~> 2.0)
|
|
18
18
|
addressable (2.5.2)
|
|
19
19
|
public_suffix (>= 2.0.2, < 4.0)
|
|
20
20
|
byebug (8.2.5)
|
|
21
21
|
coderay (1.1.2)
|
|
22
|
-
concurrent-ruby (1.1.
|
|
22
|
+
concurrent-ruby (1.1.10)
|
|
23
23
|
crack (0.4.3)
|
|
24
24
|
safe_yaml (~> 1.0.0)
|
|
25
25
|
diff-lcs (1.3)
|
|
26
|
-
faraday (0.
|
|
26
|
+
faraday (0.17.5)
|
|
27
27
|
multipart-post (>= 1.2, < 3)
|
|
28
|
-
faraday_middleware (0.
|
|
28
|
+
faraday_middleware (0.14.0)
|
|
29
29
|
faraday (>= 0.7.4, < 1.0)
|
|
30
30
|
footrest (0.5.3)
|
|
31
31
|
activesupport (>= 3.0.0)
|
|
32
32
|
faraday (>= 0.9.0, < 1)
|
|
33
33
|
link_header (>= 0.0.7)
|
|
34
34
|
hashdiff (0.3.7)
|
|
35
|
-
i18n (1.
|
|
35
|
+
i18n (1.10.0)
|
|
36
36
|
concurrent-ruby (~> 1.0)
|
|
37
37
|
link_header (0.0.8)
|
|
38
38
|
method_source (0.9.0)
|
|
39
|
-
minitest (5.
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
minitest (5.15.0)
|
|
40
|
+
mock_redis (0.31.0)
|
|
41
|
+
ruby2_keywords
|
|
42
|
+
multipart-post (2.1.1)
|
|
43
|
+
paul_walker (0.1.2)
|
|
42
44
|
redis (>= 4.0)
|
|
43
45
|
pry (0.11.3)
|
|
44
46
|
coderay (~> 1.1.0)
|
|
@@ -48,7 +50,7 @@ GEM
|
|
|
48
50
|
rack-protection (1.5.5)
|
|
49
51
|
rack
|
|
50
52
|
rake (0.9.6)
|
|
51
|
-
redis (4.
|
|
53
|
+
redis (4.6.0)
|
|
52
54
|
rspec (2.99.0)
|
|
53
55
|
rspec-core (~> 2.99.0)
|
|
54
56
|
rspec-expectations (~> 2.99.0)
|
|
@@ -57,15 +59,15 @@ GEM
|
|
|
57
59
|
rspec-expectations (2.99.2)
|
|
58
60
|
diff-lcs (>= 1.1.3, < 2.0)
|
|
59
61
|
rspec-mocks (2.99.4)
|
|
62
|
+
ruby2_keywords (0.0.5)
|
|
60
63
|
safe_yaml (1.0.4)
|
|
61
64
|
sinatra (1.4.8)
|
|
62
65
|
rack (~> 1.5)
|
|
63
66
|
rack-protection (~> 1.4)
|
|
64
67
|
tilt (>= 1.3, < 3)
|
|
65
|
-
thread_safe (0.3.6)
|
|
66
68
|
tilt (1.4.1)
|
|
67
|
-
tzinfo (
|
|
68
|
-
|
|
69
|
+
tzinfo (2.0.4)
|
|
70
|
+
concurrent-ruby (~> 1.0)
|
|
69
71
|
webmock (1.22.6)
|
|
70
72
|
addressable (>= 2.3.6)
|
|
71
73
|
crack (>= 0.3.2)
|
|
@@ -78,6 +80,7 @@ DEPENDENCIES
|
|
|
78
80
|
bridge_api!
|
|
79
81
|
bundler (~> 1.0, >= 1.0.0)
|
|
80
82
|
byebug (~> 8.2.2)
|
|
83
|
+
mock_redis
|
|
81
84
|
pry (~> 0)
|
|
82
85
|
rake (~> 0)
|
|
83
86
|
rspec (~> 2.6)
|
data/bridge_api.gemspec
CHANGED
|
@@ -26,8 +26,9 @@ Gem::Specification.new do |gem|
|
|
|
26
26
|
gem.add_development_dependency 'sinatra', '~> 1.0'
|
|
27
27
|
gem.add_development_dependency 'tilt', '>= 1.3.4', '~> 1.3'
|
|
28
28
|
gem.add_development_dependency 'webmock', '~>1.22.6'
|
|
29
|
+
gem.add_development_dependency 'mock_redis'
|
|
29
30
|
|
|
30
|
-
gem.add_dependency 'faraday', '~> 0.
|
|
31
|
+
gem.add_dependency 'faraday', '~> 0.17.3'
|
|
31
32
|
gem.add_dependency 'faraday_middleware', '>= 0.12.2'
|
|
32
33
|
gem.add_dependency 'footrest', '>= 0.5.1'
|
|
33
34
|
gem.add_dependency 'paul_walker', '>= 0.1.1'
|
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/test_helper.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'rspec'
|
|
|
5
5
|
require 'webmock/rspec'
|
|
6
6
|
require 'json'
|
|
7
7
|
require 'pry'
|
|
8
|
+
require 'mock_redis'
|
|
8
9
|
|
|
9
10
|
RSpec.configure do |config|
|
|
10
11
|
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
|
@@ -12,6 +13,8 @@ RSpec.configure do |config|
|
|
|
12
13
|
config.before(:each) do
|
|
13
14
|
WebMock.disable_net_connect!
|
|
14
15
|
WebMock.stub_request(:any, %r{api/.*}).to_rack(FakeBridge)
|
|
16
|
+
$redis = MockRedis.new
|
|
17
|
+
PaulWalker.redis = $redis
|
|
15
18
|
end
|
|
16
19
|
end
|
|
17
20
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bridge_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jay Shaffer
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-05-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -134,20 +134,34 @@ dependencies:
|
|
|
134
134
|
- - "~>"
|
|
135
135
|
- !ruby/object:Gem::Version
|
|
136
136
|
version: 1.22.6
|
|
137
|
+
- !ruby/object:Gem::Dependency
|
|
138
|
+
name: mock_redis
|
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
type: :development
|
|
145
|
+
prerelease: false
|
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
147
|
+
requirements:
|
|
148
|
+
- - ">="
|
|
149
|
+
- !ruby/object:Gem::Version
|
|
150
|
+
version: '0'
|
|
137
151
|
- !ruby/object:Gem::Dependency
|
|
138
152
|
name: faraday
|
|
139
153
|
requirement: !ruby/object:Gem::Requirement
|
|
140
154
|
requirements:
|
|
141
155
|
- - "~>"
|
|
142
156
|
- !ruby/object:Gem::Version
|
|
143
|
-
version: 0.
|
|
157
|
+
version: 0.17.3
|
|
144
158
|
type: :runtime
|
|
145
159
|
prerelease: false
|
|
146
160
|
version_requirements: !ruby/object:Gem::Requirement
|
|
147
161
|
requirements:
|
|
148
162
|
- - "~>"
|
|
149
163
|
- !ruby/object:Gem::Version
|
|
150
|
-
version: 0.
|
|
164
|
+
version: 0.17.3
|
|
151
165
|
- !ruby/object:Gem::Dependency
|
|
152
166
|
name: faraday_middleware
|
|
153
167
|
requirement: !ruby/object:Gem::Requirement
|