lhc 12.1.3 → 13.1.0
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.activesupport5 +1 -1
- data/Gemfile.activesupport6 +1 -1
- data/README.md +88 -16
- data/cider-ci.yml +0 -1
- data/lhc.gemspec +3 -2
- data/lib/lhc/error.rb +3 -1
- data/lib/lhc/interceptor.rb +4 -0
- data/lib/lhc/interceptors/auth.rb +0 -4
- data/lib/lhc/interceptors/caching.rb +65 -44
- data/lib/lhc/interceptors/monitoring.rb +39 -10
- data/lib/lhc/interceptors/throttle.rb +26 -21
- data/lib/lhc/railtie.rb +0 -1
- data/lib/lhc/request.rb +6 -2
- data/lib/lhc/rspec.rb +1 -2
- data/lib/lhc/version.rb +1 -1
- data/spec/error/to_s_spec.rb +7 -2
- data/spec/interceptors/after_response_spec.rb +1 -1
- data/spec/interceptors/caching/main_spec.rb +2 -2
- data/spec/interceptors/caching/multilevel_cache_spec.rb +139 -0
- data/spec/interceptors/caching/options_spec.rb +0 -11
- data/spec/interceptors/monitoring/caching_spec.rb +66 -0
- data/spec/interceptors/response_competition_spec.rb +2 -2
- data/spec/interceptors/return_response_spec.rb +2 -2
- data/spec/interceptors/throttle/main_spec.rb +165 -35
- data/spec/spec_helper.rb +1 -0
- metadata +24 -8
- data/Gemfile.activesupport4 +0 -4
- data/cider-ci/jobs/rspec-activesupport-4.yml +0 -28
@@ -12,7 +12,7 @@ describe LHC do
|
|
12
12
|
|
13
13
|
def before_request
|
14
14
|
if @@cached
|
15
|
-
return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from local cache'), nil)
|
15
|
+
return LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from local cache'), nil)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -22,7 +22,7 @@ describe LHC do
|
|
22
22
|
|
23
23
|
def before_request
|
24
24
|
if request.response.nil?
|
25
|
-
return LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from remote cache'), nil)
|
25
|
+
return LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from remote cache'), nil)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -8,7 +8,7 @@ describe LHC do
|
|
8
8
|
class CacheInterceptor < LHC::Interceptor
|
9
9
|
|
10
10
|
def before_request
|
11
|
-
LHC::Response.new(Typhoeus::Response.new(response_body: 'Im served from cache'), nil)
|
11
|
+
LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok, response_body: 'Im served from cache'), nil)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
LHC.configure { |c| c.interceptors = [CacheInterceptor] }
|
@@ -23,7 +23,7 @@ describe LHC do
|
|
23
23
|
before(:each) do
|
24
24
|
class AnotherInterceptor < LHC::Interceptor
|
25
25
|
def before_request
|
26
|
-
LHC::Response.new(Typhoeus::Response.new(
|
26
|
+
LHC::Response.new(Typhoeus::Response.new(response_code: 200, return_code: :ok), nil)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
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,150 @@ 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 'configuration values as Procs' do
|
104
|
+
describe 'calculate "limit" in proc' do
|
105
|
+
let(:options_limit) do
|
106
|
+
->(*) { 10_000 }
|
107
|
+
end
|
108
|
+
|
109
|
+
before(:each) do
|
110
|
+
LHC.get('http://local.ch', options)
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'breaks' do
|
114
|
+
let(:options_break) { '80%' }
|
115
|
+
|
116
|
+
it 'hit the breaks if throttling quota is reached' do
|
117
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
118
|
+
LHC::Throttle::OutOfQuota,
|
119
|
+
'Reached predefined quota for local.ch'
|
120
|
+
)
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'still within quota' do
|
124
|
+
let(:options_break) { '90%' }
|
125
|
+
|
126
|
+
it 'does not hit the breaks' do
|
127
|
+
LHC.get('http://local.ch', options)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe 'calculate "remaining" in proc' do
|
134
|
+
let(:quota_current) { 8100 }
|
135
|
+
let(:options_remaining) do
|
136
|
+
->(response) { (response.headers['limit']).to_i - (response.headers['current']).to_i }
|
137
|
+
end
|
138
|
+
|
139
|
+
before(:each) do
|
140
|
+
stub_request(:get, 'http://local.ch').to_return(
|
141
|
+
headers: { 'limit' => quota_limit, 'current' => quota_current, 'reset' => quota_reset }
|
142
|
+
)
|
143
|
+
LHC.get('http://local.ch', options)
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'breaks' do
|
147
|
+
let(:options_break) { '80%' }
|
148
|
+
|
149
|
+
it 'hit the breaks if throttling quota is reached' do
|
150
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
151
|
+
LHC::Throttle::OutOfQuota,
|
152
|
+
'Reached predefined quota for local.ch'
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
context 'still within quota' do
|
157
|
+
let(:options_break) { '90%' }
|
158
|
+
|
159
|
+
it 'does not hit the breaks' do
|
160
|
+
LHC.get('http://local.ch', options)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe 'calculate "reset" in proc' do
|
167
|
+
let(:options_expires) { ->(*) { Time.zone.now + 1.second } }
|
168
|
+
|
169
|
+
before(:each) do
|
170
|
+
stub_request(:get, 'http://local.ch').to_return(
|
171
|
+
headers: { 'limit' => quota_limit, 'remaining' => quota_remaining }
|
172
|
+
)
|
173
|
+
LHC.get('http://local.ch', options)
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'breaks' do
|
177
|
+
let(:options_break) { '80%' }
|
178
|
+
|
179
|
+
it 'hit the breaks if throttling quota is reached' do
|
180
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
181
|
+
LHC::Throttle::OutOfQuota,
|
182
|
+
'Reached predefined quota for local.ch'
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
context 'still within quota' do
|
187
|
+
let(:options_break) { '90%' }
|
188
|
+
|
189
|
+
it 'does not hit the breaks' do
|
190
|
+
LHC.get('http://local.ch', options)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe 'parsing reset time given in prose' do
|
198
|
+
let(:quota_reset) { (Time.zone.now + 1.day).strftime('%A, %B %d, %Y 12:00:00 AM GMT').to_s }
|
199
|
+
|
200
|
+
before { LHC.get('http://local.ch', options) }
|
201
|
+
|
202
|
+
context 'breaks' do
|
203
|
+
let(:options_break) { '80%' }
|
204
|
+
|
205
|
+
it 'hit the breaks if throttling quota is reached' do
|
206
|
+
expect { LHC.get('http://local.ch', options) }.to raise_error(
|
207
|
+
LHC::Throttle::OutOfQuota,
|
208
|
+
'Reached predefined quota for local.ch'
|
209
|
+
)
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'still within quota' do
|
213
|
+
let(:options_break) { '90%' }
|
214
|
+
|
215
|
+
it 'does not hit the breaks' do
|
216
|
+
LHC.get('http://local.ch', options)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
context 'when value is empty' do
|
223
|
+
let(:quota_reset) { nil }
|
224
|
+
|
225
|
+
before do
|
226
|
+
stub_request(:get, 'http://local.ch').to_return(
|
227
|
+
headers: { 'limit' => quota_limit, 'remaining' => quota_remaining }
|
228
|
+
)
|
229
|
+
LHC.get('http://local.ch', options)
|
230
|
+
end
|
231
|
+
|
232
|
+
it 'still runs' do
|
233
|
+
LHC.get('http://local.ch', options)
|
234
|
+
end
|
235
|
+
end
|
106
236
|
end
|
data/spec/spec_helper.rb
CHANGED
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:
|
4
|
+
version: 13.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/local-ch/lhc/contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '5.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: addressable
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -100,14 +100,28 @@ dependencies:
|
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '5.2'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '5.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: redis
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: rspec-rails
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,7 +207,6 @@ files:
|
|
193
207
|
- ".rubocop.yml"
|
194
208
|
- ".ruby-version"
|
195
209
|
- Gemfile
|
196
|
-
- Gemfile.activesupport4
|
197
210
|
- Gemfile.activesupport5
|
198
211
|
- Gemfile.activesupport6
|
199
212
|
- LICENSE
|
@@ -203,7 +216,6 @@ files:
|
|
203
216
|
- cider-ci/bin/bundle
|
204
217
|
- cider-ci/bin/ruby_install
|
205
218
|
- cider-ci/bin/ruby_version
|
206
|
-
- cider-ci/jobs/rspec-activesupport-4.yml
|
207
219
|
- cider-ci/jobs/rspec-activesupport-5.yml
|
208
220
|
- cider-ci/jobs/rspec-activesupport-6.yml
|
209
221
|
- cider-ci/jobs/rubocop.yml
|
@@ -335,6 +347,7 @@ files:
|
|
335
347
|
- spec/interceptors/caching/hydra_spec.rb
|
336
348
|
- spec/interceptors/caching/main_spec.rb
|
337
349
|
- spec/interceptors/caching/methods_spec.rb
|
350
|
+
- spec/interceptors/caching/multilevel_cache_spec.rb
|
338
351
|
- spec/interceptors/caching/options_spec.rb
|
339
352
|
- spec/interceptors/caching/parameters_spec.rb
|
340
353
|
- spec/interceptors/caching/response_status_spec.rb
|
@@ -344,6 +357,7 @@ files:
|
|
344
357
|
- spec/interceptors/define_spec.rb
|
345
358
|
- spec/interceptors/dup_spec.rb
|
346
359
|
- spec/interceptors/logging/main_spec.rb
|
360
|
+
- spec/interceptors/monitoring/caching_spec.rb
|
347
361
|
- spec/interceptors/monitoring/main_spec.rb
|
348
362
|
- spec/interceptors/prometheus_spec.rb
|
349
363
|
- spec/interceptors/response_competition_spec.rb
|
@@ -488,6 +502,7 @@ test_files:
|
|
488
502
|
- spec/interceptors/caching/hydra_spec.rb
|
489
503
|
- spec/interceptors/caching/main_spec.rb
|
490
504
|
- spec/interceptors/caching/methods_spec.rb
|
505
|
+
- spec/interceptors/caching/multilevel_cache_spec.rb
|
491
506
|
- spec/interceptors/caching/options_spec.rb
|
492
507
|
- spec/interceptors/caching/parameters_spec.rb
|
493
508
|
- spec/interceptors/caching/response_status_spec.rb
|
@@ -497,6 +512,7 @@ test_files:
|
|
497
512
|
- spec/interceptors/define_spec.rb
|
498
513
|
- spec/interceptors/dup_spec.rb
|
499
514
|
- spec/interceptors/logging/main_spec.rb
|
515
|
+
- spec/interceptors/monitoring/caching_spec.rb
|
500
516
|
- spec/interceptors/monitoring/main_spec.rb
|
501
517
|
- spec/interceptors/prometheus_spec.rb
|
502
518
|
- spec/interceptors/response_competition_spec.rb
|
data/Gemfile.activesupport4
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
rspec-active-support-v4:
|
2
|
-
name: 'rspec with ActiveSupport v4'
|
3
|
-
|
4
|
-
run_when:
|
5
|
-
'some HEAD has been updated':
|
6
|
-
type: branch
|
7
|
-
include_match: ^.*$
|
8
|
-
|
9
|
-
context:
|
10
|
-
|
11
|
-
script_defaults:
|
12
|
-
template_environment_variables: true
|
13
|
-
|
14
|
-
task_defaults:
|
15
|
-
environment_variables:
|
16
|
-
ACTIVESUPPORT: '4'
|
17
|
-
BUNDLER: '_1.17.3_'
|
18
|
-
|
19
|
-
max_trials: 2
|
20
|
-
dispatch_storm_delay_duration: 1 Seconds
|
21
|
-
include:
|
22
|
-
- cider-ci/task_components/ruby.yml
|
23
|
-
- cider-ci/task_components/bundle.yml
|
24
|
-
- cider-ci/task_components/rspec.yml
|
25
|
-
|
26
|
-
tasks:
|
27
|
-
all-rspec:
|
28
|
-
name: All rspec tests, using ActiveSupport v4
|