diff options
author | Douwe Maan <douwe@gitlab.com> | 2017-10-17 10:03:03 +0000 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2017-10-17 10:03:03 +0000 |
commit | 79e889122b9f1cb41eb75ee33e94e625a8c679e2 (patch) | |
tree | eb8bba9933b3241b3ca80574bc47fc40d4f3950f /app/models/concerns | |
parent | 3fa410c831dac1dd1a74a14260ed99a5920218f8 (diff) | |
parent | 893402d477436d36b48c2fd0244576a0d16e9425 (diff) | |
download | gitlab-ce-79e889122b9f1cb41eb75ee33e94e625a8c679e2.tar.gz |
Merge branch 'bvl-group-trees' into 'master'
Show collapsible tree on the project show page
Closes #30343
See merge request gitlab-org/gitlab-ce!14055
Diffstat (limited to 'app/models/concerns')
-rw-r--r-- | app/models/concerns/group_descendant.rb | 56 | ||||
-rw-r--r-- | app/models/concerns/loaded_in_group_list.rb | 72 |
2 files changed, 128 insertions, 0 deletions
diff --git a/app/models/concerns/group_descendant.rb b/app/models/concerns/group_descendant.rb new file mode 100644 index 00000000000..01957da0bf3 --- /dev/null +++ b/app/models/concerns/group_descendant.rb @@ -0,0 +1,56 @@ +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) } + raise ArgumentError.new('element is not a hierarchy') + 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? + raise ArgumentError.new('parent was not preloaded') + end + + if parent.nil? && hierarchy_top.present? + raise ArgumentError.new('specified top is not part of the tree') + end + + if parent && parent != hierarchy_top + expand_hierarchy_for_child(parent, + { parent => hierarchy }, + hierarchy_top, + preloaded) + else + hierarchy + end + end +end diff --git a/app/models/concerns/loaded_in_group_list.rb b/app/models/concerns/loaded_in_group_list.rb new file mode 100644 index 00000000000..dcb3b2b5ff3 --- /dev/null +++ b/app/models/concerns/loaded_in_group_list.rb @@ -0,0 +1,72 @@ +module LoadedInGroupList + extend ActiveSupport::Concern + + module ClassMethods + def with_counts(archived:) + selects_including_counts = [ + 'namespaces.*', + "(#{project_count_sql(archived).to_sql}) AS preloaded_project_count", + "(#{member_count_sql.to_sql}) AS preloaded_member_count", + "(#{subgroup_count_sql.to_sql}) AS preloaded_subgroup_count" + ] + + select(selects_including_counts) + end + + def with_selects_for_list(archived: nil) + with_route.with_counts(archived: archived) + end + + private + + def project_count_sql(archived = nil) + projects = Project.arel_table + namespaces = Namespace.arel_table + + base_count = projects.project(Arel.star.count.as('preloaded_project_count')) + .where(projects[:namespace_id].eq(namespaces[:id])) + if archived == 'only' + base_count.where(projects[:archived].eq(true)) + elsif Gitlab::Utils.to_boolean(archived) + base_count + else + base_count.where(projects[:archived].not_eq(true)) + end + end + + def subgroup_count_sql + namespaces = Namespace.arel_table + children = namespaces.alias('children') + + namespaces.project(Arel.star.count.as('preloaded_subgroup_count')) + .from(children) + .where(children[:parent_id].eq(namespaces[:id])) + end + + def member_count_sql + members = Member.arel_table + namespaces = Namespace.arel_table + + members.project(Arel.star.count.as('preloaded_member_count')) + .where(members[:source_type].eq(Namespace.name)) + .where(members[:source_id].eq(namespaces[:id])) + .where(members[:requested_at].eq(nil)) + end + end + + def children_count + @children_count ||= project_count + subgroup_count + end + + def project_count + @project_count ||= try(:preloaded_project_count) || projects.non_archived.count + end + + def subgroup_count + @subgroup_count ||= try(:preloaded_subgroup_count) || children.count + end + + def member_count + @member_count ||= try(:preloaded_member_count) || users.count + end +end |