56 lines
1.7 KiB
Ruby
56 lines
1.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module Gitlab
|
|
module Pagination
|
|
module Keyset
|
|
class Pager
|
|
attr_reader :request
|
|
|
|
def initialize(request)
|
|
@request = request
|
|
end
|
|
|
|
def paginate(relation)
|
|
# Validate assumption: The last two columns must match the page order_by
|
|
validate_order!(relation)
|
|
|
|
# This performs the database query and retrieves records
|
|
# We retrieve one record more to check if we have data beyond this page
|
|
all_records = relation.limit(page.per_page + 1).to_a # rubocop: disable CodeReuse/ActiveRecord
|
|
|
|
records_for_page = all_records.first(page.per_page)
|
|
|
|
# If we retrieved more records than belong on this page,
|
|
# we know there's a next page
|
|
there_is_more = all_records.size > records_for_page.size
|
|
apply_headers(records_for_page.last, there_is_more)
|
|
|
|
records_for_page
|
|
end
|
|
|
|
private
|
|
|
|
def apply_headers(last_record_in_page, there_is_more)
|
|
end_reached = last_record_in_page.nil? || !there_is_more
|
|
lower_bounds = last_record_in_page&.slice(page.order_by.keys)
|
|
|
|
next_page = page.next(lower_bounds, end_reached)
|
|
|
|
request.apply_headers(next_page)
|
|
end
|
|
|
|
def page
|
|
@page ||= request.page
|
|
end
|
|
|
|
def validate_order!(rel)
|
|
present_order = rel.order_values.map { |val| [val.expr.name.to_sym, val.direction] }.last(2).to_h
|
|
|
|
unless page.order_by == present_order
|
|
raise ArgumentError, "Page's order_by does not match the relation's order: #{present_order} vs #{page.order_by}"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|