2018-11-20 20:47:30 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
module GroupDescendant
|
|
|
|
# Returns the hierarchy of a project or group in the from of a hash upto a
|
|
|
|
# given top.
|
|
|
|
#
|
|
|
|
# > project.hierarchy
|
|
|
|
# => { parent_group => { child_group => project } }
|
|
|
|
def hierarchy(hierarchy_top = nil, preloaded = nil)
|
|
|
|
preloaded ||= ancestors_upto(hierarchy_top)
|
|
|
|
expand_hierarchy_for_child(self, self, hierarchy_top, preloaded)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Merges all hierarchies of the given groups or projects into an array of
|
|
|
|
# hashes. All ancestors need to be loaded into the given `descendants` to avoid
|
|
|
|
# queries down the line.
|
|
|
|
#
|
|
|
|
# > GroupDescendant.merge_hierarchy([project, child_group, child_group2, parent])
|
|
|
|
# => { parent => [{ child_group => project}, child_group2] }
|
|
|
|
def self.build_hierarchy(descendants, hierarchy_top = nil)
|
|
|
|
descendants = Array.wrap(descendants).uniq
|
|
|
|
return [] if descendants.empty?
|
|
|
|
|
|
|
|
unless descendants.all? { |hierarchy| hierarchy.is_a?(GroupDescendant) }
|
2019-12-16 22:33:55 +05:30
|
|
|
raise ArgumentError.new(_('element is not a hierarchy'))
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
all_hierarchies = descendants.map do |descendant|
|
|
|
|
descendant.hierarchy(hierarchy_top, descendants)
|
|
|
|
end
|
|
|
|
|
|
|
|
Gitlab::Utils::MergeHash.merge(all_hierarchies)
|
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
|
|
|
def expand_hierarchy_for_child(child, hierarchy, hierarchy_top, preloaded)
|
|
|
|
parent = hierarchy_top if hierarchy_top && child.parent_id == hierarchy_top.id
|
|
|
|
parent ||= preloaded.detect { |possible_parent| possible_parent.is_a?(Group) && possible_parent.id == child.parent_id }
|
|
|
|
|
|
|
|
if parent.nil? && !child.parent_id.nil?
|
2018-10-15 14:42:47 +05:30
|
|
|
parent = child.parent
|
|
|
|
|
|
|
|
exception = ArgumentError.new <<~MSG
|
2019-12-16 22:33:55 +05:30
|
|
|
Parent was not preloaded for child when rendering group hierarchy.
|
2018-10-15 14:42:47 +05:30
|
|
|
This error is not user facing, but causes a +1 query.
|
|
|
|
MSG
|
|
|
|
extras = {
|
2018-11-08 19:23:39 +05:30
|
|
|
parent: parent.inspect,
|
|
|
|
child: child.inspect,
|
2018-10-15 14:42:47 +05:30
|
|
|
preloaded: preloaded.map(&:full_path)
|
|
|
|
}
|
2019-12-16 22:33:55 +05:30
|
|
|
issue_url = 'https://gitlab.com/gitlab-org/gitlab-foss/issues/49404'
|
2018-10-15 14:42:47 +05:30
|
|
|
|
|
|
|
Gitlab::Sentry.track_exception(exception, issue_url: issue_url, extra: extras)
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
if parent.nil? && hierarchy_top.present?
|
2019-12-16 22:33:55 +05:30
|
|
|
raise ArgumentError.new(_('specified top is not part of the tree'))
|
2018-03-17 18:26:18 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
if parent && parent != hierarchy_top
|
|
|
|
expand_hierarchy_for_child(parent,
|
|
|
|
{ parent => hierarchy },
|
|
|
|
hierarchy_top,
|
|
|
|
preloaded)
|
|
|
|
else
|
|
|
|
hierarchy
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|