4me-sdk 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 +4 -4
- data/LICENSE +1 -1
- data/README.md +3 -2
- data/lib/sdk4me/client.rb +29 -9
- data/lib/sdk4me/client/response.rb +4 -0
- data/lib/sdk4me/client/version.rb +1 -1
- data/spec/lib/sdk4me/client_spec.rb +35 -3
- data/spec/lib/sdk4me/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: 5709d9ce126fa6349fdff96ab4a2dd1c80b8653a
|
|
4
|
+
data.tar.gz: 56ea4d39c7dce6e5c0578f0eb5ab1bbeb154622d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 754794a8253b590ddfd4064d2d96e9bae96597ab89c0169c6ebdb7fc8db4dd9035e7a0770be6f6644cff362a9f0dc5e061cb3992e15e6502556921bbdd997c5a
|
|
7
|
+
data.tar.gz: 80eaad3b45968a8ca3e1ce3afa4a6750c74105ba188122e1bcaf800ea87c64ab63c88893d70ee32793bf8bc19b22a38f5cec830935c153a03ccb978c83fb1607
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
4me-sdk (1.1.
|
|
4
|
+
4me-sdk (1.1.5)
|
|
5
5
|
activesupport (>= 4.2)
|
|
6
6
|
gem_config (>= 0.3)
|
|
7
7
|
mime-types (>= 3.0)
|
|
@@ -9,21 +9,21 @@ PATH
|
|
|
9
9
|
GEM
|
|
10
10
|
remote: https://rubygems.org/
|
|
11
11
|
specs:
|
|
12
|
-
activesupport (5.2.
|
|
12
|
+
activesupport (5.2.2)
|
|
13
13
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
14
14
|
i18n (>= 0.7, < 2)
|
|
15
15
|
minitest (~> 5.1)
|
|
16
16
|
tzinfo (~> 1.1)
|
|
17
17
|
addressable (2.5.2)
|
|
18
18
|
public_suffix (>= 2.0.2, < 4.0)
|
|
19
|
-
concurrent-ruby (1.1.
|
|
19
|
+
concurrent-ruby (1.1.4)
|
|
20
20
|
crack (0.4.3)
|
|
21
21
|
safe_yaml (~> 1.0.0)
|
|
22
22
|
diff-lcs (1.3)
|
|
23
23
|
docile (1.3.1)
|
|
24
24
|
gem_config (0.3.1)
|
|
25
25
|
hashdiff (0.3.7)
|
|
26
|
-
i18n (1.
|
|
26
|
+
i18n (1.5.1)
|
|
27
27
|
concurrent-ruby (~> 1.0)
|
|
28
28
|
json (2.1.0)
|
|
29
29
|
mime-types (3.2.2)
|
data/LICENSE
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.4me.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.4me.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/sdk4me/client.rb
CHANGED
|
@@ -298,14 +298,25 @@ module Sdk4me
|
|
|
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 Sdk4me
|
|
|
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
|
|
@@ -111,6 +111,10 @@ module Sdk4me
|
|
|
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
|
|
@@ -282,7 +282,8 @@ describe Sdk4me::Client do
|
|
|
282
282
|
context 'import' do
|
|
283
283
|
before(:each) do
|
|
284
284
|
@client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: -1)
|
|
285
|
-
|
|
285
|
+
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
|
|
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}
|
|
@@ -477,10 +478,11 @@ describe Sdk4me::Client do
|
|
|
477
478
|
|
|
478
479
|
it 'should not retry 4 times when max_retry_time = 16' do
|
|
479
480
|
stub = stub_request(:get, 'https://api.4me.com/v1/me').with(basic_auth: ['secret', 'x']).to_raise(StandardError.new('network error'))
|
|
480
|
-
[2,4,8
|
|
481
|
+
[2,4,8].each_with_index do |secs, i|
|
|
481
482
|
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
482
483
|
expect_log("Request failed, retry ##{i+1} in #{secs} seconds: 500: No Response from Server - network error for 'api.4me.com:443/v1/me'", :warn)
|
|
483
484
|
end
|
|
485
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
484
486
|
|
|
485
487
|
client = Sdk4me::Client.new(api_token: 'secret', max_retry_time: 16)
|
|
486
488
|
allow(client).to receive(:sleep)
|
|
@@ -522,7 +524,37 @@ describe Sdk4me::Client do
|
|
|
522
524
|
it 'should block on rate limit when block_at_rate_limit is true' do
|
|
523
525
|
stub = stub_request(:get, 'https://api.4me.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)
|
|
524
526
|
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
525
|
-
expect_log('Request throttled, trying again in
|
|
527
|
+
expect_log('Request throttled, trying again in 300 seconds: 429: Too Many Requests', :warn)
|
|
528
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
529
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
|
530
|
+
|
|
531
|
+
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
|
532
|
+
allow(client).to receive(:sleep)
|
|
533
|
+
response = client.get('me')
|
|
534
|
+
expect(stub).to have_been_requested.times(2)
|
|
535
|
+
expect(response.valid?).to be_truthy
|
|
536
|
+
expect(response[:name]).to eq('my name')
|
|
537
|
+
end
|
|
538
|
+
|
|
539
|
+
it 'should block on rate limit using Retry-After when block_at_rate_limit is true' do
|
|
540
|
+
stub = stub_request(:get, 'https://api.4me.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)
|
|
541
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
542
|
+
expect_log('Request throttled, trying again in 20 seconds: 429: Too Many Requests', :warn)
|
|
543
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
544
|
+
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
|
545
|
+
|
|
546
|
+
client = Sdk4me::Client.new(api_token: 'secret', block_at_rate_limit: true)
|
|
547
|
+
allow(client).to receive(:sleep)
|
|
548
|
+
response = client.get('me')
|
|
549
|
+
expect(stub).to have_been_requested.times(2)
|
|
550
|
+
expect(response.valid?).to be_truthy
|
|
551
|
+
expect(response[:name]).to eq('my name')
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
it 'should block on rate limit using Retry-After with minimum of 2 seconds when block_at_rate_limit is true' do
|
|
555
|
+
stub = stub_request(:get, 'https://api.4me.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)
|
|
556
|
+
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
557
|
+
expect_log('Request throttled, trying again in 2 seconds: 429: Too Many Requests', :warn)
|
|
526
558
|
expect_log('Sending GET request to api.4me.com:443/v1/me', :debug )
|
|
527
559
|
expect_log(%(Response:\n{\n "name": "my name"\n}), :debug )
|
|
528
560
|
|
|
@@ -93,10 +93,10 @@ describe Sdk4me::Response do
|
|
|
93
93
|
stub_request(:get, 'https://api.4me.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: 4me-sdk
|
|
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
|
- 4me
|
|
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.6
|
|
174
|
+
rubygems_version: 2.4.6
|
|
175
175
|
signing_key:
|
|
176
176
|
specification_version: 4
|
|
177
177
|
summary: The official 4me SDK for Ruby. Provides easy access to the APIs found at
|