itrp-client 1.1.4 → 1.1.5
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/Gemfile.lock +12 -11
- data/LICENSE.txt +1 -1
- data/README.md +3 -2
- data/lib/itrp/client.rb +29 -9
- data/lib/itrp/client/response.rb +4 -0
- data/lib/itrp/client/version.rb +1 -1
- data/spec/lib/itrp/client_spec.rb +36 -4
- data/spec/lib/itrp/response_spec.rb +4 -4
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8703ef24c263256f975b6bcc841ea70b2b9dd9fb
|
4
|
+
data.tar.gz: b7d384aab10d84b2b1cab2e33fff2975fbf69a40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 66205dde73494d6e6a95a45a316bf80522f5a61106735b315ad8d9636a8bd3d2fd6a375228476f42ad1b154c40933d3a6e24538afd20a1fea98e6ba46e2df40e
|
7
|
+
data.tar.gz: 213c2468c8be27d03b53f318a3b6aa2cf7e872a05be9042128b88db7bd1f90327a8d91170ac0c23227720a7fe99e51b35668cae647d924872dd32439a7c8d085
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
itrp-client (1.1.
|
4
|
+
itrp-client (1.1.5)
|
5
5
|
activesupport
|
6
6
|
gem_config
|
7
7
|
mime-types
|
@@ -9,26 +9,27 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (
|
13
|
-
|
14
|
-
|
12
|
+
activesupport (5.2.2)
|
13
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
|
+
i18n (>= 0.7, < 2)
|
15
15
|
minitest (~> 5.1)
|
16
|
-
thread_safe (~> 0.3, >= 0.3.4)
|
17
16
|
tzinfo (~> 1.1)
|
18
17
|
addressable (2.5.0)
|
19
18
|
public_suffix (~> 2.0, >= 2.0.2)
|
19
|
+
concurrent-ruby (1.1.4)
|
20
20
|
crack (0.4.3)
|
21
21
|
safe_yaml (~> 1.0.0)
|
22
22
|
diff-lcs (1.2.5)
|
23
23
|
docile (1.1.5)
|
24
24
|
gem_config (0.3.1)
|
25
25
|
hashdiff (0.3.1)
|
26
|
-
i18n (
|
26
|
+
i18n (1.5.1)
|
27
|
+
concurrent-ruby (~> 1.0)
|
27
28
|
json (1.8.3)
|
28
|
-
mime-types (3.
|
29
|
+
mime-types (3.2.2)
|
29
30
|
mime-types-data (~> 3.2015)
|
30
|
-
mime-types-data (3.
|
31
|
-
minitest (5.
|
31
|
+
mime-types-data (3.2018.0812)
|
32
|
+
minitest (5.11.3)
|
32
33
|
public_suffix (2.0.4)
|
33
34
|
rake (11.3.0)
|
34
35
|
rspec (3.3.0)
|
@@ -50,8 +51,8 @@ GEM
|
|
50
51
|
json (>= 1.8, < 3)
|
51
52
|
simplecov-html (~> 0.10.0)
|
52
53
|
simplecov-html (0.10.0)
|
53
|
-
thread_safe (0.3.
|
54
|
-
tzinfo (1.2.
|
54
|
+
thread_safe (0.3.6)
|
55
|
+
tzinfo (1.2.5)
|
55
56
|
thread_safe (~> 0.1)
|
56
57
|
webmock (2.1.0)
|
57
58
|
addressable (>= 2.3.6)
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -40,9 +40,10 @@ All options available:
|
|
40
40
|
* _max_retry_time_: maximum nr of seconds to wait for server to respond (default = 5400 = 1.5 hours)<br/>
|
41
41
|
The sleep time between retries starts at 2 seconds and doubles after each retry, i.e.
|
42
42
|
2, 6, 18, 54, 162, 486, 1458, 4374, 13122, ... seconds.<br/>
|
43
|
-
|
43
|
+
Set to 0 to prevent retries.
|
44
44
|
* _read_timeout_: [HTTP read timeout](http://ruby-doc.org/stdlib-2.0.0/libdoc/net/http/rdoc/Net/HTTP.html#method-i-read_timeout-3D) in seconds (default = 25)
|
45
|
-
* _block_at_rate_limit_: Set to `true` to block the request until the [rate limit](http://developer.itrp.com/v1/#rate-limiting) is lifted, default: `false
|
45
|
+
* _block_at_rate_limit_: Set to `true` to block the request until the [rate limit](http://developer.itrp.com/v1/#rate-limiting) is lifted, default: `false`<br/>
|
46
|
+
The `Retry-After` header is used to compute when the retry should be performed. If that moment is later than the _max_retry_time_ the request will not be blocked and the throttled response is returned.
|
46
47
|
* _proxy_host_: Define in case HTTP traffic needs to go through a proxy
|
47
48
|
* _proxy_port_: Port of the proxy, defaults to 8080
|
48
49
|
* _proxy_user_: Proxy user
|
data/lib/itrp/client.rb
CHANGED
@@ -298,14 +298,25 @@ module Itrp
|
|
298
298
|
end
|
299
299
|
|
300
300
|
module SendWithRateLimitBlock
|
301
|
-
# Wraps the _send method with retries when the server does not
|
301
|
+
# Wraps the _send method with retries when the server does not respond, see +initialize+ option +:rate_limit_block+
|
302
302
|
def _send(request, domain = @domain, port = @port, ssl = @ssl)
|
303
303
|
return super(request, domain, port, ssl) unless option(:block_at_rate_limit)
|
304
304
|
now = Time.now
|
305
|
+
timed_out = false
|
306
|
+
# respect the max_retry_time with fallback to max 1 hour and 1 minute wait time
|
307
|
+
max_retry_time = option(:max_retry_time) > 0 ? option(:max_retry_time) : 3660
|
305
308
|
begin
|
306
309
|
_response = super(request, domain, port, ssl)
|
307
|
-
|
308
|
-
|
310
|
+
if _response.throttled?
|
311
|
+
retry_after = _response.retry_after == 0 ? 300 : [_response.retry_after, 2].max
|
312
|
+
if (Time.now - now + retry_after) < max_retry_time
|
313
|
+
@logger.warn { "Request throttled, trying again in #{retry_after} seconds: #{_response.message}" }
|
314
|
+
sleep(retry_after)
|
315
|
+
else
|
316
|
+
timed_out = true
|
317
|
+
end
|
318
|
+
end
|
319
|
+
end while _response.throttled? && !timed_out
|
309
320
|
_response
|
310
321
|
end
|
311
322
|
end
|
@@ -314,15 +325,24 @@ module Itrp
|
|
314
325
|
module SendWithRetries
|
315
326
|
# Wraps the _send method with retries when the server does not respond, see +initialize+ option +:retries+
|
316
327
|
def _send(request, domain = @domain, port = @port, ssl = @ssl)
|
328
|
+
return super(request, domain, port, ssl) unless option(:max_retry_time) > 0
|
317
329
|
retries = 0
|
318
|
-
sleep_time =
|
319
|
-
|
330
|
+
sleep_time = 1
|
331
|
+
now = Time.now
|
332
|
+
timed_out = false
|
320
333
|
begin
|
321
334
|
_response = super(request, domain, port, ssl)
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
335
|
+
# throttling is handled separately
|
336
|
+
if !_response.success? && !_response.throttled?
|
337
|
+
sleep_time *= 2
|
338
|
+
if (Time.now - now + sleep_time) < option(:max_retry_time)
|
339
|
+
@logger.warn { "Request failed, retry ##{retries += 1} in #{sleep_time} seconds: #{_response.message}" }
|
340
|
+
sleep(sleep_time)
|
341
|
+
else
|
342
|
+
timed_out = true
|
343
|
+
end
|
344
|
+
end
|
345
|
+
end while !_response.success? && !_response.throttled? && !timed_out
|
326
346
|
_response
|
327
347
|
end
|
328
348
|
end
|
data/lib/itrp/client/response.rb
CHANGED
@@ -111,6 +111,10 @@ module Itrp
|
|
111
111
|
!!(@response.code.to_s == '429' || (message && message =~ /Too Many Requests/))
|
112
112
|
end
|
113
113
|
|
114
|
+
def retry_after
|
115
|
+
@current_page ||= @response.header['Retry-After'].to_i
|
116
|
+
end
|
117
|
+
|
114
118
|
def to_s
|
115
119
|
valid? ? json.to_s : message
|
116
120
|
end
|
data/lib/itrp/client/version.rb
CHANGED
@@ -281,8 +281,9 @@ describe Itrp::Client do
|
|
281
281
|
|
282
282
|
context 'import' do
|
283
283
|
before(:each) do
|
284
|
+
csv_mime_type = ['text/csv', 'text/comma-separated-values'].detect{|t| MIME::Types[t].any?} # which mime type is used depends on version of mime-types gem
|
284
285
|
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
285
|
-
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npeople\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/people.csv\"\r\nContent-Type:
|
286
|
+
@multi_part_body = "--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\npeople\r\n--0123456789ABLEWASIEREISAWELBA9876543210\r\nContent-Disposition: form-data; name=\"file\"; filename=\"#{@fixture_dir}/people.csv\"\r\nContent-Type: #{csv_mime_type}\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
|
286
287
|
@multi_part_headers = {'Accept'=>'*/*', 'Content-Type'=>'multipart/form-data; boundary=0123456789ABLEWASIEREISAWELBA9876543210', 'User-Agent'=>'Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en-us) AppleWebKit/523.10.6 (KHTML, like Gecko) Version/3.0.4 Safari/523.10.6'}
|
287
288
|
|
288
289
|
@import_queued_response = {body: {state: 'queued'}.to_json}
|
@@ -476,10 +477,11 @@ describe Itrp::Client do
|
|
476
477
|
|
477
478
|
it 'should not retry 4 times when max_retry_time = 16' do
|
478
479
|
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_raise(StandardError.new('network error'))
|
479
|
-
[2,4,8
|
480
|
+
[2,4,8].each_with_index do |secs, i|
|
480
481
|
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
481
482
|
expect_log("Request failed, retry ##{i+1} in #{secs} seconds: 500: No Response from Server - network error for 'api.itrp.com:443/v1/me'", :warn)
|
482
483
|
end
|
484
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
483
485
|
|
484
486
|
client = Itrp::Client.new(api_token: 'secret', max_retry_time: 16)
|
485
487
|
allow(client).to receive(:sleep)
|
@@ -518,10 +520,40 @@ describe Itrp::Client do
|
|
518
520
|
expect(response.message).to eq('429: Too Many Requests')
|
519
521
|
end
|
520
522
|
|
521
|
-
it 'should block on rate limit when block_at_rate_limit is true' do
|
523
|
+
it 'should block on rate limit (max 300 seconds) when block_at_rate_limit is true' do
|
522
524
|
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json).then.to_return(body: {name: 'my name'}.to_json)
|
523
525
|
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
524
|
-
expect_log('Request throttled, trying again in
|
526
|
+
expect_log('Request throttled, trying again in 300 seconds: 429: Too Many Requests', :warn)
|
527
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
528
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
529
|
+
|
530
|
+
client = Itrp::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
531
|
+
allow(client).to receive(:sleep)
|
532
|
+
response = client.get('me')
|
533
|
+
expect(stub).to have_been_requested.times(2)
|
534
|
+
expect(response.valid?).to be_truthy
|
535
|
+
expect(response[:name]).to eq('my name')
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'should block on rate limit using Retry-After when block_at_rate_limit is true' do
|
539
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json, headers: {'Retry-After' => '20'}).then.to_return(body: {name: 'my name'}.to_json)
|
540
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
541
|
+
expect_log('Request throttled, trying again in 20 seconds: 429: Too Many Requests', :warn)
|
542
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
543
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
544
|
+
|
545
|
+
client = Itrp::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
546
|
+
allow(client).to receive(:sleep)
|
547
|
+
response = client.get('me')
|
548
|
+
expect(stub).to have_been_requested.times(2)
|
549
|
+
expect(response.valid?).to be_truthy
|
550
|
+
expect(response[:name]).to eq('my name')
|
551
|
+
end
|
552
|
+
|
553
|
+
it 'should block on rate limit using Retry-After with minimum of 2 seconds when block_at_rate_limit is true' do
|
554
|
+
stub = stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json, headers: {'Retry-After' => '1'}).then.to_return(body: {name: 'my name'}.to_json)
|
555
|
+
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
556
|
+
expect_log('Request throttled, trying again in 2 seconds: 429: Too Many Requests', :warn)
|
525
557
|
expect_log('Sending GET request to api.itrp.com:443/v1/me', :debug )
|
526
558
|
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
527
559
|
|
@@ -93,10 +93,10 @@ describe Itrp::Response do
|
|
93
93
|
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(body: '==$$!invalid')
|
94
94
|
response = @client.get('organizations')
|
95
95
|
|
96
|
-
message = "
|
97
|
-
expect(response.json[:message]).to
|
98
|
-
expect(response.json['message']).to
|
99
|
-
expect(response.message).to
|
96
|
+
message = "unexpected token at '==$$!invalid' for:\n#{response.body}"
|
97
|
+
expect(response.json[:message]).to include(message)
|
98
|
+
expect(response.json['message']).to include(message)
|
99
|
+
expect(response.message).to include(message)
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'should have a blank message when single record is succesfully retrieved' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itrp-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ITRP
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem_config
|
@@ -171,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
171
171
|
version: '0'
|
172
172
|
requirements: []
|
173
173
|
rubyforge_project:
|
174
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.5.1
|
175
175
|
signing_key:
|
176
176
|
specification_version: 4
|
177
177
|
summary: Client for accessing the ITRP REST API
|