lhs 13.2.3 → 14.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2bda2ff4ae84ce367ee21ad8922b46e600b288c5
4
- data.tar.gz: c8fc0746ffca39f2b7e5ad370ba71cc884535ec9
3
+ metadata.gz: 2844d363e539a18b08de95cd58f510dcaa101651
4
+ data.tar.gz: 0e4fc872e5730d30b8ead542f1b135cb28c96d81
5
5
  SHA512:
6
- metadata.gz: e298a06e35a61cdbaddbceffc23e7e982e4387b9d21c7753165202e309bc0e463339b511039abf20c3038c389460e4676ef2ce933a285b83cb53b6543ffb180b
7
- data.tar.gz: d2020691e22febd3f22256c2f689d093e65fd92fb0bb5519ff5413905f54f6a946fe5ce8cbb83ba9b2681322033f7f93ec26f50c4eba56b19e92cb580b267c1c
6
+ metadata.gz: 4ecde150a77ea8c199abc608331f35b854e546714f20e155003fa52b8cca63e6088128cec7d6ae0466bc9432124650d26d86fe16bc4bf840d8824bf1a44482ac
7
+ data.tar.gz: dd4f3cee3b961267307ca3a3f40cfa1bebffa53baa5b958255ac3594a6437095f00c1944e3f5dc4f68a97993ed0e90ea3c3e6c30a36e27d03063eb268b03aaab
data/README.md CHANGED
@@ -336,6 +336,8 @@ Record.all(color: 'blue')
336
336
  # All three are doing the same thing: fetching all records with the color 'blue' from the endpoint while resolving pagingation if endpoint is paginated
