lhc 12.1.3 → 13.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|