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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3191ba404199a9c372100cfbaf4d098b4bf421ea
4
- data.tar.gz: 6af16dbe18fe503f3186cb8ed1110bf8e00b518c
3
+ metadata.gz: 8703ef24c263256f975b6bcc841ea70b2b9dd9fb
4
+ data.tar.gz: b7d384aab10d84b2b1cab2e33fff2975fbf69a40
5
5
  SHA512:
6
- metadata.gz: 722a14bab6be83a23c483d2dece405bf680901dbdff3f7389211e7dcea9f6af2028cdc0f07510e091f045ce8909fa7fbb432e486436307e10481ad96150a583e
7
- data.tar.gz: fb8f9090c5ce0c3da2c81e4d8431521a132f448c94a493ebed8ff459f527eecf0b5173341ad5e36a949f2f5fe3438d170d5f5790c0c7d048905cbbbb64bd029a
6
+ metadata.gz: 66205dde73494d6e6a95a45a316bf80522f5a61106735b315ad8d9636a8bd3d2fd6a375228476f42ad1b154c40933d3a6e24538afd20a1fea98e6ba46e2df40e
7
+ data.tar.gz: 213c2468c8be27d03b53f318a3b6aa2cf7e872a05be9042128b88db7bd1f90327a8d91170ac0c23227720a7fe99e51b35668cae647d924872dd32439a7c8d085
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- itrp-client (1.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 (4.2.7.1)
13
- i18n (~> 0.7)
14
- json (~> 1.7, >= 1.7.7)
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 (0.7.0)
26
+ i18n (1.5.1)
27
+ concurrent-ruby (~> 1.0)
27
28
  json (1.8.3)
28
- mime-types (3.1)
29
+ mime-types (3.2.2)
29
30
  mime-types-data (~> 3.2015)
30
- mime-types-data (3.2016.0521)
31
- minitest (5.9.1)
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.5)
54
- tzinfo (1.2.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)
@@ -1,4 +1,4 @@
1
- Copyright (c) 2017 ITRP Inc.
1
+ Copyright (c) 2019 ITRP Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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
- One retry will always be performed unless you set the value to -1.
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
@@ -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 responsd, see +initialize+ option +:rate_limit_block+
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
- @logger.warn { "Request throttled, trying again in 5 minutes: #{_response.message}" } and sleep(300) if _response.throttled?
308
- end while _response.throttled? && (Time.now - now) < 3660 # max 1 hour and 1 minute
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 = 2
319
- total_retry_time = 0
330
+ sleep_time = 1
331
+ now = Time.now
332
+ timed_out = false
320
333
  begin
321
334
  _response = super(request, domain, port, ssl)
322
- @logger.warn { "Request failed, retry ##{retries += 1} in #{sleep_time} seconds: #{_response.message}" } and sleep(sleep_time) if (_response.raw.code.to_s != '204' && _response.empty?) && option(:max_retry_time) > 0
323
- total_retry_time += sleep_time
324
- sleep_time *= 2
325
- end while (_response.raw.code.to_s != '204' && _response.empty?) && total_retry_time < option(:max_retry_time)
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
@@ -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
@@ -1,5 +1,5 @@
1
1
  module Itrp
2
2
  class Client
3
- VERSION = '1.1.4'
3
+ VERSION = '1.1.5'
4
4
  end
5
5
  end
@@ -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: text/comma-separated-values\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
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,16].each_with_index do |secs, i|
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 5 minutes: 429: Too Many Requests', :warn)
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 = "Invalid JSON - 757: unexpected token at '==$$!invalid' for:\n#{response.body}"
97
- expect(response.json[:message]).to eq(message)
98
- expect(response.json['message']).to eq(message)
99
- expect(response.message).to eq(message)
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
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: 2018-11-21 00:00:00.000000000 Z
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.4.6
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