summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorBob Van Landuyt <bob@vanlanduyt.co>2017-09-07 19:08:56 +0200
committerBob Van Landuyt <bob@vanlanduyt.co>2017-10-04 22:49:41 +0200
commit518216c0627cb6c4b3db62f10877b44d0e912ddb (patch)
tree68a4aa0a3301728097e73c6e09c48780a1b52757 /app
parent530cf2a2669ea1ee3c41d48a15919f875babefa4 (diff)
downloadgitlab-ce-518216c0627cb6c4b3db62f10877b44d0e912ddb.tar.gz
Merge group hierarchies when parents are shared
Diffstat (limited to 'app')
-rw-r--r--app/controllers/groups_controller.rb1
-rw-r--r--app/finders/group_children_finder.rb3
-rw-r--r--app/finders/group_projects_finder.rb1
-rw-r--r--app/models/concerns/group_hierarchy.rb47
-rw-r--r--app/serializers/base_serializer.rb3
-rw-r--r--app/serializers/group_child_serializer.rb37
6 files changed, 88 insertions, 4 deletions
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 4f0d1f88e58..a714f2cc7b0 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -77,6 +77,7 @@ class GroupsController < Groups::ApplicationController
render json: GroupChildSerializer
.new(current_user: current_user)
.with_pagination(request, response)
+ .hierarchy_base(parent, open_hierarchy: filter[:filter].present)
.represent(@children)
end
end
diff --git a/app/finders/group_children_finder.rb b/app/finders/group_children_finder.rb
index 31b122e10f6..eb0129947e2 100644
--- a/app/finders/group_children_finder.rb
+++ b/app/finders/group_children_finder.rb
@@ -52,7 +52,7 @@ class GroupChildrenFinder
end
def subgroups_matching_filter
- all_subgroups.search(params[:filter])
+ all_subgroups.search(params[:filter]).include(:parent)
end
def subgroups
@@ -75,6 +75,7 @@ class GroupChildrenFinder
def projects_matching_filter
ProjectsFinder.new(current_user: current_user).execute
.search(params[:filter])
+ .include(:namespace)
.where(namespace: all_subgroups)
end
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
index f2d3b90b8e2..6e8733bb49c 100644
--- a/app/finders/group_projects_finder.rb
+++ b/app/finders/group_projects_finder.rb
@@ -34,7 +34,6 @@ class GroupProjectsFinder < ProjectsFinder
else
collection_without_user
end
-
union(projects)
end
diff --git a/app/models/concerns/group_hierarchy.rb b/app/models/concerns/group_hierarchy.rb
index 03864023771..f7a62c9a607 100644
--- a/app/models/concerns/group_hierarchy.rb
+++ b/app/models/concerns/group_hierarchy.rb
@@ -1,6 +1,6 @@
module GroupHierarchy
def hierarchy(hierarchy_base = nil)
- @hierarchy ||= tree_for_child(self, self, hierarchy_base)
+ tree_for_child(self, self, hierarchy_base)
end
def parent
@@ -16,7 +16,7 @@ module GroupHierarchy
raise ArgumentError.new('specified base is not part of the tree')
end
- if child.parent != hierarchy_base
+ if child.parent && child.parent != hierarchy_base
tree_for_child(child.parent,
{ child.parent => tree },
hierarchy_base)
@@ -24,4 +24,47 @@ module GroupHierarchy
tree
end
end
+
+ def merge_hierarchy(other_element, hierarchy_base = nil)
+ GroupHierarchy.merge_hierarchies([self, other_element], hierarchy_base)
+ end
+
+ def self.merge_hierarchies(hierarchies, hierarchy_base = nil)
+ hierarchies = Array.wrap(hierarchies)
+ return if hierarchies.empty?
+
+ unless hierarchies.all? { |other_base| other_base.is_a?(GroupHierarchy) }
+ raise ArgumentError.new('element is not a hierarchy')
+ end
+
+ first_hierarchy, *other_hierarchies = hierarchies
+ merged = first_hierarchy.hierarchy(hierarchy_base)
+
+ other_hierarchies.each do |child|
+ next_hierarchy = child.hierarchy(hierarchy_base)
+ merged = merge_values(merged, next_hierarchy)
+ end
+
+ merged
+ end
+
+ def self.merge_values(first_child, second_child)
+ # When the first is an array, we need to go over every element to see if
+ # we can merge deeper.
+ if first_child.is_a?(Array)
+ first_child.map do |element|
+ if element.is_a?(Hash) && element.keys.any? { |k| second_child.keys.include?(k) }
+ element.deep_merge(second_child) { |key, first, second| merge_values(first, second) }
+ else
+ element
+ end
+ end
+ # If both of them are hashes, we can deep_merge with the same logic
+ elsif first_child.is_a?(Hash) && second_child.is_a?(Hash)
+ first_child.deep_merge(second_child) { |key, first, second| merge_values(first, second) }
+ # One of them is a root node, we just need to put them next to eachother in an array
+ else
+ Array.wrap(first_child) + Array.wrap(second_child)
+ end
+ end
end
diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb
index 4e6c15f673b..d78108480d3 100644
--- a/app/serializers/base_serializer.rb
+++ b/app/serializers/base_serializer.rb
@@ -1,5 +1,8 @@
class BaseSerializer
+ attr_reader :parameters
+
def initialize(parameters = {})
+ @parameters = parameters
@request = EntityRequest.new(parameters)
end
diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb
index fbf4a6783b9..ed84c3ae1d7 100644
--- a/app/serializers/group_child_serializer.rb
+++ b/app/serializers/group_child_serializer.rb
@@ -1,5 +1,42 @@
class GroupChildSerializer < BaseSerializer
include WithPagination
+ attr_reader :hierarchy_root
+
entity GroupChildEntity
+
+ def expand_hierarchy(hierarchy_root)
+ @hierarchy_root = hierarchy_root
+ self
+ end
+
+ def represent(resource, opts = {}, entity_class = nil)
+ if hierarchy_root.present?
+ represent_hierarchies(resource, opts)
+ else
+ super(resource, opts)
+ end
+ end
+
+ def represent_hierarchies(children, opts)
+ if children.is_a?(GroupHierarchy)
+ represent_hierarchy(children.hierarchy(hierarchy_root), opts)
+ else
+ children.map { |child| represent_hierarchy(child.hierarchy(hierarchy_root), opts) }
+ end
+ end
+
+ def represent_hierarchy(hierarchy, opts)
+ serializer = self.class.new(parameters)
+
+ result = if hierarchy.is_a?(Hash)
+ parent = hierarchy.keys.first
+ serializer.represent(parent, opts)
+ .merge(children: [serializer.represent_hierarchy(hierarchy[parent], opts)])
+ else
+ serializer.represent(hierarchy, opts)
+ end
+
+ result
+ end
end