lhc 12.0.3 → 12.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +41 -14
- data/lib/lhc/interceptors/auth.rb +10 -8
- data/lib/lhc/interceptors/throttle.rb +26 -22
- data/lib/lhc/response/data.rb +2 -2
- data/lib/lhc/response/data/base.rb +2 -6
- data/lib/lhc/version.rb +1 -1
- data/spec/interceptors/auth/body_spec.rb +36 -0
- data/spec/interceptors/auth/no_instance_var_for_options_spec.rb +27 -0
- data/spec/interceptors/throttle/main_spec.rb +91 -35
- data/spec/response/data_spec.rb +24 -0
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a536d04e90db04fdeca6edd06c3e88782106a660d410a1b908b94147a3e9154
|
4
|
+
data.tar.gz: d0d89904a00069fdd8f3ef297f296e5da9d1383a4b4239aeebf00b6869be082a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 858291c8b53ce6447df1cc23fabd7c74ca4307ff7590eaebb76f4d538b769f81d9d89055bf79bf564814cf55c6832fccee42918100433061331252c6aafd8042
|
7
|
+
data.tar.gz: 49356178736094b85a4d9c1eab8dcf390024df5b580cb696e7f02683cbc1f1cf4a3e5bcfc6e03f5e8b9330fed1e02b94dd8bbbe17ec3c58e14b91b7352df9030
|
data/README.md
CHANGED
@@ -58,6 +58,7 @@ use it like:
|
|
58
58
|
* [Authentication Interceptor](#authentication-interceptor)
|
59
59
|
* [Bearer Authentication](#bearer-authentication)
|
60
60
|
* [Basic Authentication](#basic-authentication)
|
61
|
+
* [Body Authentication](#body-authentication)
|
61
62
|
* [Reauthenticate](#reauthenticate)
|
62
63
|
* [Bearer Authentication with client access token](#bearer-authentication-with-client-access-token)
|
63
64
|
* [Caching Interceptor](#caching-interceptor)
|
@@ -92,6 +93,8 @@ use it like:
|
|
92
93
|
|
93
94
|
|
94
95
|
|
96
|
+
|
97
|
+
|
95
98
|
## Basic methods
|
96
99
|
|
97
100
|
Available are `get`, `post`, `put` & `delete`.
|
@@ -261,7 +264,7 @@ You can also use URL templates, when [configuring endpoints](#configuring-endpoi
|
|
261
264
|
LHC.configure do |c|
|
262
265
|
c.endpoint(:find_feedback, 'http://datastore/v2/feedbacks/{id}')
|
263
266
|
end
|
264
|
-
|
267
|
+
|
265
268
|
LHC.get(:find_feedback, params:{ id: 123 }) # GET http://datastore/v2/feedbacks/123
|
266
269
|
```
|
267
270
|
|
@@ -274,7 +277,7 @@ Working and configuring timeouts is important, to ensure your app stays alive wh
|
|
274
277
|
LHC forwards two timeout options directly to typhoeus:
|
275
278
|
|
276
279
|
`timeout` (in seconds) - The maximum time in seconds that you allow the libcurl transfer operation to take. Normally, name lookups can take a considerable time and limiting operations to less than a few seconds risk aborting perfectly normal operations. This option may cause libcurl to use the SIGALRM signal to timeout system calls.
|
277
|
-
`connecttimeout` (in seconds) - It should contain the maximum time in seconds that you allow the connection phase to the server to take. This only limits the connection phase, it has no impact once it has connected. Set to zero to switch to the default built-in connection timeout - 300 seconds.
|
280
|
+
`connecttimeout` (in seconds) - It should contain the maximum time in seconds that you allow the connection phase to the server to take. This only limits the connection phase, it has no impact once it has connected. Set to zero to switch to the default built-in connection timeout - 300 seconds.
|
278
281
|
|
279
282
|
```ruby
|
280
283
|
LHC.get('http://local.ch', timeout: 5, connecttimeout: 1)
|
@@ -479,7 +482,7 @@ You can configure global placeholders, that are used when generating urls from u
|
|
479
482
|
c.placeholder(:datastore, 'http://datastore')
|
480
483
|
c.endpoint(:feedbacks, '{+datastore}/feedbacks', { params: { has_reviews: true } })
|
481
484
|
end
|
482
|
-
|
485
|
+
|
483
486
|
LHC.get(:feedbacks) # http://datastore/v2/feedbacks
|
484
487
|
```
|
485
488
|
|
@@ -549,6 +552,20 @@ Adds the following header to the request:
|
|
549
552
|
|
550
553
|
Which is the base64 encoded credentials "username:password".
|
551
554
|
|
555
|
+
##### Body Authentication
|
556
|
+
|
557
|
+
```ruby
|
558
|
+
LHC.post('http://local.ch', auth: { body: { userToken: 'dheur5hrk3' } })
|
559
|
+
```
|
560
|
+
|
561
|
+
Adds the following to body of all requests:
|
562
|
+
|
563
|
+
```
|
564
|
+
{
|
565
|
+
"userToken": "dheur5hrk3"
|
566
|
+
}
|
567
|
+
```
|
568
|
+
|
552
569
|
##### Reauthenticate
|
553
570
|
|
554
571
|
The current implementation can only offer reauthenticate for _client access tokens_. For this to work the following has to be given:
|
@@ -713,7 +730,7 @@ LHC::Monitoring.env = ENV['DEPLOYMENT_TYPE'] || Rails.env
|
|
713
730
|
|
714
731
|
It tracks request attempts with `before_request` and `after_request` (counts).
|
715
732
|
|
716
|
-
In case your workers/processes are getting killed due limited time constraints,
|
733
|
+
In case your workers/processes are getting killed due limited time constraints,
|
717
734
|
you are able to detect deltas with relying on "before_request", and "after_request" counts:
|
718
735
|
|
719
736
|
```ruby
|
@@ -764,7 +781,7 @@ Logs basic request/response information to prometheus.
|
|
764
781
|
LHC.configure do |c|
|
765
782
|
c.interceptors = [LHC::Prometheus]
|
766
783
|
end
|
767
|
-
|
784
|
+
|
768
785
|
LHC::Prometheus.client = Prometheus::Client
|
769
786
|
LHC::Prometheus.namespace = 'web_location_app'
|
770
787
|
```
|
@@ -786,7 +803,7 @@ If you enable the retry interceptor, you can have LHC retry requests for you:
|
|
786
803
|
LHC.configure do |c|
|
787
804
|
c.interceptors = [LHC::Retry]
|
788
805
|
end
|
789
|
-
|
806
|
+
|
790
807
|
response = LHC.get('http://local.ch', retry: true)
|
791
808
|
```
|
792
809
|
|
@@ -861,15 +878,15 @@ The throttle interceptor allows you to raise an exception if a predefined quota
|
|
861
878
|
end
|
862
879
|
```
|
863
880
|
```ruby
|
864
|
-
options = {
|
881
|
+
options = {
|
865
882
|
throttle: {
|
866
|
-
track: true,
|
867
|
-
break: '80%',
|
868
|
-
provider: 'local.ch',
|
869
|
-
limit: { header: 'Rate-Limit-Limit' },
|
870
|
-
remaining: { header: 'Rate-Limit-Remaining' },
|
871
|
-
expires: { header: 'Rate-Limit-Reset' }
|
872
|
-
}
|
883
|
+
track: true,
|
884
|
+
break: '80%',
|
885
|
+
provider: 'local.ch',
|
886
|
+
limit: { header: 'Rate-Limit-Limit' },
|
887
|
+
remaining: { header: 'Rate-Limit-Remaining' },
|
888
|
+
expires: { header: 'Rate-Limit-Reset' }
|
889
|
+
}
|
873
890
|
}
|
874
891
|
|
875
892
|
LHC.get('http://local.ch', options)
|
@@ -878,6 +895,16 @@ LHC.get('http://local.ch', options)
|
|
878
895
|
LHC.get('http://local.ch', options)
|
879
896
|
# raises LHC::Throttle::OutOfQuota: Reached predefined quota for local.ch
|
880
897
|
```
|
898
|
+
**Options Description**
|
899
|
+
* `track`: enables tracking of current limit/remaining requests of rate-limiting
|
900
|
+
* `break`: quota in percent after which errors are raised. Percentage symbol is optional, values will be converted to integer (e.g. '23.5' will become 23)
|
901
|
+
* `provider`: name of the provider under which throttling tracking is aggregated,
|
902
|
+
* `limit`: either a hard-coded integer, or a hash pointing at the response header containing the limit value
|
903
|
+
* `remaining`:
|
904
|
+
* a hash pointing at the response header containing the current amount of remaining requests
|
905
|
+
* a proc that receives the response as argument and returns the current amount
|
906
|
+
of remaining requests
|
907
|
+
* `expires`: a hash pointing at the response header containing the timestamp when the quota will reset
|
881
908
|
|
882
909
|
#### Zipkin
|
883
910
|
|
@@ -4,8 +4,13 @@ class LHC::Auth < LHC::Interceptor
|
|
4
4
|
include ActiveSupport::Configurable
|
5
5
|
config_accessor :refresh_client_token
|
6
6
|
|
7
|
+
def before_raw_request
|
8
|
+
body_authentication! if auth_options[:body]
|
9
|
+
end
|
10
|
+
|
7
11
|
def before_request
|
8
|
-
|
12
|
+
bearer_authentication! if auth_options[:bearer]
|
13
|
+
basic_authentication! if auth_options[:basic]
|
9
14
|
end
|
10
15
|
|
11
16
|
def after_response
|
@@ -16,12 +21,9 @@ class LHC::Auth < LHC::Interceptor
|
|
16
21
|
|
17
22
|
private
|
18
23
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
elsif auth_options[:basic]
|
23
|
-
basic_authentication!
|
24
|
-
end
|
24
|
+
def body_authentication!
|
25
|
+
auth = auth_options[:body]
|
26
|
+
request.options[:body] = (request.options[:body] || {}).merge(auth)
|
25
27
|
end
|
26
28
|
|
27
29
|
def basic_authentication!
|
@@ -78,7 +80,7 @@ class LHC::Auth < LHC::Interceptor
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def auth_options
|
81
|
-
|
83
|
+
request.options[:auth] || {}
|
82
84
|
end
|
83
85
|
|
84
86
|
def configuration_correct?
|
@@ -21,8 +21,7 @@ class LHC::Throttle < LHC::Interceptor
|
|
21
21
|
|
22
22
|
def after_response
|
23
23
|
options = response.request.options.dig(:throttle)
|
24
|
-
return unless options
|
25
|
-
return unless options.dig(:track)
|
24
|
+
return unless throttle?(options)
|
26
25
|
self.class.track ||= {}
|
27
26
|
self.class.track[options.dig(:provider)] = {
|
28
27
|
limit: limit(options: options[:limit], response: response),
|
@@ -33,6 +32,10 @@ class LHC::Throttle < LHC::Interceptor
|
|
33
32
|
|
34
33
|
private
|
35
34
|
|
35
|
+
def throttle?(options)
|
36
|
+
[options&.dig(:track), response.headers].none?(&:blank?)
|
37
|
+
end
|
38
|
+
|
36
39
|
def break_when_quota_reached!
|
37
40
|
options = request.options.dig(:throttle)
|
38
41
|
track = (self.class.track || {}).dig(options[:provider])
|
@@ -46,36 +49,37 @@ class LHC::Throttle < LHC::Interceptor
|
|
46
49
|
end
|
47
50
|
|
48
51
|
def limit(options:, response:)
|
49
|
-
@limit ||=
|
50
|
-
|
51
|
-
options
|
52
|
-
|
53
|
-
|
52
|
+
@limit ||=
|
53
|
+
begin
|
54
|
+
if options.is_a?(Integer)
|
55
|
+
options
|
56
|
+
elsif options.is_a?(Hash) && options[:header]
|
57
|
+
response.headers[options[:header]]&.to_i
|
58
|
+
end
|
54
59
|
end
|
55
|
-
end
|
56
60
|
end
|
57
61
|
|
58
62
|
def remaining(options:, response:)
|
59
|
-
@remaining ||=
|
60
|
-
|
61
|
-
|
63
|
+
@remaining ||=
|
64
|
+
begin
|
65
|
+
if options.is_a?(Proc)
|
66
|
+
options.call(response)
|
67
|
+
elsif options.is_a?(Hash) && options[:header]
|
68
|
+
response.headers[options[:header]]&.to_i
|
69
|
+
end
|
62
70
|
end
|
63
|
-
end
|
64
71
|
end
|
65
72
|
|
66
73
|
def expires(options:, response:)
|
67
|
-
@expires ||=
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
73
|
-
end
|
74
|
+
@expires ||= convert_expires(read_expire_option(options, response))
|
75
|
+
end
|
76
|
+
|
77
|
+
def read_expire_option(options, response)
|
78
|
+
(options.is_a?(Hash) && options[:header]) ? response.headers[options[:header]] : options
|
74
79
|
end
|
75
80
|
|
76
81
|
def convert_expires(value)
|
77
|
-
if value.
|
78
|
-
|
79
|
-
end
|
82
|
+
return Time.parse(value) if value.match(/GMT/)
|
83
|
+
Time.zone.at(value.to_i).to_datetime if value.present?
|
80
84
|
end
|
81
85
|
end
|
data/lib/lhc/response/data.rb
CHANGED
@@ -12,9 +12,9 @@ class LHC::Response::Data
|
|
12
12
|
@data = data
|
13
13
|
|
14
14
|
if as_json.is_a?(Hash)
|
15
|
-
@base = LHC::Response::Data::Item.new(response, data: data)
|
15
|
+
@base = LHC::Response::Data::Item.new(@response, data: data)
|
16
16
|
elsif as_json.is_a?(Array)
|
17
|
-
@base = LHC::Response::Data::Collection.new(response, data: data)
|
17
|
+
@base = LHC::Response::Data::Collection.new(@response, data: data)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# but made accssible in the ruby world
|
5
5
|
module LHC::Response::Data::Base
|
6
6
|
def as_json
|
7
|
-
@json ||= (@data || response.format.as_json(response.body))
|
7
|
+
@json ||= (@data || @response.format.as_json(@response.body))
|
8
8
|
end
|
9
9
|
|
10
10
|
def as_open_struct
|
@@ -12,11 +12,7 @@ module LHC::Response::Data::Base
|
|
12
12
|
if @data
|
13
13
|
JSON.parse(@data.to_json, object_class: OpenStruct)
|
14
14
|
else
|
15
|
-
response.format.as_open_struct(response.body)
|
15
|
+
@response.format.as_open_struct(@response.body)
|
16
16
|
end
|
17
17
|
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
attr_reader :response
|
22
18
|
end
|
data/lib/lhc/version.rb
CHANGED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
describe LHC::Auth do
|
6
|
+
before(:each) do
|
7
|
+
LHC.config.interceptors = [LHC::Auth]
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'adds body authentication to the existing request body' do
|
11
|
+
stub_request(:post, "http://local.ch/")
|
12
|
+
.with(body: {
|
13
|
+
message: 'body',
|
14
|
+
userToken: 'dheur5hrk3'
|
15
|
+
}.to_json)
|
16
|
+
|
17
|
+
LHC.post('http://local.ch', auth: { body: { userToken: 'dheur5hrk3' } }, body: {
|
18
|
+
message: 'body'
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'adds body authentication to an empty request body' do
|
23
|
+
stub_request(:post, "http://local.ch/")
|
24
|
+
.with(body: {
|
25
|
+
userToken: 'dheur5hrk3'
|
26
|
+
}.to_json)
|
27
|
+
|
28
|
+
LHC.post('http://local.ch', auth: { body: { userToken: 'dheur5hrk3' } })
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'adds nothing if request method is GET' do
|
32
|
+
stub_request(:get, "http://local.ch/")
|
33
|
+
|
34
|
+
LHC.get('http://local.ch', auth: { body: { userToken: 'dheur5hrk3' } })
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
describe LHC::Auth do
|
6
|
+
before(:each) do
|
7
|
+
class AuthPrepInterceptor < LHC::Interceptor
|
8
|
+
|
9
|
+
def before_request
|
10
|
+
request.options[:auth] = { bearer: 'sometoken' }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
LHC.config.interceptors = [AuthPrepInterceptor, LHC::Auth]
|
15
|
+
end
|
16
|
+
|
17
|
+
after do
|
18
|
+
LHC.config.reset
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'does not use instance variables internally so that other interceptors can still change auth options' do
|
22
|
+
stub_request(:get, "http://local.ch/")
|
23
|
+
.with(headers: { 'Authorization' => 'Bearer sometoken' })
|
24
|
+
.to_return(status: 200)
|
25
|
+
LHC.get('http://local.ch')
|
26
|
+
end
|
27
|
+
end
|
@@ -3,66 +3,64 @@
|
|
3
3
|
require 'rails_helper'
|
4
4
|
|
5
5
|
describe LHC::Throttle do
|
6
|
+
let(:options_break) { false }
|
7
|
+
let(:options_expires) { { header: 'reset' } }
|
8
|
+
let(:options_limit) { { header: 'limit' } }
|
9
|
+
let(:options_remaining) { { header: 'remaining' } }
|
6
10
|
let(:provider) { 'local.ch' }
|
7
|
-
let(:
|
8
|
-
let(:
|
11
|
+
let(:quota_limit) { 10_000 }
|
12
|
+
let(:quota_remaining) { 1900 }
|
13
|
+
let(:quota_reset) { (Time.zone.now + 1.hour).to_i }
|
9
14
|
let(:options) do
|
10
15
|
{
|
11
16
|
throttle: {
|
12
17
|
provider: provider,
|
13
18
|
track: true,
|
14
|
-
limit:
|
15
|
-
remaining:
|
16
|
-
expires:
|
17
|
-
break:
|
19
|
+
limit: options_limit,
|
20
|
+
remaining: options_remaining,
|
21
|
+
expires: options_expires,
|
22
|
+
break: options_break
|
18
23
|
}
|
19
24
|
}
|
20
25
|
end
|
21
|
-
let(:limit_options) { { header: 'Rate-Limit-Limit' } }
|
22
|
-
let(:break_option) { false }
|
23
|
-
let(:expires_in) { (Time.zone.now + 1.hour).to_i }
|
24
26
|
|
25
27
|
before(:each) do
|
26
28
|
LHC::Throttle.track = nil
|
27
29
|
LHC.config.interceptors = [LHC::Throttle]
|
28
30
|
|
29
|
-
stub_request(:get, 'http://local.ch')
|
30
|
-
|
31
|
-
|
32
|
-
'Rate-Limit-Limit' => limit,
|
33
|
-
'Rate-Limit-Remaining' => remaining,
|
34
|
-
'Rate-Limit-Reset' => expires_in
|
35
|
-
}
|
36
|
-
)
|
31
|
+
stub_request(:get, 'http://local.ch').to_return(
|
32
|
+
headers: { 'limit' => quota_limit, 'remaining' => quota_remaining, 'reset' => quota_reset }
|
33
|
+
)
|
37
34
|
end
|
38
35
|
|
39
36
|
it 'tracks the request limits based on response data' do
|
40
37
|
LHC.get('http://local.ch', options)
|
41
|
-
expect(LHC::Throttle.track[provider][:limit]).to eq
|
42
|
-
expect(LHC::Throttle.track[provider][:remaining]).to eq
|
38
|
+
expect(LHC::Throttle.track[provider][:limit]).to eq quota_limit
|
39
|
+
expect(LHC::Throttle.track[provider][:remaining]).to eq quota_remaining
|
43
40
|
end
|
44
41
|
|
45
42
|
context 'fix predefined integer for limit' do
|
46
|
-
let(:
|
43
|
+
let(:options_limit) { 1000 }
|
47
44
|
|
48
45
|
it 'tracks the limit based on initialy provided data' do
|
49
46
|
LHC.get('http://local.ch', options)
|
50
|
-
expect(LHC::Throttle.track[provider][:limit]).to eq
|
47
|
+
expect(LHC::Throttle.track[provider][:limit]).to eq options_limit
|
51
48
|
end
|
52
49
|
end
|
53
50
|
|
54
51
|
context 'breaks' do
|
55
|
-
let(:
|
52
|
+
let(:options_break) { '80%' }
|
56
53
|
|
57
54
|
it 'hit the breaks if throttling quota is reached' do
|
58
55
|
LHC.get('http://local.ch', options)
|
59
|
-
expect(
|
60
|
-
LHC
|
61
|
-
|
56
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
57
|
+
LHC::Throttle::OutOfQuota,
|
58
|
+
'Reached predefined quota for local.ch'
|
59
|
+
)
|
62
60
|
end
|
63
61
|
|
64
62
|
context 'still within quota' do
|
65
|
-
let(:
|
63
|
+
let(:options_break) { '90%' }
|
66
64
|
|
67
65
|
it 'does not hit the breaks' do
|
68
66
|
LHC.get('http://local.ch', options)
|
@@ -72,17 +70,14 @@ describe LHC::Throttle do
|
|
72
70
|
end
|
73
71
|
|
74
72
|
context 'no response headers' do
|
75
|
-
before
|
76
|
-
stub_request(:get, 'http://local.ch')
|
77
|
-
.to_return(status: 200)
|
78
|
-
end
|
73
|
+
before { stub_request(:get, 'http://local.ch').to_return(status: 200) }
|
79
74
|
|
80
75
|
it 'does not raise an exception' do
|
81
76
|
LHC.get('http://local.ch', options)
|
82
77
|
end
|
83
78
|
|
84
79
|
context 'no remaining tracked, but break enabled' do
|
85
|
-
let(:
|
80
|
+
let(:options_break) { '90%' }
|
86
81
|
|
87
82
|
it 'does not fail if a remaining was not tracked yet' do
|
88
83
|
LHC.get('http://local.ch', options)
|
@@ -92,15 +87,76 @@ describe LHC::Throttle do
|
|
92
87
|
end
|
93
88
|
|
94
89
|
context 'expires' do
|
95
|
-
let(:
|
90
|
+
let(:options_break) { '80%' }
|
96
91
|
|
97
92
|
it 'attempts another request if the quota expired' do
|
98
93
|
LHC.get('http://local.ch', options)
|
99
|
-
expect(
|
100
|
-
LHC
|
101
|
-
|
94
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
95
|
+
LHC::Throttle::OutOfQuota,
|
96
|
+
'Reached predefined quota for local.ch'
|
97
|
+
)
|
102
98
|
Timecop.travel(Time.zone.now + 2.hours)
|
103
99
|
LHC.get('http://local.ch', options)
|
104
100
|
end
|
105
101
|
end
|
102
|
+
|
103
|
+
describe 'calculate "remaining" in proc' do
|
104
|
+
let(:quota_current) { 8100 }
|
105
|
+
let(:options_remaining) do
|
106
|
+
->(response) { (response.headers['limit']).to_i - (response.headers['current']).to_i }
|
107
|
+
end
|
108
|
+
|
109
|
+
before(:each) do
|
110
|
+
stub_request(:get, 'http://local.ch').to_return(
|
111
|
+
headers: { 'limit' => quota_limit, 'current' => quota_current, 'reset' => quota_reset }
|
112
|
+
)
|
113
|
+
LHC.get('http://local.ch', options)
|
114
|
+
end
|
115
|
+
|
116
|
+
context 'breaks' do
|
117
|
+
let(:options_break) { '80%' }
|
118
|
+
|
119
|
+
it 'hit the breaks if throttling quota is reached' do
|
120
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
121
|
+
LHC::Throttle::OutOfQuota,
|
122
|
+
'Reached predefined quota for local.ch'
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'still within quota' do
|
127
|
+
let(:options_break) { '90%' }
|
128
|
+
|
129
|
+
it 'does not hit the breaks' do
|
130
|
+
LHC.get('http://local.ch', options)
|
131
|
+
LHC.get('http://local.ch', options)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe 'parsing reset time given in prose' do
|
138
|
+
let(:quota_reset) { (Time.zone.now + 1.day).strftime('%A, %B %d, %Y 12:00:00 AM GMT').to_s }
|
139
|
+
|
140
|
+
before { LHC.get('http://local.ch', options) }
|
141
|
+
|
142
|
+
context 'breaks' do
|
143
|
+
let(:options_break) { '80%' }
|
144
|
+
|
145
|
+
it 'hit the breaks if throttling quota is reached' do
|
146
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
147
|
+
LHC::Throttle::OutOfQuota,
|
148
|
+
'Reached predefined quota for local.ch'
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'still within quota' do
|
153
|
+
let(:options_break) { '90%' }
|
154
|
+
|
155
|
+
it 'does not hit the breaks' do
|
156
|
+
LHC.get('http://local.ch', options)
|
157
|
+
LHC.get('http://local.ch', options)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
106
162
|
end
|
data/spec/response/data_spec.rb
CHANGED
@@ -58,4 +58,28 @@ describe LHC::Response do
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
context 'response data if responding error data contains a response' do
|
63
|
+
before do
|
64
|
+
stub_request(:get, "http://listings/")
|
65
|
+
.to_return(status: 404, body: {
|
66
|
+
meta: {
|
67
|
+
errors: [
|
68
|
+
{ code: 2000, msg: 'I like to hide error messages (this is meta).' }
|
69
|
+
]
|
70
|
+
},
|
71
|
+
response: 'why not?'
|
72
|
+
}.to_json)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'does not throw a stack level to deep issue when accessing data in a rescue context' do
|
76
|
+
begin
|
77
|
+
LHC.get('http://listings')
|
78
|
+
rescue LHC::Error => error
|
79
|
+
expect(
|
80
|
+
error.response.request.response.data.meta.errors.detect { |item| item.code == 2000 }.msg
|
81
|
+
).to eq 'I like to hide error messages (this is meta).'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
61
85
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lhc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 12.0
|
4
|
+
version: 12.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/local-ch/lhc/contributors
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -325,7 +325,9 @@ files:
|
|
325
325
|
- spec/interceptors/after_response_spec.rb
|
326
326
|
- spec/interceptors/auth/basic_auth_spec.rb
|
327
327
|
- spec/interceptors/auth/bearer_spec.rb
|
328
|
+
- spec/interceptors/auth/body_spec.rb
|
328
329
|
- spec/interceptors/auth/long_basic_auth_credentials_spec.rb
|
330
|
+
- spec/interceptors/auth/no_instance_var_for_options_spec.rb
|
329
331
|
- spec/interceptors/auth/reauthentication_configuration_spec.rb
|
330
332
|
- spec/interceptors/auth/reauthentication_spec.rb
|
331
333
|
- spec/interceptors/before_request_spec.rb
|
@@ -387,7 +389,7 @@ homepage: https://github.com/local-ch/lhc
|
|
387
389
|
licenses:
|
388
390
|
- GPL-3.0
|
389
391
|
metadata: {}
|
390
|
-
post_install_message:
|
392
|
+
post_install_message:
|
391
393
|
rdoc_options: []
|
392
394
|
require_paths:
|
393
395
|
- lib
|
@@ -403,8 +405,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
403
405
|
version: '0'
|
404
406
|
requirements:
|
405
407
|
- Ruby >= 2.0.0
|
406
|
-
rubygems_version: 3.0.
|
407
|
-
signing_key:
|
408
|
+
rubygems_version: 3.0.3
|
409
|
+
signing_key:
|
408
410
|
specification_version: 4
|
409
411
|
summary: Advanced HTTP Client for Ruby, fueled with interceptors
|
410
412
|
test_files:
|
@@ -476,7 +478,9 @@ test_files:
|
|
476
478
|
- spec/interceptors/after_response_spec.rb
|
477
479
|
- spec/interceptors/auth/basic_auth_spec.rb
|
478
480
|
- spec/interceptors/auth/bearer_spec.rb
|
481
|
+
- spec/interceptors/auth/body_spec.rb
|
479
482
|
- spec/interceptors/auth/long_basic_auth_credentials_spec.rb
|
483
|
+
- spec/interceptors/auth/no_instance_var_for_options_spec.rb
|
480
484
|
- spec/interceptors/auth/reauthentication_configuration_spec.rb
|
481
485
|
- spec/interceptors/auth/reauthentication_spec.rb
|
482
486
|
- spec/interceptors/before_request_spec.rb
|