Commit 9fe5d803 authored by manojmj's avatar manojmj

Allow subgroups to use their parent group's custom project templates

This change allows subgroups to use their parent group's custom
project templates.
parent 26d8ef1e
...@@ -24,7 +24,7 @@ project in the group will be available to every logged in user. ...@@ -24,7 +24,7 @@ project in the group will be available to every logged in user.
However, private projects will be available only if the user is a member of the project. However, private projects will be available only if the user is a member of the project.
NOTE: **Note:** NOTE: **Note:**
Projects of nested subgroups of a selected template source cannot be used. Only direct subgroups can be set as the template source. Projects of nested subgroups of a selected template source cannot be used.
Repository and database information that are copied over to each new project are Repository and database information that are copied over to each new project are
identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md). identical to the data exported with [GitLab's Project Import/Export](../project/settings/import_export.md).
......
...@@ -217,9 +217,9 @@ module EE ...@@ -217,9 +217,9 @@ module EE
def custom_project_templates_group_allowed def custom_project_templates_group_allowed
return if custom_project_templates_group_id.blank? return if custom_project_templates_group_id.blank?
return if descendants.exists?(id: custom_project_templates_group_id) return if children.exists?(id: custom_project_templates_group_id)
errors.add(:custom_project_templates_group_id, "has to be a descendant of the group") errors.add(:custom_project_templates_group_id, "has to be a subgroup of the group")
end end
end end
end end
...@@ -65,17 +65,17 @@ module EE ...@@ -65,17 +65,17 @@ module EE
end end
# When using a project template from a Group, the new project can only be created # When using a project template from a Group, the new project can only be created
# under the top level group or any subgroup # under the template owner's group or subgroups
def validate_namespace_used_with_template(project, group_with_project_templates_id) def validate_namespace_used_with_template(project, group_with_project_templates_id)
return unless project.group return unless project.group
subgroup_with_templates_id = group_with_project_templates_id || params[:group_with_project_templates_id] subgroup_with_templates_id = group_with_project_templates_id || params[:group_with_project_templates_id]
return if subgroup_with_templates_id.blank? return if subgroup_with_templates_id.blank?
templates_owner = ::Group.find(subgroup_with_templates_id) templates_owner = ::Group.find(subgroup_with_templates_id).parent
unless templates_owner.self_and_hierarchy.exists?(id: project.namespace_id) unless templates_owner.self_and_descendants.exists?(id: project.namespace_id)
project.errors.add(:namespace, _("is out of the hierarchy of the Group owning the template")) project.errors.add(:namespace, _("is not a descendant of the Group owning the template"))
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
---
title: Allow subgroups to use their parent group's custom project templates
merge_request: 14499
author:
type: fixed
...@@ -49,25 +49,24 @@ describe Group do ...@@ -49,25 +49,24 @@ describe Group do
context 'validates if custom_project_templates_group_id is allowed' do context 'validates if custom_project_templates_group_id is allowed' do
let(:subgroup_1) { create(:group, parent: group) } let(:subgroup_1) { create(:group, parent: group) }
it 'rejects change if the assigned group is not a descendant' do it 'rejects change if the assigned group is not a subgroup' do
group.custom_project_templates_group_id = create(:group).id group.custom_project_templates_group_id = create(:group).id
expect(group).not_to be_valid expect(group).not_to be_valid
expect(group.errors.messages[:custom_project_templates_group_id]).to eq ['has to be a descendant of the group'] expect(group.errors.messages[:custom_project_templates_group_id]).to eq ['has to be a subgroup of the group']
end end
it 'allows value if the current group is a top parent and the value is from a descendant' do it 'allows value if the assigned value is from a subgroup' do
subgroup = create(:group, parent: group) group.custom_project_templates_group_id = subgroup_1.id
group.custom_project_templates_group_id = subgroup.id
expect(group).to be_valid expect(group).to be_valid
end end
it 'allows value if the current group is a subgroup and the value is from a descendant' do it 'rejects change if the assigned value is from a subgroup\'s descendant group' do
subgroup_1_1 = create(:group, parent: subgroup_1) subgroup_1_1 = create(:group, parent: subgroup_1)
subgroup_1.custom_project_templates_group_id = subgroup_1_1.id group.custom_project_templates_group_id = subgroup_1_1.id
expect(group).to be_valid expect(group).not_to be_valid
end end
it 'allows value when it is blank' do it 'allows value when it is blank' do
......
require 'spec_helper' require 'spec_helper'
# Group Hierarchy:
# Group
# Subgroup 1
# Subgroup 1_1
# Subgroup 1_1_1
# Subgroup 1_2 (the group with the template)
# project_template
# Subgroup_1_2_1
# Subgroup 2
# Subgroup 2_1
# Group 2
describe Projects::CreateFromTemplateService do describe Projects::CreateFromTemplateService do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) } let(:project) { create(:project, :public, namespace: group) }
...@@ -9,7 +22,12 @@ describe Projects::CreateFromTemplateService do ...@@ -9,7 +22,12 @@ describe Projects::CreateFromTemplateService do
let(:subgroup_1) { create(:group, parent: group) } let(:subgroup_1) { create(:group, parent: group) }
let(:subgroup_1_1) { create(:group, parent: subgroup_1) } let(:subgroup_1_1) { create(:group, parent: subgroup_1) }
let(:project_template) { create(:project, :public, namespace: subgroup_1) } let(:subgroup_1_1_1) { create(:group, parent: subgroup_1_1) }
let(:subgroup_1_2) { create(:group, parent: subgroup_1) }
let(:subgroup_1_2_1) { create(:group, parent: subgroup_1_2) }
let(:subgroup_2) { create(:group, parent: group) }
let(:subgroup_2_1) { create(:group, parent: subgroup_2) }
let(:project_template) { create(:project, :public, namespace: subgroup_1_2) }
let(:namespace_id) { nil } let(:namespace_id) { nil }
let(:group_with_project_templates_id) { nil } let(:group_with_project_templates_id) { nil }
...@@ -97,17 +115,25 @@ describe Projects::CreateFromTemplateService do ...@@ -97,17 +115,25 @@ describe Projects::CreateFromTemplateService do
describe 'creating project from a Group project template', :postgresql do describe 'creating project from a Group project template', :postgresql do
let(:project_name) { project_template.name } let(:project_name) { project_template.name }
let(:group_with_project_templates_id) { subgroup_1.id } let(:group_with_project_templates_id) { subgroup_1_2.id }
let(:group2) { create(:group) } let(:group2) { create(:group) }
before do before do
subgroup_1.update!(custom_project_templates_group_id: subgroup_1_2.id)
group.add_maintainer(user) group.add_maintainer(user)
group2.add_maintainer(user) group2.add_maintainer(user)
end end
context "when the namespace is out of the hierarchy of the Group's owning the template" do shared_examples 'a persisted project' do
let(:namespace_id) { group2.id } it "is persisted" do
project = subject.execute
expect(project).to be_saved
expect(project.import_scheduled?).to be(true)
end
end
shared_examples 'a project that isn\'t persisted' do
it "isn't persisted" do it "isn't persisted" do
project = subject.execute project = subject.execute
...@@ -116,32 +142,57 @@ describe Projects::CreateFromTemplateService do ...@@ -116,32 +142,57 @@ describe Projects::CreateFromTemplateService do
end end
end end
shared_examples 'a persisted project' do context 'when the namespace is not a descendant of the Group owning the template' do
it "is persisted" do context 'when project is created under a group that is outside the hierarchy its root ancestor group' do
project = subject.execute let(:namespace_id) { group2.id }
expect(project).to be_saved it_behaves_like 'a project that isn\'t persisted'
expect(project.import_scheduled?).to be(true)
end end
context 'when project is created under a group that is a descendant of its root ancestor group' do
let(:namespace_id) { subgroup_2.id }
it_behaves_like 'a project that isn\'t persisted'
end end
context 'when project is created under a top level group' do context 'when project is created under a subgroup that is a descendant of its root ancestor group' do
let(:namespace_id) { group.id } let(:namespace_id) { subgroup_2_1.id }
it_behaves_like 'a persisted project' it_behaves_like 'a project that isn\'t persisted'
end
end end
context 'when project is created under a subgroup' do context 'when the namespace is inside the hierarchy of the Group owning the template' do
context 'when project is created under its parent group' do
let(:namespace_id) { subgroup_1.id } let(:namespace_id) { subgroup_1.id }
it_behaves_like 'a persisted project' it_behaves_like 'a persisted project'
end end
context 'when project is created under a nested subgroup' do context 'when project is created under the same group' do
let(:namespace_id) { subgroup_1_2.id }
it_behaves_like 'a persisted project'
end
context 'when project is created under its descendant group' do
let(:namespace_id) { subgroup_1_2_1.id }
it_behaves_like 'a persisted project'
end
context 'when project is created under a group that is a descendant of its parent group' do
let(:namespace_id) { subgroup_1_1.id } let(:namespace_id) { subgroup_1_1.id }
it_behaves_like 'a persisted project' it_behaves_like 'a persisted project'
end end
context 'when project is created under a subgroup that is a descendant of its parent group' do
let(:namespace_id) { subgroup_1_1_1.id }
it_behaves_like 'a persisted project'
end
end
end end
end end
end end
...@@ -16980,13 +16980,13 @@ msgstr "" ...@@ -16980,13 +16980,13 @@ msgstr ""
msgid "is invalid because there is upstream lock" msgid "is invalid because there is upstream lock"
msgstr "" msgstr ""
msgid "is not a valid X509 certificate." msgid "is not a descendant of the Group owning the template"
msgstr "" msgstr ""
msgid "is not an email you own" msgid "is not a valid X509 certificate."
msgstr "" msgstr ""
msgid "is out of the hierarchy of the Group owning the template" msgid "is not an email you own"
msgstr "" msgstr ""
msgid "issue" msgid "issue"
......
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