dhs 1.3.0 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -1
- data/lib/dhs/concerns/record/pagination.rb +4 -1
- data/lib/dhs/concerns/record/request.rb +13 -10
- data/lib/dhs/pagination/link.rb +4 -0
- data/lib/dhs/pagination/next_offset.rb +28 -0
- data/lib/dhs/problems/errors.rb +1 -1
- data/lib/dhs/version.rb +1 -1
- data/lib/dhs.rb +1 -0
- data/spec/concerns/record/request_spec.rb +4 -4
- data/spec/graphql/main_spec.rb +3 -1
- data/spec/pagination/next_offset_spec.rb +31 -0
- data/spec/record/endpoint_options_spec.rb +6 -6
- data/spec/record/includes_spec.rb +5 -5
- data/spec/record/paginatable_collection_spec.rb +1 -1
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 690186fffb1221bbc432b5a502bdfecb895b1f117e42602e6bef6b2c55cd7bd1
|
4
|
+
data.tar.gz: 7f544c63db8a4db092a6c3980b3cb2f437879876db145a6ac7d8c6cfc7647a05
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '087dacf286f24b6860419759c5ea7cd443d69e2cd8f4900b2ee17fdf93d2d9f55918534ec0aff8294e6a4906c2dad51d3c4eb2b6ad8b3f19111a4133ed15f6c5'
|
7
|
+
data.tar.gz: f14b0dad8dffb72c65cd6b785469a59c590a99187639e554865e99bcf8e696434f262c4e95d64b1241bfd997180ef278ce2f0f0f5b91c6e5f11a673466f7eff8
|
data/README.md
CHANGED
@@ -164,7 +164,7 @@ You can use DHS also to fetch records from GraphQL Endpoints:
|
|
164
164
|
|
165
165
|
class Record < DHS::Record
|
166
166
|
|
167
|
-
configuration
|
167
|
+
configuration items_key: [:data, :ethereum, :address, 0, :balances]
|
168
168
|
|
169
169
|
endpoint 'https://graphql.bitquery.io/',
|
170
170
|
graphql: {
|
@@ -1386,6 +1386,51 @@ Sequentially:
|
|
1386
1386
|
GET https://service.example.com/records?from_record_id=xcaoXBmuMyFFEcFDSgNgDQ&limit=100
|
1387
1387
|
```
|
1388
1388
|
|
1389
|
+
##### Pagination strategy: next_offset
|
1390
|
+
|
1391
|
+
The `next_offset` strategy continuously follows in-response offset information to following pages until the last page is reached (indicated by next offset being either empty or 0).
|
1392
|
+
|
1393
|
+
*WARNING*
|
1394
|
+
|
1395
|
+
Loading all pages from a resource paginated with next_offset only can result in very poor performance, as pages can only be loaded sequentially!
|
1396
|
+
|
1397
|
+
```ruby
|
1398
|
+
# app/models/record.rb
|
1399
|
+
|
1400
|
+
class Search < DHS::Record
|
1401
|
+
configuration pagination_strategy: 'next_offset'
|
1402
|
+
|
1403
|
+
endpoint '{+service}/assets'
|
1404
|
+
end
|
1405
|
+
```
|
1406
|
+
|
1407
|
+
```ruby
|
1408
|
+
# app/controllers/some_controller.rb
|
1409
|
+
|
1410
|
+
Record.all
|
1411
|
+
|
1412
|
+
```
|
1413
|
+
```
|
1414
|
+
GET https://service.example.com/assets?limit=100
|
1415
|
+
{
|
1416
|
+
items: [{...}, ...],
|
1417
|
+
limit: 10,
|
1418
|
+
next_offset: 29
|
1419
|
+
}
|
1420
|
+
GET https://service.example.com/assets?offset=29
|
1421
|
+
{
|
1422
|
+
items: [{...}, ...],
|
1423
|
+
limit: 10,
|
1424
|
+
next_offset: 39
|
1425
|
+
}
|
1426
|
+
GET https://service.example.com/assets?offset=39
|
1427
|
+
{
|
1428
|
+
items: [{...}, ...],
|
1429
|
+
limit: 10,
|
1430
|
+
next_offset: 0
|
1431
|
+
}
|
1432
|
+
```
|
1433
|
+
|
1389
1434
|
#### Pagination keys
|
1390
1435
|
|
1391
1436
|
##### limit_key
|
@@ -30,6 +30,8 @@ class DHS::Record
|
|
30
30
|
DHS::Pagination::Start
|
31
31
|
when :link
|
32
32
|
DHS::Pagination::Link
|
33
|
+
when :next_offset
|
34
|
+
DHS::Pagination::NextOffset
|
33
35
|
else
|
34
36
|
DHS::Pagination::Offset
|
35
37
|
end
|
@@ -43,7 +45,8 @@ class DHS::Record
|
|
43
45
|
def paginated?(raw)
|
44
46
|
raw.is_a?(Hash) && (
|
45
47
|
raw.dig(*total_key).present? ||
|
46
|
-
raw.dig(*limit_key(:body)).present?
|
48
|
+
raw.dig(*limit_key(:body)).present? ||
|
49
|
+
raw.dig(*pagination_key(:body)).present?
|
47
50
|
)
|
48
51
|
end
|
49
52
|
end
|
@@ -204,7 +204,7 @@ class DHS::Record
|
|
204
204
|
end
|
205
205
|
|
206
206
|
def load_and_merge_not_paginated_collection!(data, options)
|
207
|
-
return if data.
|
207
|
+
return if data.empty?
|
208
208
|
options = options.is_a?(Hash) ? options : {}
|
209
209
|
limit = options.dig(:params, limit_key(:parameter)) || pagination_class::DEFAULT_LIMIT
|
210
210
|
offset = options.dig(:params, pagination_key(:parameter)) || pagination_class::DEFAULT_OFFSET
|
@@ -230,14 +230,14 @@ class DHS::Record
|
|
230
230
|
end
|
231
231
|
|
232
232
|
def load_and_merge_paginated_collection!(data, options)
|
233
|
-
set_nested_data(data._raw, limit_key(:body), data.length) if data._raw.dig(*limit_key(:body)).blank? && !data.
|
233
|
+
set_nested_data(data._raw, limit_key(:body), data.length) if data._raw.dig(*limit_key(:body)).blank? && !data.empty?
|
234
234
|
pagination = data._record.pagination(data)
|
235
235
|
return data unless pagination.pages_left?
|
236
236
|
record = data._record
|
237
237
|
if pagination.parallel?
|
238
238
|
load_and_merge_parallel_requests!(record, data, pagination, options)
|
239
239
|
else
|
240
|
-
load_and_merge_sequential_requests!(record, data, options,
|
240
|
+
load_and_merge_sequential_requests!(record, data, options, pagination)
|
241
241
|
end
|
242
242
|
end
|
243
243
|
|
@@ -249,13 +249,16 @@ class DHS::Record
|
|
249
249
|
end
|
250
250
|
end
|
251
251
|
|
252
|
-
def load_and_merge_sequential_requests!(record, data, options,
|
253
|
-
warn '[WARNING] You are loading all pages from a resource paginated with
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
252
|
+
def load_and_merge_sequential_requests!(record, data, options, pagination)
|
253
|
+
warn '[WARNING] You are loading all pages from a resource paginated with sequential pagination.'
|
254
|
+
next_value = pagination.next(data._raw)
|
255
|
+
while next_value.present?
|
256
|
+
page_data = if next_value.is_a?(String) && next_value.match(/^http/)
|
257
|
+
record.request(options.except(:all).merge(url: next_value))
|
258
|
+
else
|
259
|
+
record.request(options.except(:all).merge(params: (options.dig(:params) || {}).merge(next_value)))
|
260
|
+
end
|
261
|
+
next_value = pagination.next(page_data._raw)
|
259
262
|
merge_batch_data_with_parent!(page_data, data, pagination)
|
260
263
|
end
|
261
264
|
end
|
data/lib/dhs/pagination/link.rb
CHANGED
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class DHS::Pagination::NextOffset < DHS::Pagination::Base
|
4
|
+
|
5
|
+
DEFAULT_OFFSET = 0
|
6
|
+
|
7
|
+
def total
|
8
|
+
data._raw.dig(*_record.items_key).count || 0
|
9
|
+
end
|
10
|
+
alias count total
|
11
|
+
|
12
|
+
def parallel?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def pages_left?
|
17
|
+
next_offset = data._raw.dig(*_record.pagination_key(:body))
|
18
|
+
next_offset.present? && !next_offset.zero?
|
19
|
+
end
|
20
|
+
|
21
|
+
def next(current)
|
22
|
+
next_value = current.dig(*_record.pagination_key(:body))
|
23
|
+
return if next_value.blank? || next_value.zero?
|
24
|
+
{
|
25
|
+
_record.pagination_key(:parameter) => current.dig(*_record.pagination_key(:body))
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
data/lib/dhs/problems/errors.rb
CHANGED
@@ -54,7 +54,7 @@ module DHS::Problems
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def messages_from_response(response = nil)
|
57
|
-
return {} if !response || !response.body.is_a?(String) || response.body.
|
57
|
+
return {} if !response || !response.body.is_a?(String) || response.body.empty?
|
58
58
|
json = JSON.parse(response.body)
|
59
59
|
parse_messages(json)
|
60
60
|
end
|
data/lib/dhs/version.rb
CHANGED
data/lib/dhs.rb
CHANGED
@@ -39,6 +39,7 @@ module DHS
|
|
39
39
|
autoload :Page, 'dhs/pagination/page'
|
40
40
|
autoload :TotalPages, 'dhs/pagination/total_pages'
|
41
41
|
autoload :OffsetPage, 'dhs/pagination/offset_page'
|
42
|
+
autoload :NextOffset, 'dhs/pagination/next_offset'
|
42
43
|
autoload :Start, 'dhs/pagination/start'
|
43
44
|
autoload :Link, 'dhs/pagination/link'
|
44
45
|
end
|
@@ -19,15 +19,15 @@ describe DHS::Record::Request do
|
|
19
19
|
|
20
20
|
it 'calls correct prepare method for a Hash' do
|
21
21
|
expect(subject).to receive(:prepare_option_for_include_all_request!)
|
22
|
-
.with(abc: 'def').and_return('ignore')
|
23
|
-
expect(subject.send(:prepare_options_for_include_all_request!, abc: 'def')).to eq(abc: 'def')
|
22
|
+
.with({ abc: 'def' }).and_return('ignore')
|
23
|
+
expect(subject.send(:prepare_options_for_include_all_request!, { abc: 'def' })).to eq(abc: 'def')
|
24
24
|
end
|
25
25
|
|
26
26
|
it 'calls correct prepare method for a Hash (collection)' do
|
27
27
|
expect(subject).to receive(:prepare_option_for_include_all_request!)
|
28
|
-
.with(abc: 'def').and_return('ignore')
|
28
|
+
.with({ abc: 'def' }).and_return('ignore')
|
29
29
|
expect(subject).to receive(:prepare_option_for_include_all_request!)
|
30
|
-
.with(hij: 'kel').and_return('ignore')
|
30
|
+
.with({ hij: 'kel' }).and_return('ignore')
|
31
31
|
expect(subject.send(:prepare_options_for_include_all_request!, [{ abc: 'def' }, { hij: 'kel' }]))
|
32
32
|
.to eq([{ abc: 'def' }, { hij: 'kel' }])
|
33
33
|
end
|
data/spec/graphql/main_spec.rb
CHANGED
@@ -88,11 +88,13 @@ describe 'main graphql support' do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
before do
|
91
|
+
DHC.config.placeholder('bitquery', 'https://graphql.bitquery.io/')
|
92
|
+
|
91
93
|
class Record < DHS::Record
|
92
94
|
|
93
95
|
configuration items_key: [:data, :ethereum, :address, 0, :balances]
|
94
96
|
|
95
|
-
endpoint '
|
97
|
+
endpoint '{+bitquery}',
|
96
98
|
graphql: {
|
97
99
|
query: %{
|
98
100
|
query ($network: EthereumNetwork!, $address: String!) {
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
describe DHS::Record do
|
6
|
+
context 'pagination' do
|
7
|
+
def stub_api_request(next_offset:, items: [], offset: nil)
|
8
|
+
stub_request(:get, ['http://depay.fi/v2/transactions?limit=100', offset ? "offset=#{offset}" : nil].compact.join('&'))
|
9
|
+
.to_return(body: { items: items, next_offset: next_offset }.to_json)
|
10
|
+
end
|
11
|
+
|
12
|
+
let!(:requests) do
|
13
|
+
stub_api_request(items: (0...100).to_a, next_offset: 99)
|
14
|
+
stub_api_request(items: (100...200).to_a, offset: 99, next_offset: 199)
|
15
|
+
stub_api_request(items: (200...300).to_a, offset: 199, next_offset: 0)
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
class Transaction < DHS::Record
|
20
|
+
configuration pagination_strategy: :next_offset, pagination_key: { body: :next_offset, parameter: :offset }
|
21
|
+
|
22
|
+
endpoint 'http://depay.fi/v2/transactions'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'fetches all the pages' do
|
27
|
+
transactions = Transaction.all.fetch
|
28
|
+
expect(transactions.to_a).to eq (0...300).to_a
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -16,12 +16,12 @@ describe DHS::Record do
|
|
16
16
|
|
17
17
|
it 'uses the options that are configured for an endpoint' do
|
18
18
|
expect(DHC).to receive(:request)
|
19
|
-
.with(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
.with({
|
20
|
+
cache_expires_in: 1.day,
|
21
|
+
retry: 2,
|
22
|
+
cache: true,
|
23
|
+
url: 'backend/v2/feedbacks/1'
|
24
|
+
}).and_call_original
|
25
25
|
|
26
26
|
stub_request(:get, 'http://backend/v2/feedbacks/1').to_return(status: 200)
|
27
27
|
Record.find(1)
|
@@ -167,7 +167,7 @@ describe DHS::Record do
|
|
167
167
|
.includes(:users, contracts: :products)
|
168
168
|
.find(1)
|
169
169
|
end).to output(
|
170
|
-
%r{\[WARNING\] You are loading all pages from a resource paginated with
|
170
|
+
%r{\[WARNING\] You are loading all pages from a resource paginated with sequential pagination.}
|
171
171
|
).to_stderr
|
172
172
|
|
173
173
|
expect(customer.users.length).to eq amount_of_users
|
@@ -214,12 +214,12 @@ describe DHS::Record do
|
|
214
214
|
|
215
215
|
it 'overwrites existing pagination paramters if they are already contained in a string' do
|
216
216
|
expect(DHC).to receive(:request)
|
217
|
-
.with(url: 'http://datastore/customers/1').and_call_original
|
217
|
+
.with({ url: 'http://datastore/customers/1' }).and_call_original
|
218
218
|
|
219
219
|
expect(DHC).to receive(:request)
|
220
|
-
.with(url: 'http://datastore/customers/1/contracts',
|
221
|
-
|
222
|
-
|
220
|
+
.with({ url: 'http://datastore/customers/1/contracts',
|
221
|
+
all: true,
|
222
|
+
params: { limit: 100 } }).and_call_original
|
223
223
|
|
224
224
|
expect(DHC).to receive(:request)
|
225
225
|
.with([{ url: 'http://datastore/customers/1/contracts',
|
@@ -348,7 +348,7 @@ describe DHS::Record do
|
|
348
348
|
expect(lambda do
|
349
349
|
all = Record.all.fetch
|
350
350
|
end).to output(
|
351
|
-
%r{\[WARNING\] You are loading all pages from a resource paginated with
|
351
|
+
%r{\[WARNING\] You are loading all pages from a resource paginated with sequential pagination.}
|
352
352
|
).to_stderr
|
353
353
|
|
354
354
|
expect(all).to be_kind_of Record
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dhs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- https://github.com/DePayFi/dhs/graphs/contributors
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -327,6 +327,7 @@ files:
|
|
327
327
|
- lib/dhs/item.rb
|
328
328
|
- lib/dhs/pagination/base.rb
|
329
329
|
- lib/dhs/pagination/link.rb
|
330
|
+
- lib/dhs/pagination/next_offset.rb
|
330
331
|
- lib/dhs/pagination/offset.rb
|
331
332
|
- lib/dhs/pagination/offset_page.rb
|
332
333
|
- lib/dhs/pagination/page.rb
|
@@ -466,6 +467,7 @@ files:
|
|
466
467
|
- spec/pagination/link/pages_left_spec.rb
|
467
468
|
- spec/pagination/link/parallel_spec.rb
|
468
469
|
- spec/pagination/link/total_spec.rb
|
470
|
+
- spec/pagination/next_offset_spec.rb
|
469
471
|
- spec/pagination/offset/pages_left_spec.rb
|
470
472
|
- spec/pagination/offset_page_spec.rb
|
471
473
|
- spec/pagination/parameters_spec.rb
|
@@ -578,7 +580,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
578
580
|
version: '0'
|
579
581
|
requirements:
|
580
582
|
- Ruby >= 2.7.2
|
581
|
-
rubygems_version: 3.2.
|
583
|
+
rubygems_version: 3.2.3
|
582
584
|
signing_key:
|
583
585
|
specification_version: 4
|
584
586
|
summary: 'REST services accelerator: Rails gem providing an easy, active-record-like
|
@@ -703,6 +705,7 @@ test_files:
|
|
703
705
|
- spec/pagination/link/pages_left_spec.rb
|
704
706
|
- spec/pagination/link/parallel_spec.rb
|
705
707
|
- spec/pagination/link/total_spec.rb
|
708
|
+
- spec/pagination/next_offset_spec.rb
|
706
709
|
- spec/pagination/offset/pages_left_spec.rb
|
707
710
|
- spec/pagination/offset_page_spec.rb
|
708
711
|
- spec/pagination/parameters_spec.rb
|