summaryrefslogtreecommitdiff
path: root/lib/gitlab/pagination/keyset/pager.rb
blob: 6a2ae20f3b87445144e1c0eab89dcb1acae4ee09 (plain)
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
# frozen_string_literal: true

module Gitlab
  module Pagination
    module Keyset
      class Pager < Gitlab::Pagination::Base
        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)

          relation.limit(page.per_page) # rubocop: disable CodeReuse/ActiveRecord
        end

        def finalize(records)
          apply_headers(records.last)
        end

        private

        def apply_headers(last_record_in_page)
          return unless last_record_in_page

          lower_bounds = last_record_in_page&.slice(page.order_by.keys)
          next_page = page.next(lower_bounds)

          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