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