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 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