Skip to content
This repository was archived by the owner on Oct 6, 2024. It is now read-only.

Commit 807809d

Browse files
committed
add next_offset pagination strategy
1 parent 3b14bfd commit 807809d

File tree

11 files changed

+132
-13
lines changed

11 files changed

+132
-13
lines changed

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,51 @@ Sequentially:
13861386
GET https://service.example.com/records?from_record_id=xcaoXBmuMyFFEcFDSgNgDQ&limit=100
13871387
```
13881388

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+
13891434
#### Pagination keys
13901435

13911436
##### limit_key

lib/dhs.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ module Pagination
3939
autoload :Page, 'dhs/pagination/page'
4040
autoload :TotalPages, 'dhs/pagination/total_pages'
4141
autoload :OffsetPage, 'dhs/pagination/offset_page'
42+
autoload :NextOffset, 'dhs/pagination/next_offset'
4243
autoload :Start, 'dhs/pagination/start'
4344
autoload :Link, 'dhs/pagination/link'
4445
end

lib/dhs/concerns/record/pagination.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ def pagination_class
3030
DHS::Pagination::Start
3131
when :link
3232
DHS::Pagination::Link
33+
when :next_offset
34+
DHS::Pagination::NextOffset
3335
else
3436
DHS::Pagination::Offset
3537
end
@@ -43,7 +45,8 @@ def pagination(data)
4345
def paginated?(raw)
4446
raw.is_a?(Hash) && (
4547
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?
4750
)
4851
end
4952
end

lib/dhs/concerns/record/request.rb

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ def load_and_merge_paginated_collection!(data, options)
237237
if pagination.parallel?
238238
load_and_merge_parallel_requests!(record, data, pagination, options)
239239
else
240-
load_and_merge_sequential_requests!(record, data, options, data._raw.dig(:next, :href), pagination)
240+
load_and_merge_sequential_requests!(record, data, options, pagination)
241241
end
242242
end
243243

@@ -249,13 +249,16 @@ def load_and_merge_parallel_requests!(record, data, pagination, options)
249249
end
250250
end
251251

252-
def load_and_merge_sequential_requests!(record, data, options, next_link, pagination)
253-
warn '[WARNING] You are loading all pages from a resource paginated with links only. As this is performed sequentially, it can result in very poor performance! (https://github.com/DePayFi/dhs#pagination-strategy-link).'
254-
while next_link.present?
255-
page_data = record.request(
256-
options.except(:all).merge(url: next_link)
257-
)
258-
next_link = page_data._raw.dig(:next, :href)
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)
259262
merge_batch_data_with_parent!(page_data, data, pagination)
260263
end
261264
end

lib/dhs/pagination/link.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ def total
77

88
alias count total
99

10+
def next(current)
11+
current.dig(:next, :href)
12+
end
13+
1014
def pages_left
1115
pages_left? ? 1 : 0
1216
end

lib/dhs/pagination/next_offset.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

lib/dhs/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module DHS
4-
VERSION = '1.3.0'
4+
VERSION = '1.4.0'
55
end

spec/graphql/main_spec.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,13 @@
8888
end
8989

9090
before do
91+
DHC.config.placeholder('bitquery', 'https://graphql.bitquery.io/')
92+
9193
class Record < DHS::Record
9294

9395
configuration items_key: [:data, :ethereum, :address, 0, :balances]
9496

95-
endpoint 'https://graphql.bitquery.io/',
97+
endpoint '{+bitquery}',
9698
graphql: {
9799
query: %{
98100
query ($network: EthereumNetwork!, $address: String!) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
describe DHS::Record do
6+
7+
context 'pagination' do
8+
9+
def stub_api_request(items: [], offset: nil, next_offset:)
10+
stub_request(:get, ["http://depay.fi/v2/transactions?limit=100", offset ? "offset=#{offset}" : nil].compact.join('&'))
11+
.to_return(body: { items: items, next_offset: next_offset }.to_json)
12+
end
13+
14+
let!(:requests) do
15+
stub_api_request(items: (0...100).to_a, next_offset: 99)
16+
stub_api_request(items: (100...200).to_a, offset: 99, next_offset: 199)
17+
stub_api_request(items: (200...300).to_a, offset: 199, next_offset: 0)
18+
end
19+
20+
before do
21+
class Transaction < DHS::Record
22+
configuration pagination_strategy: :next_offset, pagination_key: { body: :next_offset, parameter: :offset }
23+
24+
endpoint 'http://depay.fi/v2/transactions'
25+
end
26+
end
27+
28+
it 'fetches all the pages' do
29+
transactions = Transaction.all.fetch
30+
expect(transactions.to_a).to eq (0...300).to_a
31+
end
32+
end
33+
end

spec/record/includes_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def additional_products_request(offset, amount)
167167
.includes(:users, contracts: :products)
168168
.find(1)
169169
end).to output(
170-
%r{\[WARNING\] You are loading all pages from a resource paginated with links only. As this is performed sequentially, it can result in very poor performance! \(https://github.com/DePayFi/dhs#pagination-strategy-link\).}
170+
%r{\[WARNING\] You are loading all pages from a resource paginated with sequential pagination.}
171171
).to_stderr
172172

173173
expect(customer.users.length).to eq amount_of_users

0 commit comments

Comments
 (0)