337
337
  ```
338
338
 
339
+ In case an API does not provide pagination information (limit, offset and total), LHS keeps on loading pages when requesting `all` until the first empty page responds.
340
+
339
341
  [Count vs. Length](#count-vs-length)
340
342
 
341
343
  `find_each` is a more fine grained way to process single records that are fetched in batches.
@@ -21,6 +21,15 @@ class LHS::Record
21
21
 
22
22
  private
23
23
 
24
+ def single_request_load_and_merge_remaining_objects!(data, options, endpoint)
25
+ return unless options[:all]
26
+ load_and_merge_remaining_objects!(
27
+ data: data,
28
+ options: process_options(options, endpoint),
29
+ load_not_paginated_collection: true
30
+ )
31
+ end
32
+
24
33
  def filter_empty_request_options(options)
25
34
  options.map do |option|
26
35
  option if !option || !option.key?(:url) || !option[:url].nil?
@@ -208,15 +217,36 @@ class LHS::Record
208
217
  # we can evaluate if there are further remote objects remaining
209
218
  # and after preparing all the requests that have to be made in order to fetch all
210
219
  # remote items during this batch, they are fetched in parallel
211
- def load_and_merge_remaining_objects!(data, options)
220
+ def load_and_merge_remaining_objects!(data:, options:, load_not_paginated_collection: false)
212
221
  if paginated?(data._raw)
213
222
  load_and_merge_paginated_collection!(data, options)
214
223
  elsif data.collection? && paginated?(data.first.try(:_raw))
215
224
  load_and_merge_set_of_paginated_collections!(data, options)
225
+ elsif load_not_paginated_collection
226
+ load_and_merge_not_paginated_collection!(data, options)
227
+ end
228
+ end
229
+
230
+ def load_and_merge_not_paginated_collection!(data, options)
231
+ return if data.length.zero?
232
+ options = options.is_a?(Hash) ? options : {}
233
+ limit = options.dig(:params, limit_key) || pagination_class::DEFAULT_LIMIT
234
+ offset = options.dig(:params, pagination_key) || pagination_class::DEFAULT_OFFSET
235
+ options[:params] = options.fetch(:params, {}).merge(
236
+ limit_key => limit,
237
+ pagination_key => pagination_class.next_offset(
238
+ offset,
239
+ limit
240
+ )
241
+ )
242
+ additional_data = data._record.request(options)
243
+ additional_data.each do |item_data|
244
+ data.concat(input: data._raw, items: [item_data], record: self)
216
245
  end
217
246
  end
218
247
 
219
248
  def load_and_merge_paginated_collection!(data, options)
249
+ data._raw[limit_key] = data.length if data._raw[limit_key].blank? && !data.length.zero?
220
250
  pagination = data._record.pagination(data)
221
251
  return data if pagination.pages_left.zero?
222
252
  record = data._record
@@ -279,13 +309,13 @@ class LHS::Record
279
309
  # paginates itself to ensure all records are fetched
280
310
  def load_all_included!(record, options)
281
311
  data = record.request(options)
282
- load_and_merge_remaining_objects!(data, options)
312
+ load_and_merge_remaining_objects!(data: data, options: options)
283
313
  data
284
314
  end
285
315
 
286
316
  # Checks if given raw is paginated or not
287
317
  def paginated?(raw)
288
- !!(raw.is_a?(Hash) && raw[total_key] && raw[pagination_key])
318
+ !!(raw.is_a?(Hash) && raw[total_key])
289
319
  end
290
320
 
291
321
  def prepare_options_for_include_all_request!(options)
@@ -466,7 +496,7 @@ class LHS::Record
466
496
  apply_limit!(options) if options[:all]
467
497
  response = LHC.request(process_options(options, endpoint))
468
498
  data = LHS::Data.new(response.body, nil, self, response.request, endpoint)
469
- load_and_merge_remaining_objects!(data, process_options(options, endpoint)) if paginated?(data._raw) && options[:all]
499
+ single_request_load_and_merge_remaining_objects!(data, options, endpoint)
470
500
  expand_items(data, options[:expanded]) if data.collection? && options[:expanded]
471
501
  handle_includes(including, data, referencing) if including.present? && data.present?
472
502
  data
@@ -23,7 +23,7 @@ module LHS::Pagination
23
23
  end
24
24
 
25
25
  def offset
26
- data._raw.dig(*_record.pagination_key) || 0
26
+ data._raw.dig(*_record.pagination_key) || self.class::DEFAULT_OFFSET
27
27
  end
28
28
  alias current_page offset
29
29
  alias start offset
@@ -1,14 +1,20 @@
1
1
  class LHS::Pagination::Offset < LHS::Pagination::Base
2
2
 
3
+ DEFAULT_OFFSET = 0
4
+
3
5
  def current_page
4
6
  (offset + limit) / limit
5
7
  end
6
8
 
7
9
  def next_offset(step = 1)
8
- offset + limit * step
10
+ self.class.next_offset(offset, limit, step)
9
11
  end
10
12
 
11
13
  def self.page_to_offset(page, limit = DEFAULT_LIMIT)
12
14
  (page.to_i - 1) * limit.to_i
13
15
  end
16
+
17
+ def self.next_offset(offset, limit, step = 1)
18
+ offset.to_i + limit.to_i * step.to_i
19
+ end
14
20
  end
@@ -1,10 +1,16 @@
1
1
  class LHS::Pagination::Page < LHS::Pagination::Base
2
2
 
3
+ DEFAULT_OFFSET = 1
4
+
3
5
  def current_page
4
6
  offset
5
7
  end
6
8
 
7
9
  def next_offset(step = 1)
8
- current_page + step
10
+ self.class.next_offset(current_page, limit, step)
11
+ end
12
+
13
+ def self.next_offset(current_page, _limit, step = 1)
14
+ current_page.to_i + step.to_i
9
15
  end
10
16
  end
@@ -1,14 +1,20 @@
1
1
  class LHS::Pagination::Start < LHS::Pagination::Base
2
2
 
3
+ DEFAULT_OFFSET = 1
4
+
3
5
  def current_page
4
6
  (offset + limit - 1) / limit
5
7
  end
6
8
 
7
9
  def next_offset(step = 1)
8
- offset + limit * step
10
+ self.class.next_offset(offset, limit, step)
9
11
  end
10
12
 
11
13
  def self.page_to_offset(page, limit = DEFAULT_LIMIT)
12
14
  (page.to_i - 1) * limit.to_i + 1
13
15
  end
16
+
17
+ def self.next_offset(offset, limit, step = 1)
18
+ offset.to_i + limit.to_i * step.to_i
19
+ end
14
20
  end
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "13.2.3"
2
+ VERSION = "14.0.0"
3
3
  end
@@ -1,13 +1,13 @@
1
1
  require 'rails_helper'
2
2
 
3
3
  describe LHS::Record do
4
- before(:each) do
5
- class Record < LHS::Record
6
- endpoint 'http://datastore/feedbacks'
4
+ context 'all' do
5
+ before(:each) do
6
+ class Record < LHS::Record
7
+ endpoint 'http://datastore/feedbacks'
8
+ end
7
9
  end
8
- end
9
10
 
10
- context 'all' do
11
11
  it 'is querying endpoint without pagination when using all' do
12
12
  stub_request(:get, "http://datastore/feedbacks?limit=100").to_return(body: { items: 300.times.map { { foo: 'bar' } }, total: 300 }.to_json)
13
13
  records = Record.all
@@ -58,4 +58,48 @@ describe LHS::Record do
58
58
  end
59
59
  end
60
60
  end
61
+
62
+ context 'all without current page indicator' do
63
+ before(:each) do
64
+ class Category < LHS::Record
65
+ configuration(
66
+ items_key: %i(response results),
67
+ limit_key: :max,
68
+ pagination_key: :offset,
69
+ pagination_strategy: :offset
70
+ )
71
+
72
+ endpoint 'http://store/categories'
73
+ end
74
+ end
75
+
76
+ def stub_batch(url, items = 10)
77
+ stub_request(:get, url)
78
+ .to_return(
79
+ body: {
80
+ response: {
81
+ results: items.times.map { { name: 'category' } }
82
+ }
83
+ }.to_json
84
+ )
85
+ end
86
+
87
+ it 'is able to fetch all remote objects without any current page indicator by simply increasing the offset until response is empty' do
88
+ stub_batch('http://store/categories?language=en&max=10&offset=0')
89
+ stub_batch('http://store/categories?language=en&max=10&offset=10')
90
+ stub_batch('http://store/categories?language=en&max=10&offset=20')
91
+ stub_batch('http://store/categories?language=en&max=10&offset=30', 0)
92
+ records = Category.limit(10).all(language: 'en').fetch
93
+ expect(records.length).to eq 30
94
+ end
95
+
96
+ it 'is able to fetch all remote objects without any current page indicator by simply increasing the offset until response is empty' do
97
+ stub_batch('http://store/categories?language=en&max=100', 100)
98
+ stub_batch('http://store/categories?language=en&max=100&offset=100', 100)
99
+ stub_batch('http://store/categories?language=en&max=100&offset=200', 100)
100
+ stub_batch('http://store/categories?language=en&max=100&offset=300', 0)
101
+ records = Category.all(language: 'en').fetch
102
+ expect(records.length).to eq 300
103
+ end
104
+ end
61
105
  end
@@ -81,6 +81,8 @@ describe LHS::Record do
81
81
  it 'uses urls instead of trying to find base endpoint of parent class' do
82
82
  stub_request(:get, "#{datastore}/entry/123/contracts?limit=100")
83
83
  .to_return(body: [{ product: { href: "#{datastore}/products/LBC" } }].to_json)
84
+ stub_request(:get, "#{datastore}/entry/123/contracts?limit=100&offset=100")
85
+ .to_return(body: [].to_json)
84
86
  stub_request(:get, "#{datastore}/products/LBC")
85
87
  .to_return(body: { name: 'Local Business Card' }.to_json)
86
88
  expect(lambda {
@@ -346,7 +346,10 @@ describe LHS::Record do
346
346
  .to_return(
347
347
  body: {
348
348
  href: "http://datastore/v2/places/1/contracts?offset=0&limit=10",
349
- items: [{ href: "http://datastore/v2/contracts/1" }]
349
+ items: [{ href: "http://datastore/v2/contracts/1" }],
350
+ offset: 0,
351
+ limit: 10,
352
+ total: 10
350
353
  }.to_json
351
354
  )
352
355
 
@@ -30,7 +30,8 @@ describe LHS::Record do
30
30
  end
31
31
 
32
32
  it 'also works when there is no total in the stubbing' do
33
- stub_request(:get, %r{/feedbacks}).to_return(body: { items: (1..100).to_a }.to_json)
33
+ stub_request(:get, "http://local.ch/v2/feedbacks?limit=100").to_return(body: { items: (1..100).to_a }.to_json)
34
+ stub_request(:get, "http://local.ch/v2/feedbacks?limit=100&offset=100").to_return(body: { items: [] }.to_json)
34
35
  all = Record.all
35
36
  expect(all).to be_kind_of Record
36
37
  expect(all._proxy).to be_kind_of LHS::Collection
@@ -38,7 +39,8 @@ describe LHS::Record do
38
39
  end
39
40
 
40
41
  it 'also works when there is no key "items" in the stubbing' do
41
- stub_request(:get, %r{/feedbacks}).to_return(body: (1..100).to_a.to_json)
42
+ stub_request(:get, "http://local.ch/v2/feedbacks?limit=100").to_return(body: (1..100).to_a.to_json)
43
+ stub_request(:get, "http://local.ch/v2/feedbacks?limit=100&offset=100").to_return(body: [].to_json)
42
44
  all = Record.all
43
45
  expect(all).to be_kind_of Record
44
46
  expect(all._proxy).to be_kind_of LHS::Collection
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 13.2.3
4
+ version: 14.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhs/graphs/contributors