-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpaginator.rb
More file actions
103 lines (82 loc) · 2.82 KB
/
Copy pathpaginator.rb
File metadata and controls
103 lines (82 loc) · 2.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# frozen_string_literal: true
require "faraday"
require_relative "http"
require_relative "response"
module Seam
THREAD_CONTEXT_KEY = :seam_pagination_context
PaginationContext = Struct.new(:body, :path)
class Paginator
def initialize(request, params = {})
raise ArgumentError, "request must be a Method" unless request.is_a?(Method)
raise ArgumentError, "params must be a Hash" unless params.is_a?(Hash)
unless request.parameters.any? { |_, name| name == :page_cursor }
raise ArgumentError, "request does not support pagination"
end
@request = request
@params = params.transform_keys(&:to_sym)
end
def first_page
fetch_page(@params)
end
def next_page(next_page_cursor)
if next_page_cursor.nil? || next_page_cursor.empty?
raise ArgumentError,
"Cannot get the next page with a nil or empty next_page_cursor."
end
fetch_page(@params.merge(page_cursor: next_page_cursor))
end
def flatten_to_list
all_items = []
current_items, pagination = first_page
all_items.concat(current_items) if current_items
while pagination&.has_next_page? && (cursor = pagination.next_page_cursor)
current_items, pagination = next_page(cursor)
all_items.concat(current_items) if current_items
end
all_items
end
def flatten
Enumerator.new do |yielder|
current_items, pagination = first_page
current_items&.each { |item| yielder << item }
while pagination&.has_next_page? && (cursor = pagination.next_page_cursor)
current_items, pagination = next_page(cursor)
current_items&.each { |item| yielder << item }
end
end
end
private
def fetch_page(params)
context = PaginationContext.new(nil, nil)
Thread.current[THREAD_CONTEXT_KEY] = context
begin
res_data = @request.call(**params)
pagination_result = Pagination.from_hash(Http::Response.read_pagination(context.body, context.path))
[res_data, pagination_result]
ensure
Thread.current[THREAD_CONTEXT_KEY] = nil
end
end
end
Pagination = Struct.new(:has_next_page, :next_page_cursor, :next_page_url) do
def self.from_hash(hash)
return nil unless hash.is_a?(Hash) && !hash.empty?
new(
has_next_page: hash.fetch("has_next_page", false),
next_page_cursor: hash.fetch("next_page_cursor", nil),
next_page_url: hash.fetch("next_page_url", nil)
)
end
def has_next_page?
has_next_page == true
end
end
class PaginationMiddleware < Faraday::Middleware
def on_complete(env)
context = Thread.current[THREAD_CONTEXT_KEY]
return unless context.is_a?(PaginationContext)
context.body = env[:body]
context.path = env.url.path
end
end
end