4me-sdk 1.1.4 → 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 864b07c2b953a22cde7b603d2ca40e0c922af5ab
4
- data.tar.gz: 191f083e0183d044f77c9872a3b28f6e3541bf24
3
+ metadata.gz: 5709d9ce126fa6349fdff96ab4a2dd1c80b8653a
4
+ data.tar.gz: 56ea4d39c7dce6e5c0578f0eb5ab1bbeb154622d
5
5
  SHA512:
6
- metadata.gz: 161e613f6c9e5af777051f09aad8c83cd4ade36395550e0c4da63c8d7ee4a399f161aadbd0c3ee965db44864c2faa3ed4f336017b98e85b9196c6a0ee8844d13
7
- data.tar.gz: 348aa6bb7536b81c6cf0642e0b7376c3aea935d88dc6743af82cdf563b023d92f82773f72ef500c44458a448eb96b0027bce74d9e4b79f7f9c2901f3483bb013
6
+ metadata.gz: 754794a8253b590ddfd4064d2d96e9bae96597ab89c0169c6ebdb7fc8db4dd9035e7a0770be6f6644cff362a9f0dc5e061cb3992e15e6502556921bbdd997c5a
7
+ data.tar.gz: 80eaad3b45968a8ca3e1ce3afa4a6750c74105ba188122e1bcaf800ea87c64ab63c88893d70ee32793bf8bc19b22a38f5cec830935c153a03ccb978c83fb1607
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- 4me-sdk (1.1.4)
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.1)
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.3)
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.1.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
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2018 https://code.4me.com
3
+ Copyright (c) 2019 https://code.4me.com
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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.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
@@ -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 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 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 = 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 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
@@ -1,5 +1,5 @@
1
1
  module Sdk4me
2
2
  class Client
3
- VERSION = '1.1.4'
3
+ VERSION = '1.1.5'
4
4
  end
5
5
  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
- @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/csv\r\n\r\nPrimary Email,Name\nchess.cole@example.com,Chess Cole\ned.turner@example.com,Ed Turner\r\n--0123456789ABLEWASIEREISAWELBA9876543210--"
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,16].each_with_index do |secs, i|
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 5 minutes: 429: Too Many Requests', :warn)
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 = "Invalid JSON - 765: 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: 4me-sdk
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
  - 4me
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.6.14
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