1# frozen_string_literal: true
2
3# Returns and caches in thread max member access for a resource
4#
5module BulkMemberAccessLoad
6  extend ActiveSupport::Concern
7
8  included do
9    # Determine the maximum access level for a group of resources in bulk.
10    #
11    # Returns a Hash mapping resource ID -> maximum access level.
12    def max_member_access_for_resource_ids(resource_klass, resource_ids, &block)
13      raise 'Block is mandatory' unless block_given?
14
15      memoization_index = self.id
16      memoization_class = self.class
17
18      resource_ids = resource_ids.uniq
19      memo_id = "#{memoization_class}:#{memoization_index}"
20      access = load_access_hash(resource_klass, memo_id)
21
22      # Look up only the IDs we need
23      resource_ids -= access.keys
24
25      return access if resource_ids.empty?
26
27      resource_access = yield(resource_ids)
28
29      access.merge!(resource_access)
30
31      missing_resource_ids = resource_ids - resource_access.keys
32
33      missing_resource_ids.each do |resource_id|
34        access[resource_id] = Gitlab::Access::NO_ACCESS
35      end
36
37      access
38    end
39
40    def merge_value_to_request_store(resource_klass, resource_id, value)
41      max_member_access_for_resource_ids(resource_klass, [resource_id]) do
42        { resource_id => value }
43      end
44    end
45
46    private
47
48    def max_member_access_for_resource_key(klass, memoization_index)
49      "max_member_access_for_#{klass.name.underscore.pluralize}:#{memoization_index}"
50    end
51
52    def load_access_hash(resource_klass, memo_id)
53      return {} unless Gitlab::SafeRequestStore.active?
54
55      key = max_member_access_for_resource_key(resource_klass, memo_id)
56      Gitlab::SafeRequestStore[key] ||= {}
57
58      Gitlab::SafeRequestStore[key]
59    end
60  end
61end
62