diff options
author | Bob Van Landuyt <bob@vanlanduyt.co> | 2017-09-07 19:08:56 +0200 |
---|---|---|
committer | Bob Van Landuyt <bob@vanlanduyt.co> | 2017-10-04 22:49:41 +0200 |
commit | 518216c0627cb6c4b3db62f10877b44d0e912ddb (patch) | |
tree | 68a4aa0a3301728097e73c6e09c48780a1b52757 /app | |
parent | 530cf2a2669ea1ee3c41d48a15919f875babefa4 (diff) | |
download | gitlab-ce-518216c0627cb6c4b3db62f10877b44d0e912ddb.tar.gz |
Merge group hierarchies when parents are shared
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/groups_controller.rb | 1 | ||||
-rw-r--r-- | app/finders/group_children_finder.rb | 3 | ||||
-rw-r--r-- | app/finders/group_projects_finder.rb | 1 | ||||
-rw-r--r-- | app/models/concerns/group_hierarchy.rb | 47 | ||||
-rw-r--r-- | app/serializers/base_serializer.rb | 3 | ||||
-rw-r--r-- | app/serializers/group_child_serializer.rb | 37 |
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 |