fbe 0.24.4 → 0.25.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 +1 -1
- data/Gemfile.lock +2 -2
- data/lib/fbe/middleware/sqlite_store.rb +26 -1
- data/lib/fbe/octo.rb +2 -1
- data/lib/fbe.rb +1 -1
- data/test/fbe/middleware/test_sqlite_store.rb +106 -0
- data/test/fbe/test_octo.rb +49 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7615e3d4257252fcc987acca6caeb717c328a79edfd563eb67e704be741cef2
|
4
|
+
data.tar.gz: 3a256df746d860b44a2d6f3d44cad34690dce87ca9743227b1e114edfcdff5bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3bf3c8e87ea0f0bc52a51d7f89c60bc1dc5e67b78eccac3ce9de0ac80f73406b4b8fec508d7c83c10b42a07fbe594505d3a5cddc80fd8a35e00c292a80eaf911
|
7
|
+
data.tar.gz: 2b4b12a737a6f810d304727f649faa7dceaf224c317c7d79d01705af5442a9510ede60d9f63f9afc433b6b3ce3a4bd49a7c30934d2ee9b6c90d26aebbb1a9d84
|
data/Gemfile
CHANGED
@@ -17,7 +17,7 @@ gem 'rubocop-minitest', '~>0.38', require: false
|
|
17
17
|
gem 'rubocop-performance', '~>1.25', require: false
|
18
18
|
gem 'rubocop-rake', '~>0.7', require: false
|
19
19
|
gem 'simplecov', '~>0.22', require: false
|
20
|
-
gem 'simplecov-cobertura', '~>
|
20
|
+
gem 'simplecov-cobertura', '~>3.0', require: false
|
21
21
|
gem 'veils', '~>0.4', require: false
|
22
22
|
gem 'webmock', '~>3.25', require: false
|
23
23
|
gem 'yard', '~>0.9', require: false
|
data/Gemfile.lock
CHANGED
@@ -223,7 +223,7 @@ GEM
|
|
223
223
|
docile (~> 1.1)
|
224
224
|
simplecov-html (~> 0.11)
|
225
225
|
simplecov_json_formatter (~> 0.1)
|
226
|
-
simplecov-cobertura (
|
226
|
+
simplecov-cobertura (3.0.0)
|
227
227
|
rexml
|
228
228
|
simplecov (~> 0.19)
|
229
229
|
simplecov-html (0.13.1)
|
@@ -277,7 +277,7 @@ DEPENDENCIES
|
|
277
277
|
rubocop-performance (~> 1.25)
|
278
278
|
rubocop-rake (~> 0.7)
|
279
279
|
simplecov (~> 0.22)
|
280
|
-
simplecov-cobertura (~>
|
280
|
+
simplecov-cobertura (~> 3.0)
|
281
281
|
veils (~> 0.4)
|
282
282
|
webmock (~> 3.25)
|
283
283
|
yard (~> 0.9)
|
@@ -54,9 +54,10 @@ class Fbe::Middleware::SqliteStore
|
|
54
54
|
# @param maxsize [Integer] Maximum database size in bytes (optional, defaults to 10MB)
|
55
55
|
# @param maxvsize [Integer] Maximum size in bytes of a single value (optional, defaults to 10Kb)
|
56
56
|
# @param ttl [Integer, nil] lifetime of keys in hours
|
57
|
+
# @param cache_min_age [Integer, nil] age which will could be overwritten in cache-control header
|
57
58
|
# @raise [ArgumentError] If path is nil/empty, directory doesn't exist, version is nil/empty,
|
58
59
|
# or ttl is not nil or not Integer or not positive
|
59
|
-
def initialize(path, version, loog: Loog::NULL, maxsize: '10Mb', maxvsize: '10Kb', ttl: nil)
|
60
|
+
def initialize(path, version, loog: Loog::NULL, maxsize: '10Mb', maxvsize: '10Kb', ttl: nil, cache_min_age: nil)
|
60
61
|
raise ArgumentError, 'Database path cannot be nil or empty' if path.nil? || path.empty?
|
61
62
|
dir = File.dirname(path)
|
62
63
|
raise ArgumentError, "Directory #{dir} does not exist" unless File.directory?(dir)
|
@@ -68,6 +69,10 @@ class Fbe::Middleware::SqliteStore
|
|
68
69
|
@maxvsize = Filesize.from(maxvsize.to_s).to_i
|
69
70
|
raise ArgumentError, 'TTL can be nil or Integer > 0' if !ttl.nil? && !(ttl.is_a?(Integer) && ttl.positive?)
|
70
71
|
@ttl = ttl
|
72
|
+
if !cache_min_age.nil? && !(cache_min_age.is_a?(Integer) && cache_min_age.positive?)
|
73
|
+
raise ArgumentError, 'Cache min age can be nil or Integer > 0'
|
74
|
+
end
|
75
|
+
@cache_min_age = cache_min_age
|
71
76
|
end
|
72
77
|
|
73
78
|
# Read a value from the cache.
|
@@ -106,6 +111,26 @@ class Fbe::Middleware::SqliteStore
|
|
106
111
|
req = JSON.parse(vv[0])
|
107
112
|
req['method'] != 'get'
|
108
113
|
end
|
114
|
+
if @cache_min_age && value.is_a?(Array) && value[0].is_a?(Array) && value[0].size > 1
|
115
|
+
begin
|
116
|
+
resp = JSON.parse(value[0][1])
|
117
|
+
rescue TypeError, JSON::ParserError => e
|
118
|
+
@loog.info("Failed to parse response to rewrite the cache age: #{e.message}")
|
119
|
+
resp = nil
|
120
|
+
end
|
121
|
+
cache_control = resp.dig('response_headers', 'cache-control') if resp.is_a?(Hash)
|
122
|
+
if cache_control && !cache_control.empty?
|
123
|
+
%w[max-age s-maxage].each do |key|
|
124
|
+
age = cache_control.scan(/#{key}=(\d+)/i).first&.first&.to_i
|
125
|
+
if age
|
126
|
+
age = [age, @cache_min_age].max
|
127
|
+
cache_control = cache_control.sub(/#{key}=(\d+)/, "#{key}=#{age}")
|
128
|
+
end
|
129
|
+
end
|
130
|
+
resp['response_headers']['cache-control'] = cache_control
|
131
|
+
value[0][1] = JSON.dump(resp)
|
132
|
+
end
|
133
|
+
end
|
109
134
|
value = Zlib::Deflate.deflate(JSON.dump(value))
|
110
135
|
return if value.bytesize > @maxvsize
|
111
136
|
perform do |t|
|
data/lib/fbe/octo.rb
CHANGED
@@ -93,8 +93,9 @@ def Fbe.octo(options: $options, global: $global, loog: $loog)
|
|
93
93
|
if options.sqlite_cache
|
94
94
|
maxsize = Filesize.from(options.sqlite_cache_maxsize || '100M').to_i
|
95
95
|
maxvsize = Filesize.from(options.sqlite_cache_maxvsize || '100K').to_i
|
96
|
+
cache_min_age = options.sqlite_cache_min_age&.to_i
|
96
97
|
store = Fbe::Middleware::SqliteStore.new(
|
97
|
-
options.sqlite_cache, Fbe::VERSION, loog:, maxsize:, maxvsize:, ttl: 24
|
98
|
+
options.sqlite_cache, Fbe::VERSION, loog:, maxsize:, maxvsize:, ttl: 24, cache_min_age:
|
98
99
|
)
|
99
100
|
loog.info(
|
100
101
|
"Using HTTP cache in SQLite file: #{store.path} (" \
|
data/lib/fbe.rb
CHANGED
@@ -330,6 +330,94 @@ class SqliteStoreTest < Fbe::Test
|
|
330
330
|
end
|
331
331
|
end
|
332
332
|
|
333
|
+
def test_set_correct_cache_min_age
|
334
|
+
with_tmpfile('c.db') do |f|
|
335
|
+
s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: nil)
|
336
|
+
refute_nil(s)
|
337
|
+
s = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 600)
|
338
|
+
refute_nil(s)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def test_set_incorrect_cache_min_age
|
343
|
+
with_tmpfile('c.db') do |f|
|
344
|
+
msg = 'Cache min age can be nil or Integer > 0'
|
345
|
+
[0, -50, 120.0, '120', Object.new].each do |cache_min_age|
|
346
|
+
ex =
|
347
|
+
assert_raises(ArgumentError) do
|
348
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age:)
|
349
|
+
end
|
350
|
+
assert_equal(msg, ex.message)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
def test_not_overwrite_cache_control
|
356
|
+
with_tmpfile('t.db') do |f|
|
357
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 30).then do |store|
|
358
|
+
store.write(
|
359
|
+
'test1',
|
360
|
+
faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
|
361
|
+
)
|
362
|
+
store.write(
|
363
|
+
'test2',
|
364
|
+
faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=30, s-maxage=30' } })
|
365
|
+
)
|
366
|
+
store.write(
|
367
|
+
'test3',
|
368
|
+
faraday_value(resp: { 'response_headers' => { 'content-type' => 'application/json; charset=utf-8' } })
|
369
|
+
)
|
370
|
+
store.write('test4', faraday_value(resp: { 'status' => 200, 'body' => '{"some":"value"}' }))
|
371
|
+
store.write('test5', faraday_value(resp: {}))
|
372
|
+
store.write('test6', [[JSON.dump({ 'method' => 'get' }), 1]])
|
373
|
+
store.write('test7', faraday_value(resp: 'some string'))
|
374
|
+
store.write('test8', faraday_value(resp: nil))
|
375
|
+
end
|
376
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog).then do |store|
|
377
|
+
assert_equal(
|
378
|
+
'public, max-age=60, s-maxage=60',
|
379
|
+
JSON.parse(store.read('test1')[0][1]).dig('response_headers', 'cache-control')
|
380
|
+
)
|
381
|
+
assert_equal(
|
382
|
+
'public, max-age=30, s-maxage=30',
|
383
|
+
JSON.parse(store.read('test2')[0][1]).dig('response_headers', 'cache-control')
|
384
|
+
)
|
385
|
+
assert_nil(JSON.parse(store.read('test3')[0][1]).dig('response_headers', 'cache-control'))
|
386
|
+
assert_nil(JSON.parse(store.read('test4')[0][1]).dig('response_headers', 'cache-control'))
|
387
|
+
assert_nil(JSON.parse(store.read('test5')[0][1]).dig('response_headers', 'cache-control'))
|
388
|
+
assert_equal(1, store.read('test6')[0][1])
|
389
|
+
assert_equal(JSON.dump('some string'), store.read('test7')[0][1])
|
390
|
+
assert_nil(store.read('test8')[0][1])
|
391
|
+
end
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def test_overwrite_cache_control
|
396
|
+
with_tmpfile('t.db') do |f|
|
397
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 300).then do |store|
|
398
|
+
store.write(
|
399
|
+
'test1',
|
400
|
+
faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
|
401
|
+
)
|
402
|
+
end
|
403
|
+
Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog, cache_min_age: 1555).then do |store|
|
404
|
+
store.write(
|
405
|
+
'test2',
|
406
|
+
faraday_value(resp: { 'response_headers' => { 'cache-control' => 'public, max-age=60, s-maxage=60' } })
|
407
|
+
)
|
408
|
+
end
|
409
|
+
store = Fbe::Middleware::SqliteStore.new(f, '0.0.1', loog: fake_loog)
|
410
|
+
assert_equal(
|
411
|
+
'public, max-age=300, s-maxage=300',
|
412
|
+
JSON.parse(store.read('test1')[0][1]).dig('response_headers', 'cache-control')
|
413
|
+
)
|
414
|
+
assert_equal(
|
415
|
+
'public, max-age=1555, s-maxage=1555',
|
416
|
+
JSON.parse(store.read('test2')[0][1]).dig('response_headers', 'cache-control')
|
417
|
+
)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
333
421
|
private
|
334
422
|
|
335
423
|
def with_tmpfile(name = 'test.db', &)
|
@@ -337,4 +425,22 @@ class SqliteStoreTest < Fbe::Test
|
|
337
425
|
yield File.expand_path(name, dir)
|
338
426
|
end
|
339
427
|
end
|
428
|
+
|
429
|
+
def faraday_value(
|
430
|
+
req: {
|
431
|
+
'method' => 'get',
|
432
|
+
'url' => 'https://example.com/test',
|
433
|
+
'headers' => { 'Content-Type' => 'application/json' }
|
434
|
+
},
|
435
|
+
resp: {
|
436
|
+
'status' => 200,
|
437
|
+
'body' => '{"some":"value"}',
|
438
|
+
'response_headers' => { 'content-type' => 'application/json; charset=utf-8' }
|
439
|
+
}
|
440
|
+
)
|
441
|
+
value = []
|
442
|
+
value << JSON.dump(req) if req
|
443
|
+
value << JSON.dump(resp) if resp
|
444
|
+
[value]
|
445
|
+
end
|
340
446
|
end
|
data/test/fbe/test_octo.rb
CHANGED
@@ -709,4 +709,53 @@ class TestOcto < Fbe::Test
|
|
709
709
|
end
|
710
710
|
end
|
711
711
|
end
|
712
|
+
|
713
|
+
def test_octo_with_set_sqlite_cache_min_age
|
714
|
+
WebMock.disable_net_connect!
|
715
|
+
now = Time.now
|
716
|
+
stub_request(:get, 'https://api.github.com/rate_limit')
|
717
|
+
.to_return(
|
718
|
+
status: 200, headers: { 'Content-Type' => 'application/json', 'X-RateLimit-Remaining' => '5000' },
|
719
|
+
body: { 'rate' => { 'limit' => 5000, 'remaining' => 5000, 'reset' => 1_672_531_200 } }.to_json
|
720
|
+
)
|
721
|
+
Dir.mktmpdir do |dir|
|
722
|
+
sqlite_cache = File.expand_path('t.db', dir)
|
723
|
+
options = Judges::Options.new({ 'sqlite_cache' => sqlite_cache, 'sqlite_cache_min_age' => 120 })
|
724
|
+
o = Fbe.octo(loog: fake_loog, global: {}, options:)
|
725
|
+
stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
|
726
|
+
status: 200,
|
727
|
+
body: { id: 798_641_472, name: 'factbase' }.to_json,
|
728
|
+
headers: {
|
729
|
+
'Date' => now.httpdate,
|
730
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
731
|
+
'Cache-Control' => 'public, max-age=60, s-maxage=60',
|
732
|
+
'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
|
733
|
+
'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
|
734
|
+
}
|
735
|
+
).times(1).then.to_raise('no more request to /repositories/798641472')
|
736
|
+
Time.stub(:now, now) do
|
737
|
+
assert_equal('factbase', o.repo(798_641_472)['name'])
|
738
|
+
end
|
739
|
+
Time.stub(:now, now + 50) do
|
740
|
+
assert_equal('factbase', o.repo(798_641_472)['name'])
|
741
|
+
end
|
742
|
+
Time.stub(:now, now + 100) do
|
743
|
+
assert_equal('factbase', o.repo(798_641_472)['name'])
|
744
|
+
end
|
745
|
+
stub_request(:get, 'https://api.github.com/repositories/798641472').to_return(
|
746
|
+
status: 200,
|
747
|
+
body: { id: 798_641_472, name: 'factbase_changed' }.to_json,
|
748
|
+
headers: {
|
749
|
+
'Date' => (now + 120).httpdate,
|
750
|
+
'Content-Type' => 'application/json; charset=utf-8',
|
751
|
+
'Cache-Control' => 'public, max-age=60, s-maxage=60',
|
752
|
+
'Etag' => 'W/"f5f1ea995fd7266816f681aca5a81f539420c469070a47568bebdaa3055487bc"',
|
753
|
+
'Last-Modified' => 'Fri, 04 Jul 2025 13:39:42 GMT'
|
754
|
+
}
|
755
|
+
)
|
756
|
+
Time.stub(:now, now + 120) do
|
757
|
+
assert_equal('factbase_changed', o.repo(798_641_472)['name'])
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
712
761
|
end
|