rate-limited-jira-ruby 0.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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +16 -0
- data/Gemfile.lock +306 -0
- data/Rakefile +22 -0
- data/bin/rate-limited-jira-tool +23 -0
- data/cucumber.yml +2 -0
- data/features/control_http_request_rate_limit.feature +29 -0
- data/features/step_definitions/execution_context_steps.rb +16 -0
- data/features/support/env.rb +5 -0
- data/features/support/hooks.rb +10 -0
- data/lib/rate_limited_jira/client/in_process_based.rb +20 -0
- data/lib/rate_limited_jira/client/redis_based.rb +32 -0
- data/lib/rate_limited_jira/client.rb +54 -0
- data/lib/rate_limited_jira/version.rb +5 -0
- data/lib/rate_limited_jira.rb +4 -0
- data/spec/rate_limited_jira/client/in_process_based_spec.rb +52 -0
- data/spec/rate_limited_jira/client/redis_based_spec.rb +44 -0
- data/spec/rate_limited_jira/client_spec.rb +90 -0
- data/spec/spec_helper.rb +15 -0
- metadata +121 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f974fc0246b6865d25b39323815e3bf13443b40a242d58dc1c6fd3a2a8f6585a
|
|
4
|
+
data.tar.gz: df93cdcddc729ea281903252440d1f9f15bff60b23e10c6d26c7aef25c21c244
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0b4d577f477bb76a68b789170ebbe9ca85b29e42f80c1f000b5a088580ee12d0f572ceccf108940c7cd148a38be3d4eb83a92ab80a71f8e69b68bcf949ab915c
|
|
7
|
+
data.tar.gz: 104eff897e1ef2252152988e081c6031d95d1f517e0d5b7595f8068edb7b238cc5297fa57b108b9127ff14fc52ee0bc535c202c3009425ca9084390ab279a7b9
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
|
|
4
|
+
plugins:
|
|
5
|
+
- rubocop-rake
|
|
6
|
+
- rubocop-rspec
|
|
7
|
+
|
|
8
|
+
RSpec/ExampleLength:
|
|
9
|
+
Enabled: true
|
|
10
|
+
Max: 10
|
|
11
|
+
|
|
12
|
+
RSpec/MessageExpectation:
|
|
13
|
+
Enabled: false
|
|
14
|
+
|
|
15
|
+
RSpec/MessageSpies:
|
|
16
|
+
Enabled: false
|
|
17
|
+
|
|
18
|
+
RSpec/MultipleExpectations:
|
|
19
|
+
Enabled: true
|
|
20
|
+
Max: 4
|
|
21
|
+
|
|
22
|
+
Style/Documentation:
|
|
23
|
+
Enabled: false
|
|
24
|
+
|
|
25
|
+
Style/StringLiterals:
|
|
26
|
+
EnforcedStyle: double_quotes
|
|
27
|
+
|
|
28
|
+
Style/StringLiteralsInInterpolation:
|
|
29
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
## [Unreleased]
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-05-18
|
|
4
|
+
|
|
5
|
+
- Initial release: transparent rate-limiting wrapper around jira-ruby's JIRA::Client
|
|
6
|
+
- In-process implementation using ruby-limiter
|
|
7
|
+
- Redis-based implementation using ratelimit + redis
|
|
8
|
+
- `RateLimitedJira::Client.build` factory; blank/nil implementation normalised to `:in_process`
|
data/Gemfile
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
source "https://rubygems.org"
|
|
4
|
+
|
|
5
|
+
gemspec
|
|
6
|
+
|
|
7
|
+
group :development do
|
|
8
|
+
gem "aruba"
|
|
9
|
+
gem "cucumber"
|
|
10
|
+
gem "rake"
|
|
11
|
+
gem "rake-gem-maintenance"
|
|
12
|
+
gem "rspec"
|
|
13
|
+
gem "rubocop"
|
|
14
|
+
gem "rubocop-rake", require: false
|
|
15
|
+
gem "rubocop-rspec", require: false
|
|
16
|
+
end
|
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
rate-limited-jira-ruby (0.1.0)
|
|
5
|
+
jira-ruby
|
|
6
|
+
ratelimit
|
|
7
|
+
redis
|
|
8
|
+
ruby-limiter
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
activesupport (8.1.3)
|
|
14
|
+
base64
|
|
15
|
+
bigdecimal
|
|
16
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
|
17
|
+
connection_pool (>= 2.2.5)
|
|
18
|
+
drb
|
|
19
|
+
i18n (>= 1.6, < 2)
|
|
20
|
+
json
|
|
21
|
+
logger (>= 1.4.2)
|
|
22
|
+
minitest (>= 5.1)
|
|
23
|
+
securerandom (>= 0.3)
|
|
24
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
|
25
|
+
uri (>= 0.13.1)
|
|
26
|
+
aruba (2.4.1)
|
|
27
|
+
bundler (>= 1.17)
|
|
28
|
+
contracts (>= 0.16.0, < 0.18.0)
|
|
29
|
+
cucumber (>= 8.0, < 12.0)
|
|
30
|
+
irb (~> 1.16)
|
|
31
|
+
rspec-expectations (>= 3.4, < 5.0)
|
|
32
|
+
thor (~> 1.0)
|
|
33
|
+
ast (2.4.3)
|
|
34
|
+
atlassian-jwt (0.2.1)
|
|
35
|
+
jwt (~> 2.1)
|
|
36
|
+
base64 (0.3.0)
|
|
37
|
+
bigdecimal (4.1.2)
|
|
38
|
+
builder (3.3.0)
|
|
39
|
+
bundler-audit (0.9.3)
|
|
40
|
+
bundler (>= 1.2.0)
|
|
41
|
+
thor (~> 1.0)
|
|
42
|
+
cgi (0.5.1)
|
|
43
|
+
concurrent-ruby (1.3.6)
|
|
44
|
+
connection_pool (3.0.2)
|
|
45
|
+
contracts (0.17.3)
|
|
46
|
+
cucumber (11.0.0)
|
|
47
|
+
base64 (~> 0.2)
|
|
48
|
+
builder (~> 3.2)
|
|
49
|
+
cucumber-ci-environment (> 9, < 12)
|
|
50
|
+
cucumber-core (>= 16.2.0, < 17)
|
|
51
|
+
cucumber-cucumber-expressions (> 17, < 20)
|
|
52
|
+
cucumber-html-formatter (> 21, < 24)
|
|
53
|
+
diff-lcs (~> 1.5)
|
|
54
|
+
logger (~> 1.6)
|
|
55
|
+
mini_mime (~> 1.1)
|
|
56
|
+
multi_test (~> 1.1)
|
|
57
|
+
sys-uname (~> 1.5)
|
|
58
|
+
cucumber-ci-environment (11.0.0)
|
|
59
|
+
cucumber-core (16.2.0)
|
|
60
|
+
cucumber-gherkin (> 36, < 40)
|
|
61
|
+
cucumber-messages (> 31, < 33)
|
|
62
|
+
cucumber-tag-expressions (> 6, < 9)
|
|
63
|
+
cucumber-cucumber-expressions (19.0.0)
|
|
64
|
+
bigdecimal
|
|
65
|
+
cucumber-gherkin (39.1.0)
|
|
66
|
+
cucumber-messages (>= 31, < 33)
|
|
67
|
+
cucumber-html-formatter (23.1.0)
|
|
68
|
+
cucumber-messages (> 23, < 33)
|
|
69
|
+
cucumber-messages (32.3.1)
|
|
70
|
+
cucumber-tag-expressions (8.1.0)
|
|
71
|
+
date (3.5.1)
|
|
72
|
+
diff-lcs (1.6.2)
|
|
73
|
+
drb (2.2.3)
|
|
74
|
+
erb (6.0.4)
|
|
75
|
+
ffi (1.17.4)
|
|
76
|
+
ffi (1.17.4-x86_64-linux-gnu)
|
|
77
|
+
gem-release (2.2.4)
|
|
78
|
+
hashie (5.1.0)
|
|
79
|
+
logger
|
|
80
|
+
i18n (1.14.8)
|
|
81
|
+
concurrent-ruby (~> 1.0)
|
|
82
|
+
io-console (0.8.2)
|
|
83
|
+
irb (1.18.0)
|
|
84
|
+
pp (>= 0.6.0)
|
|
85
|
+
prism (>= 1.3.0)
|
|
86
|
+
rdoc (>= 4.0.0)
|
|
87
|
+
reline (>= 0.4.2)
|
|
88
|
+
jira-ruby (3.1.0)
|
|
89
|
+
activesupport
|
|
90
|
+
atlassian-jwt
|
|
91
|
+
cgi
|
|
92
|
+
multipart-post
|
|
93
|
+
oauth (~> 1.0)
|
|
94
|
+
json (2.19.4)
|
|
95
|
+
jwt (2.10.2)
|
|
96
|
+
base64
|
|
97
|
+
language_server-protocol (3.17.0.5)
|
|
98
|
+
lint_roller (1.1.0)
|
|
99
|
+
logger (1.7.0)
|
|
100
|
+
memoist3 (1.0.0)
|
|
101
|
+
mini_mime (1.1.5)
|
|
102
|
+
minitest (6.0.5)
|
|
103
|
+
drb (~> 2.0)
|
|
104
|
+
prism (~> 1.5)
|
|
105
|
+
multi_test (1.1.0)
|
|
106
|
+
multipart-post (2.4.1)
|
|
107
|
+
oauth (1.1.3)
|
|
108
|
+
base64 (~> 0.1)
|
|
109
|
+
oauth-tty (~> 1.0, >= 1.0.6)
|
|
110
|
+
snaky_hash (~> 2.0)
|
|
111
|
+
version_gem (~> 1.1, >= 1.1.9)
|
|
112
|
+
oauth-tty (1.0.6)
|
|
113
|
+
version_gem (~> 1.1, >= 1.1.9)
|
|
114
|
+
parallel (2.1.0)
|
|
115
|
+
parser (3.3.11.1)
|
|
116
|
+
ast (~> 2.4.1)
|
|
117
|
+
racc
|
|
118
|
+
pp (0.6.3)
|
|
119
|
+
prettyprint
|
|
120
|
+
prettyprint (0.2.0)
|
|
121
|
+
prism (1.9.0)
|
|
122
|
+
psych (5.3.1)
|
|
123
|
+
date
|
|
124
|
+
stringio
|
|
125
|
+
racc (1.8.1)
|
|
126
|
+
rainbow (3.1.1)
|
|
127
|
+
rake (13.4.2)
|
|
128
|
+
rake-gem-maintenance (0.2.0)
|
|
129
|
+
bundler-audit
|
|
130
|
+
gem-release
|
|
131
|
+
rake
|
|
132
|
+
rotp
|
|
133
|
+
ratelimit (1.1.0)
|
|
134
|
+
redis (>= 3.0.0)
|
|
135
|
+
redis-namespace (>= 1.0.0)
|
|
136
|
+
rdoc (7.2.0)
|
|
137
|
+
erb
|
|
138
|
+
psych (>= 4.0.0)
|
|
139
|
+
tsort
|
|
140
|
+
redis (5.4.1)
|
|
141
|
+
redis-client (>= 0.22.0)
|
|
142
|
+
redis-client (0.28.0)
|
|
143
|
+
connection_pool
|
|
144
|
+
redis-namespace (1.11.0)
|
|
145
|
+
redis (>= 4)
|
|
146
|
+
regexp_parser (2.12.0)
|
|
147
|
+
reline (0.6.3)
|
|
148
|
+
io-console (~> 0.5)
|
|
149
|
+
rotp (6.3.0)
|
|
150
|
+
rspec (3.13.2)
|
|
151
|
+
rspec-core (~> 3.13.0)
|
|
152
|
+
rspec-expectations (~> 3.13.0)
|
|
153
|
+
rspec-mocks (~> 3.13.0)
|
|
154
|
+
rspec-core (3.13.6)
|
|
155
|
+
rspec-support (~> 3.13.0)
|
|
156
|
+
rspec-expectations (3.13.5)
|
|
157
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
158
|
+
rspec-support (~> 3.13.0)
|
|
159
|
+
rspec-mocks (3.13.8)
|
|
160
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
161
|
+
rspec-support (~> 3.13.0)
|
|
162
|
+
rspec-support (3.13.7)
|
|
163
|
+
rubocop (1.86.1)
|
|
164
|
+
json (~> 2.3)
|
|
165
|
+
language_server-protocol (~> 3.17.0.2)
|
|
166
|
+
lint_roller (~> 1.1.0)
|
|
167
|
+
parallel (>= 1.10)
|
|
168
|
+
parser (>= 3.3.0.2)
|
|
169
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
170
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
|
171
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
172
|
+
ruby-progressbar (~> 1.7)
|
|
173
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
|
174
|
+
rubocop-ast (1.49.1)
|
|
175
|
+
parser (>= 3.3.7.2)
|
|
176
|
+
prism (~> 1.7)
|
|
177
|
+
rubocop-rake (0.7.1)
|
|
178
|
+
lint_roller (~> 1.1)
|
|
179
|
+
rubocop (>= 1.72.1)
|
|
180
|
+
rubocop-rspec (3.9.0)
|
|
181
|
+
lint_roller (~> 1.1)
|
|
182
|
+
rubocop (~> 1.81)
|
|
183
|
+
ruby-limiter (2.3.0)
|
|
184
|
+
ruby-progressbar (1.13.0)
|
|
185
|
+
securerandom (0.4.1)
|
|
186
|
+
snaky_hash (2.0.3)
|
|
187
|
+
hashie (>= 0.1.0, < 6)
|
|
188
|
+
version_gem (>= 1.1.8, < 3)
|
|
189
|
+
stringio (3.2.0)
|
|
190
|
+
sys-uname (1.5.1)
|
|
191
|
+
ffi (~> 1.1)
|
|
192
|
+
memoist3 (~> 1.0.0)
|
|
193
|
+
thor (1.5.0)
|
|
194
|
+
tsort (0.2.0)
|
|
195
|
+
tzinfo (2.0.6)
|
|
196
|
+
concurrent-ruby (~> 1.0)
|
|
197
|
+
unicode-display_width (3.2.0)
|
|
198
|
+
unicode-emoji (~> 4.1)
|
|
199
|
+
unicode-emoji (4.2.0)
|
|
200
|
+
uri (1.1.1)
|
|
201
|
+
version_gem (1.1.9)
|
|
202
|
+
|
|
203
|
+
PLATFORMS
|
|
204
|
+
ruby
|
|
205
|
+
x86_64-linux
|
|
206
|
+
|
|
207
|
+
DEPENDENCIES
|
|
208
|
+
aruba
|
|
209
|
+
cucumber
|
|
210
|
+
rake
|
|
211
|
+
rake-gem-maintenance
|
|
212
|
+
rate-limited-jira-ruby!
|
|
213
|
+
rspec
|
|
214
|
+
rubocop
|
|
215
|
+
rubocop-rake
|
|
216
|
+
rubocop-rspec
|
|
217
|
+
|
|
218
|
+
CHECKSUMS
|
|
219
|
+
activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e
|
|
220
|
+
aruba (2.4.1) sha256=c6821949136bd9c953b3cf553971c4780faa26bb40082ac5ce631c541e0caf0d
|
|
221
|
+
ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
|
|
222
|
+
atlassian-jwt (0.2.1) sha256=2fd2d87418773f2e140c038cb22e049069708aff2bd0a423a7e1740574e97823
|
|
223
|
+
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
224
|
+
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
|
|
225
|
+
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
|
|
226
|
+
bundler-audit (0.9.3) sha256=81c8766c71e47d0d28a0f98c7eed028539f21a6ea3cd8f685eb6f42333c9b4e9
|
|
227
|
+
cgi (0.5.1) sha256=e93fcafc69b8a934fe1e6146121fa35430efa8b4a4047c4893764067036f18e9
|
|
228
|
+
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
|
229
|
+
connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
|
|
230
|
+
contracts (0.17.3) sha256=e72e626413ea47099becb7b5683beb1c2ea902c69f5bad55c9258fe2b48314d7
|
|
231
|
+
cucumber (11.0.0) sha256=14bb964cc172999e9010fece2fad9f104044b055b3199230091894637a2a784c
|
|
232
|
+
cucumber-ci-environment (11.0.0) sha256=0df79a9e1d0b015b3d9def680f989200d96fef206f4d19ccf86a338c4f71d1e2
|
|
233
|
+
cucumber-core (16.2.0) sha256=592b58a95cf42feef8e5a349f68e363784ba3b6568ffbcf6776e38e136cf970b
|
|
234
|
+
cucumber-cucumber-expressions (19.0.0) sha256=33208ff204732ac9bed42b46993a0a243054f71ece08579d57e53df6a1c9d93a
|
|
235
|
+
cucumber-gherkin (39.1.0) sha256=aed12a0c955d8563d80a012633c1a72075525f4d64d4cc983001df2181b379ed
|
|
236
|
+
cucumber-html-formatter (23.1.0) sha256=7789b4a792c876394b9604aeb66aa5cf4c61514473b7e712c76d5eaedcdd8cdf
|
|
237
|
+
cucumber-messages (32.3.1) sha256=ddc88e4c1cf7afb96c06005b92a4a6f221a2fa435a8b4ca04677d215fd82771c
|
|
238
|
+
cucumber-tag-expressions (8.1.0) sha256=9bd8c4b6654f8e5bf2a9c99329b6f32136a75e50cd39d4cfb3927d0fa9f52e21
|
|
239
|
+
date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
|
|
240
|
+
diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
|
|
241
|
+
drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
|
|
242
|
+
erb (6.0.4) sha256=38e3803694be357fe2bfe312487c74beaf9fb4e5beb3e22498952fe1645b95d9
|
|
243
|
+
ffi (1.17.4) sha256=bcd1642e06f0d16fc9e09ac6d49c3a7298b9789bcb58127302f934e437d60acf
|
|
244
|
+
ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d
|
|
245
|
+
gem-release (2.2.4) sha256=2f11124c1580c811507c3b47e875e420cf3ed792a98105b49df11971e6e94db3
|
|
246
|
+
hashie (5.1.0) sha256=c266471896f323c446ea8207f8ffac985d2718df0a0ba98651a3057096ca3870
|
|
247
|
+
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
|
|
248
|
+
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
|
|
249
|
+
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
|
|
250
|
+
jira-ruby (3.1.0) sha256=45664dac20565155a5fcde514be8820a10fa6c0153d494f7aa38e77b4248ad0b
|
|
251
|
+
json (2.19.4) sha256=670a7d333fb3b18ca5b29cb255eb7bef099e40d88c02c80bd42a3f30fe5239ac
|
|
252
|
+
jwt (2.10.2) sha256=31e1ee46f7359883d5e622446969fe9c118c3da87a0b1dca765ce269c3a0c4f4
|
|
253
|
+
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
|
|
254
|
+
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
|
|
255
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
256
|
+
memoist3 (1.0.0) sha256=686e42402cf150a362050c23143dc57b0ef88f8c344943ff8b7845792b50d56f
|
|
257
|
+
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
|
|
258
|
+
minitest (6.0.5) sha256=f007d7246bf4feea549502842cd7c6aba8851cdc9c90ba06de9c476c0d01155c
|
|
259
|
+
multi_test (1.1.0) sha256=e9e550cdd863fb72becfe344aefdcd4cbd26ebf307847f4a6c039a4082324d10
|
|
260
|
+
multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8
|
|
261
|
+
oauth (1.1.3) sha256=71ca1b534561bf31a9b2aee01147384064b555e796d1a0fe2591806bb4bdd633
|
|
262
|
+
oauth-tty (1.0.6) sha256=9e8bd1861d367cce18318d8f214f2e1a1d7cb3898de0a9ea79162b4fdecb3152
|
|
263
|
+
parallel (2.1.0) sha256=b35258865c2e31134c5ecb708beaaf6772adf9d5efae28e93e99260877b09356
|
|
264
|
+
parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54
|
|
265
|
+
pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
|
|
266
|
+
prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
|
|
267
|
+
prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
|
|
268
|
+
psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
|
|
269
|
+
racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
|
|
270
|
+
rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
|
|
271
|
+
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
|
|
272
|
+
rake-gem-maintenance (0.2.0) sha256=d43f2c0387377eb9be78594c7420518379e184a601581506562b3e5fa0f9d89b
|
|
273
|
+
rate-limited-jira-ruby (0.1.0)
|
|
274
|
+
ratelimit (1.1.0) sha256=e3fb5086f14f7119a299027a1eb844ad3c8d07f718e90df9f07ba7863b25f551
|
|
275
|
+
rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192
|
|
276
|
+
redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae
|
|
277
|
+
redis-client (0.28.0) sha256=888892f9cd8787a41c0ece00bdf5f556dfff7770326ce40bb2bc11f1bfec824b
|
|
278
|
+
redis-namespace (1.11.0) sha256=e91a1aa2b2d888b6dea1d4ab8d39e1ae6fac3426161feb9d91dd5cca598a2239
|
|
279
|
+
regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb
|
|
280
|
+
reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
|
|
281
|
+
rotp (6.3.0) sha256=75d40087e65ed0d8022c33055a6306c1c400d1c12261932533b5d6cbcd868854
|
|
282
|
+
rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
|
|
283
|
+
rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
|
|
284
|
+
rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
|
|
285
|
+
rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
|
|
286
|
+
rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
|
|
287
|
+
rubocop (1.86.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531
|
|
288
|
+
rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035
|
|
289
|
+
rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d
|
|
290
|
+
rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2
|
|
291
|
+
ruby-limiter (2.3.0) sha256=a8c247e14bc092c749d67bcd2a2def052580447da02b04612a2baa88cb154464
|
|
292
|
+
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
|
|
293
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
|
294
|
+
snaky_hash (2.0.3) sha256=25a3d299566e8153fb02fa23fd9a9358845950f7a523ddbbe1fa1e0d79a6d456
|
|
295
|
+
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
|
|
296
|
+
sys-uname (1.5.1) sha256=784d7e6491b0393c25cbbe5ac38324ac7be9fda083a6094832648af669386d7b
|
|
297
|
+
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
|
|
298
|
+
tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
|
|
299
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
|
300
|
+
unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
|
|
301
|
+
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
|
|
302
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
303
|
+
version_gem (1.1.9) sha256=0c1a0962ae543c84a00889bb018d9f14d8f8af6029d26b295d98774e3d2eb9a4
|
|
304
|
+
|
|
305
|
+
BUNDLED WITH
|
|
306
|
+
4.0.10
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/gem/maintenance/install_tasks"
|
|
5
|
+
require "rspec/core/rake_task"
|
|
6
|
+
require "rubocop/rake_task"
|
|
7
|
+
require "cucumber/rake/task"
|
|
8
|
+
|
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
10
|
+
|
|
11
|
+
RuboCop::RakeTask.new(:rubocop) do |task|
|
|
12
|
+
task.options = ["--autocorrect"]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Cucumber::Rake::Task.new do |t|
|
|
16
|
+
t.profile = "rake"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task default: :verify
|
|
20
|
+
|
|
21
|
+
desc "Run all checks"
|
|
22
|
+
task verify: %i[rubocop spec cucumber]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "rate_limited_jira"
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
client = RateLimitedJira::Client.build(
|
|
8
|
+
{
|
|
9
|
+
username: ENV.fetch("JIRA_USERNAME"),
|
|
10
|
+
password: ENV.fetch("JIRA_API_TOKEN"),
|
|
11
|
+
site: ENV.fetch("JIRA_SITE_URL"),
|
|
12
|
+
context_path: ENV.fetch("JIRA_CONTEXT_PATH", ""),
|
|
13
|
+
auth_type: :basic
|
|
14
|
+
},
|
|
15
|
+
rate_limit_per_interval: ENV.fetch("JAT_RATE_LIMIT_PER_INTERVAL", "0").to_i,
|
|
16
|
+
rate_interval_in_seconds: ENV.fetch("JAT_RATE_INTERVAL_IN_SECONDS", "0").to_i,
|
|
17
|
+
implementation: ENV.fetch("JAT_RATE_LIMIT_IMPLEMENTATION", "")
|
|
18
|
+
)
|
|
19
|
+
3.times { client.Board.all }
|
|
20
|
+
rescue StandardError => e
|
|
21
|
+
warn "#{e.class}: #{e.message}"
|
|
22
|
+
exit 1
|
|
23
|
+
end
|
data/cucumber.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Feature: Control the HTTP request rate limit
|
|
2
|
+
In order to work against a JIRA instance imposing API rate limits
|
|
3
|
+
As a user
|
|
4
|
+
I need the ability to control the HTTP request limit
|
|
5
|
+
|
|
6
|
+
Scenario Outline: Limiting the request rate
|
|
7
|
+
Given the following environment variables are set:
|
|
8
|
+
| name | value |
|
|
9
|
+
| JAT_RATE_INTERVAL_IN_SECONDS | <rate_interval_in_seconds> |
|
|
10
|
+
| JAT_RATE_LIMIT_IMPLEMENTATION | <jat_rate_limit_implementation> |
|
|
11
|
+
| JAT_RATE_LIMIT_PER_INTERVAL | <rate_limit_per_interval> |
|
|
12
|
+
Then successfully running `rate-limited-jira-tool` takes between <minimal_time> and <maximal_time> seconds
|
|
13
|
+
|
|
14
|
+
Examples:
|
|
15
|
+
| jat_rate_limit_implementation | rate_limit_per_interval | rate_interval_in_seconds | minimal_time | maximal_time |
|
|
16
|
+
| | 0 | 0 | 0 | 5 |
|
|
17
|
+
| in_process | 1 | 1 | 1 | 20 |
|
|
18
|
+
| redis | 1 | 2 | 1 | 20 |
|
|
19
|
+
| redis | 1 | 10 | 18 | 120 |
|
|
20
|
+
|
|
21
|
+
Scenario: Unexpected rate limiting implementation generates an error
|
|
22
|
+
Given the following environment variables are set:
|
|
23
|
+
| name | value |
|
|
24
|
+
| JAT_RATE_LIMIT_IMPLEMENTATION | UNKNOWN IMPLEMENTATION |
|
|
25
|
+
When I run `rate-limited-jira-tool`
|
|
26
|
+
Then it should fail with:
|
|
27
|
+
"""
|
|
28
|
+
unknown rate limiting implementation
|
|
29
|
+
"""
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
BUFFER_TIME_IN_SECONDS = 10
|
|
4
|
+
|
|
5
|
+
Given(/^the following environment variables are set:$/) do |table|
|
|
6
|
+
table.hashes.each do |row|
|
|
7
|
+
set_environment_variable(row.fetch("name"), row.fetch("value"))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Then(/^successfully running `(.*)` takes between (.*) and (.*) seconds$/) do |cmd, min, max|
|
|
12
|
+
start_time = Time.now
|
|
13
|
+
run_command_and_stop(cmd, fail_on_error: true, exit_timeout: max.to_i + BUFFER_TIME_IN_SECONDS)
|
|
14
|
+
end_time = Time.now
|
|
15
|
+
expect(end_time - start_time).to be_between(min.to_i, max.to_i)
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ruby-limiter"
|
|
4
|
+
|
|
5
|
+
module RateLimitedJira
|
|
6
|
+
class Client
|
|
7
|
+
class InProcessBased < Client
|
|
8
|
+
def rate_limit(&block)
|
|
9
|
+
rate_queue.shift
|
|
10
|
+
|
|
11
|
+
block.call
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def rate_queue
|
|
15
|
+
@rate_queue ||=
|
|
16
|
+
Limiter::RateQueue.new(rate_limit_per_interval, interval: rate_interval_in_seconds)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ratelimit"
|
|
4
|
+
require "redis"
|
|
5
|
+
|
|
6
|
+
module RateLimitedJira
|
|
7
|
+
class Client
|
|
8
|
+
class RedisBased < Client
|
|
9
|
+
RATE_LIMITER_KEY = "rate_limited_jira_api_requests"
|
|
10
|
+
|
|
11
|
+
def rate_limit(&block)
|
|
12
|
+
rate_limiter.exec_within_threshold(RATE_LIMITER_KEY,
|
|
13
|
+
interval: rate_interval_in_seconds,
|
|
14
|
+
threshold: rate_limit_per_interval) do
|
|
15
|
+
response = block.call
|
|
16
|
+
|
|
17
|
+
rate_limiter.add(RATE_LIMITER_KEY)
|
|
18
|
+
|
|
19
|
+
response
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def rate_limiter
|
|
24
|
+
self.class.rate_limiter(RATE_LIMITER_KEY, rate_interval_in_seconds)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.rate_limiter(rate_limiter_key, rate_interval)
|
|
28
|
+
@rate_limiter ||= Ratelimit.new(rate_limiter_key, bucket_interval: rate_interval)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "jira-ruby"
|
|
4
|
+
|
|
5
|
+
module RateLimitedJira
|
|
6
|
+
class Client < JIRA::Client
|
|
7
|
+
NO_RATE_LIMIT_PER_INTERVAL = 0
|
|
8
|
+
NO_RATE_INTERVAL_IN_SECONDS = 0
|
|
9
|
+
|
|
10
|
+
def self.build(jira_options, rate_limit_per_interval: 0, rate_interval_in_seconds: 0, implementation: :in_process)
|
|
11
|
+
implementation_class_for(implementation)
|
|
12
|
+
.new(jira_options,
|
|
13
|
+
rate_limit_per_interval: rate_limit_per_interval,
|
|
14
|
+
rate_interval_in_seconds: rate_interval_in_seconds)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.implementation_class_for(implementation)
|
|
18
|
+
impl = implementation.to_s.then { |s| s.empty? ? :in_process : s.to_sym }
|
|
19
|
+
case impl
|
|
20
|
+
when :in_process then InProcessBased
|
|
21
|
+
when :redis then RedisBased
|
|
22
|
+
else
|
|
23
|
+
raise ArgumentError, "#{implementation.inspect}: unknown rate limiting implementation. " \
|
|
24
|
+
"Valid options: :in_process, :redis"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
private_class_method :implementation_class_for
|
|
28
|
+
|
|
29
|
+
attr_reader :rate_interval_in_seconds, :rate_limit_per_interval
|
|
30
|
+
|
|
31
|
+
def initialize(options, rate_interval_in_seconds: 0, rate_limit_per_interval: 0)
|
|
32
|
+
super(options)
|
|
33
|
+
@rate_interval_in_seconds = rate_interval_in_seconds
|
|
34
|
+
@rate_limit_per_interval = rate_limit_per_interval
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
alias original_request request
|
|
38
|
+
|
|
39
|
+
def request(*)
|
|
40
|
+
if rate_limit_per_interval == NO_RATE_LIMIT_PER_INTERVAL
|
|
41
|
+
original_request(*)
|
|
42
|
+
else
|
|
43
|
+
rate_limit { original_request(*) }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def rate_limit(&)
|
|
48
|
+
raise NotImplementedError, "rate_limit must be implemented by a subclass"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
require_relative "client/in_process_based"
|
|
54
|
+
require_relative "client/redis_based"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe RateLimitedJira::Client::InProcessBased do
|
|
4
|
+
def build_client
|
|
5
|
+
described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
let(:client) { build_client }
|
|
9
|
+
let(:rate_interval_in_seconds) { 2 }
|
|
10
|
+
let(:rate_limit_per_interval) { 1 }
|
|
11
|
+
|
|
12
|
+
describe "#rate_limit" do
|
|
13
|
+
let(:rate_queue) { instance_double(Limiter::RateQueue) }
|
|
14
|
+
|
|
15
|
+
it "properly initializes the rate queue" do
|
|
16
|
+
allow(Limiter::RateQueue)
|
|
17
|
+
.to receive(:new).with(rate_limit_per_interval, interval: rate_interval_in_seconds)
|
|
18
|
+
.and_return(rate_queue)
|
|
19
|
+
|
|
20
|
+
allow(rate_queue).to receive(:shift)
|
|
21
|
+
|
|
22
|
+
expect(client.rate_limit { :do_nothing }).to eq(:do_nothing)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "when rate limiting multiple requests" do
|
|
26
|
+
let(:rate_limit_4_calls_to_original_request_code) do
|
|
27
|
+
4.times { client.rate_limit { client.original_request(:get, "/path/to/resource") } }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
before do
|
|
31
|
+
allow(client).to receive(:original_request).with(:get, "/path/to/resource")
|
|
32
|
+
allow(client).to receive_messages(rate_queue: rate_queue)
|
|
33
|
+
allow(rate_queue).to receive(:shift)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "shifts the queue and performs the request call" do
|
|
37
|
+
rate_limit_4_calls_to_original_request_code
|
|
38
|
+
|
|
39
|
+
expect(rate_queue).to have_received(:shift).exactly(4).times
|
|
40
|
+
expect(client).to have_received(:original_request).exactly(4).times
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
describe "#rate_queue" do
|
|
45
|
+
let(:another_client) { build_client }
|
|
46
|
+
|
|
47
|
+
it "creating a second client returns another queue" do
|
|
48
|
+
expect(another_client.rate_queue).not_to equal(client.rate_queue)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe RateLimitedJira::Client::RedisBased do
|
|
4
|
+
describe "#rate_limit" do
|
|
5
|
+
let(:client) { described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:) }
|
|
6
|
+
|
|
7
|
+
let(:rate_interval_in_seconds) { 2 }
|
|
8
|
+
let(:rate_limit_per_interval) { 1 }
|
|
9
|
+
let(:rate_limiter) { instance_double(Ratelimit) }
|
|
10
|
+
|
|
11
|
+
let(:rate_limit_4_calls_to_original_request_code) do
|
|
12
|
+
4.times { client.rate_limit { client.original_request(:get, "/path/to/resource") } }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
before do
|
|
16
|
+
allow(described_class).to receive_messages(rate_limiter: rate_limiter)
|
|
17
|
+
allow(client).to receive(:original_request)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "uses :exec_within_threshold to control rate limiting" do
|
|
21
|
+
allow(rate_limiter).to receive(:exec_within_threshold)
|
|
22
|
+
|
|
23
|
+
rate_limit_4_calls_to_original_request_code
|
|
24
|
+
|
|
25
|
+
expect(rate_limiter)
|
|
26
|
+
.to have_received(:exec_within_threshold)
|
|
27
|
+
.with(RateLimitedJira::Client::RedisBased::RATE_LIMITER_KEY,
|
|
28
|
+
{ interval: rate_interval_in_seconds, threshold: rate_limit_per_interval })
|
|
29
|
+
.exactly(4).times
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "keeps track of rate limiter key calls" do
|
|
33
|
+
allow(rate_limiter).to receive(:exec_within_threshold).and_yield
|
|
34
|
+
allow(rate_limiter).to receive(:add)
|
|
35
|
+
|
|
36
|
+
rate_limit_4_calls_to_original_request_code
|
|
37
|
+
|
|
38
|
+
expect(rate_limiter)
|
|
39
|
+
.to have_received(:add)
|
|
40
|
+
.with(RateLimitedJira::Client::RedisBased::RATE_LIMITER_KEY)
|
|
41
|
+
.exactly(4).times
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec.describe RateLimitedJira::Client do
|
|
4
|
+
describe ".build" do
|
|
5
|
+
let(:jira_options) { {} }
|
|
6
|
+
|
|
7
|
+
context "when implementation is unspecified" do
|
|
8
|
+
subject(:result) { described_class.build(jira_options) }
|
|
9
|
+
|
|
10
|
+
it { expect(result).to be_a(RateLimitedJira::Client::InProcessBased) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "when implementation is :in_process" do
|
|
14
|
+
subject(:result) { described_class.build(jira_options, implementation: :in_process) }
|
|
15
|
+
|
|
16
|
+
it { expect(result).to be_a(RateLimitedJira::Client::InProcessBased) }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
context "when implementation is blank" do
|
|
20
|
+
subject(:result) { described_class.build(jira_options, implementation: "") }
|
|
21
|
+
|
|
22
|
+
it { expect(result).to be_a(RateLimitedJira::Client::InProcessBased) }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context "when implementation is :redis" do
|
|
26
|
+
subject(:result) { described_class.build(jira_options, implementation: :redis) }
|
|
27
|
+
|
|
28
|
+
it { expect(result).to be_a(RateLimitedJira::Client::RedisBased) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "when implementation is unknown" do
|
|
32
|
+
subject(:result) { described_class.build(jira_options, implementation: :unknown) }
|
|
33
|
+
|
|
34
|
+
it do
|
|
35
|
+
expect { result }
|
|
36
|
+
.to raise_error(ArgumentError, /:unknown.*unknown rate limiting implementation/)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
RSpec.shared_examples "a rate limited client" do
|
|
42
|
+
before do
|
|
43
|
+
allow(client).to receive_messages(original_request: :response)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "returns the response" do
|
|
47
|
+
expect(client.request(:get, "/path/to/resource")).to eq(:response)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "calls the original request method" do
|
|
51
|
+
client.request(:get, "/path/to/resource")
|
|
52
|
+
|
|
53
|
+
expect(client).to have_received(:original_request).with(:get, "/path/to/resource")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe "#request" do
|
|
58
|
+
let(:client) { described_class.new({}, rate_interval_in_seconds:, rate_limit_per_interval:) }
|
|
59
|
+
|
|
60
|
+
context "when rate limiting is disabled" do
|
|
61
|
+
let(:rate_interval_in_seconds) { 0 }
|
|
62
|
+
let(:rate_limit_per_interval) { 0 }
|
|
63
|
+
|
|
64
|
+
it_behaves_like "a rate limited client"
|
|
65
|
+
|
|
66
|
+
it "does not use the rate limiter" do
|
|
67
|
+
allow(client).to receive(:original_request).with(:get, "/path/to/resource")
|
|
68
|
+
expect(client).not_to receive(:rate_limit)
|
|
69
|
+
|
|
70
|
+
client.request(:get, "/path/to/resource")
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
context "when rate limiting is enabled" do
|
|
75
|
+
let(:rate_interval_in_seconds) { 2 }
|
|
76
|
+
let(:rate_limit_per_interval) { 1 }
|
|
77
|
+
|
|
78
|
+
it_behaves_like "a rate limited client" do
|
|
79
|
+
before { allow(client).to receive(:rate_limit).and_yield }
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "uses the rate limiter" do
|
|
83
|
+
allow(client).to receive(:original_request).with(:get, "/path/to/resource")
|
|
84
|
+
expect(client).to receive(:rate_limit).and_yield
|
|
85
|
+
|
|
86
|
+
client.request(:get, "/path/to/resource")
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rate_limited_jira"
|
|
4
|
+
|
|
5
|
+
RSpec.configure do |config|
|
|
6
|
+
config.disable_monkey_patching!
|
|
7
|
+
|
|
8
|
+
config.expect_with :rspec do |expectations|
|
|
9
|
+
expectations.syntax = :expect
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
config.mock_with :rspec do |mocks|
|
|
13
|
+
mocks.verify_partial_doubles = true
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rate-limited-jira-ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Christophe Broult
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: jira-ruby
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: ratelimit
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: redis
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: ruby-limiter
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
email:
|
|
69
|
+
- cbroult@yahoo.com
|
|
70
|
+
executables: []
|
|
71
|
+
extensions: []
|
|
72
|
+
extra_rdoc_files: []
|
|
73
|
+
files:
|
|
74
|
+
- ".rspec"
|
|
75
|
+
- ".rubocop.yml"
|
|
76
|
+
- CHANGELOG.md
|
|
77
|
+
- Gemfile
|
|
78
|
+
- Gemfile.lock
|
|
79
|
+
- Rakefile
|
|
80
|
+
- bin/rate-limited-jira-tool
|
|
81
|
+
- cucumber.yml
|
|
82
|
+
- features/control_http_request_rate_limit.feature
|
|
83
|
+
- features/step_definitions/execution_context_steps.rb
|
|
84
|
+
- features/support/env.rb
|
|
85
|
+
- features/support/hooks.rb
|
|
86
|
+
- lib/rate_limited_jira.rb
|
|
87
|
+
- lib/rate_limited_jira/client.rb
|
|
88
|
+
- lib/rate_limited_jira/client/in_process_based.rb
|
|
89
|
+
- lib/rate_limited_jira/client/redis_based.rb
|
|
90
|
+
- lib/rate_limited_jira/version.rb
|
|
91
|
+
- spec/rate_limited_jira/client/in_process_based_spec.rb
|
|
92
|
+
- spec/rate_limited_jira/client/redis_based_spec.rb
|
|
93
|
+
- spec/rate_limited_jira/client_spec.rb
|
|
94
|
+
- spec/spec_helper.rb
|
|
95
|
+
homepage: https://github.com/cbroult/rate-limited-jira-ruby
|
|
96
|
+
licenses:
|
|
97
|
+
- MIT
|
|
98
|
+
metadata:
|
|
99
|
+
allowed_push_host: https://rubygems.org
|
|
100
|
+
homepage_uri: https://github.com/cbroult/rate-limited-jira-ruby
|
|
101
|
+
source_code_uri: https://github.com/cbroult/rate-limited-jira-ruby
|
|
102
|
+
changelog_uri: https://github.com/cbroult/rate-limited-jira-ruby/blob/main/CHANGELOG.md
|
|
103
|
+
rubygems_mfa_required: 'true'
|
|
104
|
+
rdoc_options: []
|
|
105
|
+
require_paths:
|
|
106
|
+
- lib
|
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: 3.2.0
|
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
requirements: []
|
|
118
|
+
rubygems_version: 4.0.10
|
|
119
|
+
specification_version: 4
|
|
120
|
+
summary: Transparent rate-limiting wrapper around jira-ruby's JIRA::Client.
|
|
121
|
+
test_files: []
|