debian-mirror-gitlab/lib/banzai/filter/references/reference_cache.rb
2023-07-07 10:43:13 +05:30

199 lines
5.5 KiB
Ruby

# frozen_string_literal: true
module Banzai
module Filter
module References
class ReferenceCache
include Gitlab::Utils::StrongMemoize
include RequestStoreReferenceCache
def initialize(filter, context, result)
@filter = filter
@context = context
@result = result || {}
end
def load_reference_cache(nodes)
load_references_per_parent(nodes)
load_parent_per_reference
load_records_per_parent
@cache_loaded = true
end
# Loads all object references (e.g. issue IDs) per
# project/group they belong to.
def load_references_per_parent(nodes)
@references_per_parent ||= {}
@references_per_parent[parent_type] ||= begin
refs = Hash.new { |hash, key| hash[key] = Set.new }
[filter.object_class.link_reference_pattern, filter.object_class.reference_pattern].each do |pattern|
next unless pattern
prepare_doc_for_scan.to_enum(:scan, pattern).each do
parent_path = if parent_type == :project
full_project_path($~[:namespace], $~[:project])
else
full_group_path($~[:group])
end
ident = filter.identifier($~)
refs[parent_path] << ident if ident
end
end
refs
end
end
def references_per_parent
@references_per_parent[parent_type]
end
# Returns a Hash containing referenced projects grouped per their full
# path.
def load_parent_per_reference
@per_reference ||= {}
@per_reference[parent_type] ||= begin
refs = references_per_parent.keys
parent_ref = {}
# if we already have a parent, no need to query it again
refs.each do |ref|
next unless ref
if context[:project]&.full_path == ref
parent_ref[ref] = context[:project]
elsif context[:group]&.full_path == ref
parent_ref[ref] = context[:group]
end
refs -= [ref] if parent_ref[ref]
end
find_for_paths(refs).index_by(&:full_path).merge(parent_ref)
end
end
def parent_per_reference
@per_reference[parent_type]
end
def load_records_per_parent
@_records_per_project ||= {}
@_records_per_project[filter.object_class.to_s.underscore] ||= begin
hash = Hash.new { |h, k| h[k] = {} }
parent_per_reference.each do |path, parent|
record_ids = references_per_parent[path]
filter.parent_records(parent, record_ids).each do |record|
hash[parent][filter.record_identifier(record)] = record
end
end
hash
end
end
def records_per_parent
@_records_per_project[filter.object_class.to_s.underscore]
end
def objects_for_paths(paths)
klass = parent_type.to_s.camelize.constantize
result = klass.where_full_path_in(paths)
return result if parent_type == :group
result.includes(namespace: :route) if parent_type == :project
end
# Returns projects for the given paths.
def find_for_paths(paths)
if Gitlab::SafeRequestStore.active?
cache = refs_cache
to_query = paths - cache.keys
unless to_query.empty?
records = objects_for_paths(to_query)
found = []
records.each do |record|
ref = record.full_path
get_or_set_cache(cache, ref) { record }
found << ref
end
not_found = to_query - found
not_found.each do |ref|
get_or_set_cache(cache, ref) { nil }
end
end
cache.slice(*paths).values.compact
else
objects_for_paths(paths)
end
end
def current_parent_path
strong_memoize(:current_parent_path) do
parent&.full_path
end
end
def current_project_namespace_path
strong_memoize(:current_project_namespace_path) do
project&.namespace&.full_path
end
end
def full_project_path(namespace, project_ref)
return current_parent_path unless project_ref
namespace_ref = namespace || current_project_namespace_path
"#{namespace_ref}/#{project_ref}"
end
def full_group_path(group_ref)
return current_parent_path unless group_ref
group_ref
end
def cache_loaded?
!!@cache_loaded
end
private
attr_accessor :filter, :context, :result
delegate :project, :group, :parent, :parent_type, to: :filter
def refs_cache
Gitlab::SafeRequestStore["banzai_#{parent_type}_refs".to_sym] ||= {}
end
def prepare_doc_for_scan
filter.requires_unescaping? ? unescape_html_entities(html_content) : html_content
end
def html_content
result[:rendered_html] ||= filter.doc.to_html
end
def unescape_html_entities(text)
CGI.unescapeHTML(text.to_s)
end
end
end
end
end
Banzai::Filter::References::ReferenceCache.prepend_mod