1# frozen_string_literal: true 2 3module Gitlab 4 module Diff 5 module FileCollection 6 # Builds a paginated diff file collection and collects pagination 7 # metadata. 8 # 9 # It doesn't handle caching yet as we're not prepared to write/read 10 # separate file keys (https://gitlab.com/gitlab-org/gitlab/issues/30550). 11 # 12 class MergeRequestDiffBatch < MergeRequestDiffBase 13 DEFAULT_BATCH_PAGE = 1 14 DEFAULT_BATCH_SIZE = 30 15 16 attr_reader :pagination_data 17 18 def initialize(merge_request_diff, batch_page, batch_size, diff_options:) 19 super(merge_request_diff, diff_options: diff_options) 20 21 @paginated_collection = load_paginated_collection(batch_page, batch_size, diff_options) 22 23 @pagination_data = { 24 total_pages: @paginated_collection.blank? ? nil : relation.size 25 } 26 end 27 28 override :diffs 29 def diffs 30 strong_memoize(:diffs) do 31 @merge_request_diff.opening_external_diff do 32 # Avoiding any extra queries. 33 collection = @paginated_collection.to_a 34 35 # The offset collection and calculation is required so that we 36 # know how much has been loaded in previous batches, collapsing 37 # the current paginated set accordingly (collection limit calculation). 38 # See: https://docs.gitlab.com/ee/development/diffs.html#diff-collection-limits 39 # 40 offset_index = collection.first&.index 41 options = diff_options.dup 42 43 collection = 44 if offset_index && offset_index > 0 45 offset_collection = relation.limit(offset_index) # rubocop:disable CodeReuse/ActiveRecord 46 options[:offset_index] = offset_index 47 offset_collection + collection 48 else 49 collection 50 end 51 52 Gitlab::Git::DiffCollection.new(collection.map(&:to_hash), options) 53 end 54 end 55 end 56 57 private 58 59 def relation 60 @merge_request_diff.merge_request_diff_files 61 end 62 63 # rubocop: disable CodeReuse/ActiveRecord 64 def load_paginated_collection(batch_page, batch_size, diff_options) 65 batch_page ||= DEFAULT_BATCH_PAGE 66 batch_size ||= DEFAULT_BATCH_SIZE 67 68 paths = diff_options&.fetch(:paths, nil) 69 70 paginated_collection = relation.offset(batch_page).limit([batch_size.to_i, DEFAULT_BATCH_SIZE].min) 71 paginated_collection = paginated_collection.by_paths(paths) if paths 72 73 paginated_collection 74 end 75 # rubocop: enable CodeReuse/ActiveRecord 76 end 77 end 78 end 79end 80