Commit 5a903149 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Handle archived projects in the `GroupDescendantsFinder`

parent 99c76e9f
# GroupDescendantsFinder
#
# Used to find and filter all subgroups and projects of a passed parent group
# visible to a specified user.
#
# When passing a `filter` param, the search is performed over all nested levels
# of the `parent_group`. All ancestors for a search result are loaded
#
# Arguments:
# current_user: The user for which the children should be visible
# parent_group: The group to find children of
# params:
# Supports all params that the `ProjectsFinder` and `GroupProjectsFinder`
# support.
#
# filter: string - is aliased to `search` for consistency with the frontend
# archived: string - `only` or `true`.
# `non_archived` is passed to the `ProjectFinder`s if none
# was given.
class GroupDescendantsFinder class GroupDescendantsFinder
attr_reader :current_user, :parent_group, :params attr_reader :current_user, :parent_group, :params
def initialize(current_user: nil, parent_group:, params: {}) def initialize(current_user: nil, parent_group:, params: {})
@current_user = current_user @current_user = current_user
@parent_group = parent_group @parent_group = parent_group
@params = params.reverse_merge(non_archived: true) @params = params.reverse_merge(non_archived: params[:archived].blank?)
end end
def execute def execute
...@@ -83,7 +102,7 @@ class GroupDescendantsFinder ...@@ -83,7 +102,7 @@ class GroupDescendantsFinder
projects_to_load_ancestors_of = projects.where.not(namespace: parent_group) projects_to_load_ancestors_of = projects.where.not(namespace: parent_group)
groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id)) groups_to_load_ancestors_of = Group.where(id: projects_to_load_ancestors_of.select(:namespace_id))
ancestors_for_groups(groups_to_load_ancestors_of) ancestors_for_groups(groups_to_load_ancestors_of)
.with_selects_for_list .with_selects_for_list(params[:archived])
end end
def subgroups def subgroups
...@@ -96,7 +115,7 @@ class GroupDescendantsFinder ...@@ -96,7 +115,7 @@ class GroupDescendantsFinder
else else
direct_child_groups direct_child_groups
end end
groups.with_selects_for_list.order_by(sort) groups.with_selects_for_list(params[:archived]).order_by(sort)
end end
def direct_child_projects def direct_child_projects
...@@ -104,15 +123,15 @@ class GroupDescendantsFinder ...@@ -104,15 +123,15 @@ class GroupDescendantsFinder
.execute .execute
end end
def projects_for_user
Project.public_or_visible_to_user(current_user).non_archived
end
# Finds all projects nested under `parent_group` or any of its descendant # Finds all projects nested under `parent_group` or any of its descendant
# groups # groups
def projects_matching_filter def projects_matching_filter
projects_for_user.search(params[:filter]) projects_nested_in_group = Project.where(namespace_id: hierarchy_for_parent.base_and_descendants.select(:id))
.where(namespace_id: hierarchy_for_parent.base_and_descendants.select(:id)) params_with_search = params.merge(search: params[:filter])
ProjectsFinder.new(params: params_with_search,
current_user: current_user,
project_ids_relation: projects_nested_in_group).execute
end end
def projects def projects
......
...@@ -2,15 +2,14 @@ module LoadedInGroupList ...@@ -2,15 +2,14 @@ module LoadedInGroupList
extend ActiveSupport::Concern extend ActiveSupport::Concern
PROJECT_COUNT_SQL = <<~PROJECTCOUNT.freeze PROJECT_COUNT_SQL = <<~PROJECTCOUNT.freeze
(SELECT COUNT(*) AS preloaded_project_count SELECT COUNT(*) AS preloaded_project_count
FROM projects FROM projects
WHERE projects.namespace_id = namespaces.id WHERE projects.namespace_id = namespaces.id
AND projects.archived IS NOT true)
PROJECTCOUNT PROJECTCOUNT
SUBGROUP_COUNT_SQL = <<~SUBGROUPCOUNT.freeze SUBGROUP_COUNT_SQL = <<~SUBGROUPCOUNT.freeze
(SELECT COUNT(*) AS preloaded_subgroup_count (SELECT COUNT(*) AS preloaded_subgroup_count
FROM namespaces children FROM namespaces children
WHERE children.parent_id = namespaces.id) WHERE children.parent_id = namespaces.id)
SUBGROUPCOUNT SUBGROUPCOUNT
MEMBER_COUNT_SQL = <<~MEMBERCOUNT.freeze MEMBER_COUNT_SQL = <<~MEMBERCOUNT.freeze
(SELECT COUNT(*) AS preloaded_member_count (SELECT COUNT(*) AS preloaded_member_count
...@@ -21,17 +20,28 @@ module LoadedInGroupList ...@@ -21,17 +20,28 @@ module LoadedInGroupList
MEMBERCOUNT MEMBERCOUNT
COUNT_SELECTS = ['namespaces.*', COUNT_SELECTS = ['namespaces.*',
PROJECT_COUNT_SQL,
SUBGROUP_COUNT_SQL, SUBGROUP_COUNT_SQL,
MEMBER_COUNT_SQL].freeze MEMBER_COUNT_SQL].freeze
module ClassMethods module ClassMethods
def with_counts def with_counts(archived = nil)
select(COUNT_SELECTS) selects = COUNT_SELECTS.dup << project_count(archived)
select(selects)
end end
def with_selects_for_list def with_selects_for_list(archived = nil)
with_route.with_counts with_route.with_counts(archived)
end
def project_count(archived)
project_count = if archived == 'only'
PROJECT_COUNT_SQL + 'AND projects.archived IS true'
elsif Gitlab::Utils.to_boolean(archived)
PROJECT_COUNT_SQL
else
PROJECT_COUNT_SQL + 'AND projects.archived IS NOT true'
end
"(#{project_count})"
end end
end end
......
...@@ -35,6 +35,28 @@ describe GroupDescendantsFinder do ...@@ -35,6 +35,28 @@ describe GroupDescendantsFinder do
expect(finder.execute).to contain_exactly(project) expect(finder.execute).to contain_exactly(project)
end end
context 'when archived is `true`' do
let(:params) { { archived: 'true' } }
it 'includes archived projects' do
archived_project = create(:project, namespace: group, archived: true)
project = create(:project, namespace: group)
expect(finder.execute).to contain_exactly(archived_project, project)
end
end
context 'when archived is `only`' do
let(:params) { { archived: 'only' } }
it 'includes only archived projects' do
archived_project = create(:project, namespace: group, archived: true)
_project = create(:project, namespace: group)
expect(finder.execute).to contain_exactly(archived_project)
end
end
it 'does not include archived projects' do it 'does not include archived projects' do
_archived_project = create(:project, :archived, namespace: group) _archived_project = create(:project, :archived, namespace: group)
...@@ -84,6 +106,16 @@ describe GroupDescendantsFinder do ...@@ -84,6 +106,16 @@ describe GroupDescendantsFinder do
expect(finder.execute).to contain_exactly(public_subgroup) expect(finder.execute).to contain_exactly(public_subgroup)
end end
context 'when archived is `true`' do
let(:params) { { archived: 'true' } }
it 'includes archived projects in the count of subgroups' do
create(:project, namespace: subgroup, archived: true)
expect(finder.execute.first.preloaded_project_count).to eq(1)
end
end
context 'with a filter' do context 'with a filter' do
let(:params) { { filter: 'test' } } let(:params) { { filter: 'test' } }
......
...@@ -4,24 +4,45 @@ describe LoadedInGroupList do ...@@ -4,24 +4,45 @@ describe LoadedInGroupList do
let(:parent) { create(:group) } let(:parent) { create(:group) }
subject(:found_group) { Group.with_selects_for_list.find_by(id: parent.id) } subject(:found_group) { Group.with_selects_for_list.find_by(id: parent.id) }
before do
create(:group, parent: parent)
create(:project, namespace: parent)
parent.add_developer(create(:user))
end
describe '.with_selects_for_list' do describe '.with_selects_for_list' do
it 'includes the preloaded counts for groups' do it 'includes the preloaded counts for groups' do
create(:group, parent: parent)
create(:project, namespace: parent)
parent.add_developer(create(:user))
found_group = Group.with_selects_for_list.find_by(id: parent.id) found_group = Group.with_selects_for_list.find_by(id: parent.id)
expect(found_group.preloaded_project_count).to eq(1) expect(found_group.preloaded_project_count).to eq(1)
expect(found_group.preloaded_subgroup_count).to eq(1) expect(found_group.preloaded_subgroup_count).to eq(1)
expect(found_group.preloaded_member_count).to eq(1) expect(found_group.preloaded_member_count).to eq(1)
end end
context 'with archived projects' do
it 'counts including archived projects when `true` is passed' do
create(:project, namespace: parent, archived: true)
create(:project, namespace: parent)
found_group = Group.with_selects_for_list('true').find_by(id: parent.id)
expect(found_group.preloaded_project_count).to eq(2)
end
it 'counts only archived projects when `only` is passed' do
create_list(:project, 2, namespace: parent, archived: true)
create(:project, namespace: parent)
found_group = Group.with_selects_for_list('only').find_by(id: parent.id)
expect(found_group.preloaded_project_count).to eq(2)
end
end
end end
describe '#children_count' do describe '#children_count' do
it 'counts groups and projects' do it 'counts groups and projects' do
create(:group, parent: parent)
create(:project, namespace: parent)
expect(found_group.children_count).to eq(2) expect(found_group.children_count).to eq(2)
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment