Commit 5c6b1b47 authored by Thong Kuah's avatar Thong Kuah

Merge branch 'sh-use-template-project-id-backend' into 'master'

Add backend support for selecting custom templates by ID

See merge request gitlab-org/gitlab!18178
parents e526ee6e f7952cf7
......@@ -376,6 +376,7 @@ class ProjectsController < Projects::ApplicationController
:tag_list,
:visibility_level,
:template_name,
:template_project_id,
:merge_method,
:initialize_with_readme,
......
......@@ -4,8 +4,11 @@ module Projects
class CreateFromTemplateService < BaseService
include Gitlab::Utils::StrongMemoize
attr_reader :template_name
def initialize(user, params)
@current_user, @params = user, params.to_h.dup
@template_name = @params.delete(:template_name).presence
end
def execute
......@@ -21,12 +24,6 @@ module Projects
file&.close
end
def template_name
strong_memoize(:template_name) do
params.delete(:template_name).presence
end
end
private
def validate_template!
......
......@@ -13,7 +13,7 @@ module Projects
end
def execute
if @params[:template_name].present?
if create_from_template?
return ::Projects::CreateFromTemplateService.new(current_user, params).execute
end
......@@ -184,6 +184,10 @@ module Projects
private
def create_from_template?
@params[:template_name].present? || @params[:template_project_id].present?
end
def import_schedule
if @project.errors.empty?
@project.import_state.schedule if @project.import? && !@project.bare_repository_import?
......
---
title: Add backend support for selecting custom templates by ID
merge_request: 18178
author:
type: fixed
......@@ -182,12 +182,20 @@ module EE
email_opted_in_source_id == EMAIL_OPT_IN_SOURCE_ID_GITLAB_COM ? 'GitLab.com' : ''
end
def available_custom_project_templates(search: nil, subgroup_id: nil)
def available_custom_project_templates(search: nil, subgroup_id: nil, project_id: nil)
templates = ::Gitlab::CurrentSettings.available_custom_project_templates(subgroup_id)
params = {}
if project_id
templates = templates.where(id: project_id)
else
params = { search: search, sort: 'name_asc' }
end
::ProjectsFinder.new(current_user: self,
project_ids_relation: templates,
params: { search: search, sort: 'name_asc' })
params: params)
.execute
end
......
......@@ -6,6 +6,16 @@ module EE
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
attr_reader :template_project_id, :subgroup_id
override :initialize
def initialize(user, params)
super
@template_project_id = @params.delete(:template_project_id).to_i if @params[:template_project_id].present?
@subgroup_id = @params.delete(:group_with_project_templates_id).presence
end
override :execute
def execute
return super unless use_custom_template?
......@@ -27,13 +37,19 @@ module EE
return true if template_project.present?
if template_project_id.present?
project.errors.add(:template_project_id,
_("%{template_project_id} is unknown or invalid" % { template_project_id: template_project_id }))
else
project.errors.add(:template_name, _("'%{template_name}' is unknown or invalid" % { template_name: template_name }))
end
false
end
def use_custom_template?
strong_memoize(:use_custom_template) do
template_name &&
template_requested? &&
::Gitlab::Utils.to_boolean(params.delete(:use_custom_template)) &&
::Gitlab::CurrentSettings.custom_project_templates_enabled?
end
......@@ -41,13 +57,19 @@ module EE
def template_project
strong_memoize(:template_project) do
templates =
if template_project_id.present?
current_user.available_custom_project_templates(project_id: template_project_id, subgroup_id: subgroup_id)
else
current_user.available_custom_project_templates(search: template_name, subgroup_id: subgroup_id)
.first
end
templates.first
end
end
def subgroup_id
@subgroup_id ||= params.delete(:group_with_project_templates_id).presence
def template_requested?
template_name.present? || template_project_id.is_a?(Integer)
end
# rubocop: disable CodeReuse/ActiveRecord
......
......@@ -12,7 +12,7 @@ module EE
mirror_user_id = current_user.id if mirror
mirror_trigger_builds = params.delete(:mirror_trigger_builds)
ci_cd_only = ::Gitlab::Utils.to_boolean(params.delete(:ci_cd_only))
group_with_project_templates_id = params.delete(:group_with_project_templates_id) if params[:template_name].blank?
group_with_project_templates_id = params.delete(:group_with_project_templates_id) if params[:template_name].blank? && params[:template_project_id].blank?
project = super do |project|
# Repository size limit comes as MB from the view
......
......@@ -302,6 +302,7 @@ describe User do
let!(:private_project) { create :project, :private, namespace: group, name: 'private_project' }
let!(:internal_project) { create :project, :internal, namespace: group, name: 'internal_project' }
let!(:public_project) { create :project, :public, namespace: group, name: 'public_project' }
let!(:public_project_two) { create :project, :public, namespace: group, name: 'public_project_second' }
it 'returns public projects' do
expect(user.available_custom_project_templates).to include public_project
......@@ -332,9 +333,27 @@ describe User do
it 'allows to search available project templates by name' do
projects = user.available_custom_project_templates(search: 'publi')
expect(projects.count).to eq 1
expect(projects.count).to eq 2
expect(projects.first).to eq public_project
end
it 'filters by project ID' do
projects = user.available_custom_project_templates(project_id: public_project.id)
expect(projects.count).to eq 1
expect(projects).to match_array([public_project])
projects = user.available_custom_project_templates(project_id: [public_project.id, public_project_two.id])
expect(projects.count).to eq 2
expect(projects).to match_array([public_project, public_project_two])
end
it 'does not return inaccessible projects' do
projects = user.available_custom_project_templates(project_id: private_project.id)
expect(projects.count).to eq 0
end
end
end
end
......
......@@ -16,8 +16,10 @@ require 'spec_helper'
# Group 2
describe Projects::CreateFromTemplateService do
using RSpec::Parameterized::TableSyntax
let(:group) { create(:group) }
let(:project) { create(:project, :public, namespace: group) }
let!(:project) { create(:project, :public, namespace: group) }
let(:user) { create(:user) }
let(:project_name) { project.name }
let(:use_custom_template) { true }
......@@ -30,13 +32,14 @@ describe Projects::CreateFromTemplateService do
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(:template_name) { project_template.name }
let(:namespace_id) { nil }
let(:group_with_project_templates_id) { nil }
let(:project_params) do
{
path: user.to_param,
template_name: project_name,
template_name: template_name,
description: 'project description',
visibility_level: Gitlab::VisibilityLevel::PUBLIC,
use_custom_template: use_custom_template,
......@@ -49,7 +52,7 @@ describe Projects::CreateFromTemplateService do
before do
stub_licensed_features(custom_project_templates: true)
stub_ee_application_setting(custom_project_templates_group_id: group.id)
stub_ee_application_setting(custom_project_templates_group_id: subgroup_1_2.id)
end
context '#execute' do
......@@ -80,7 +83,7 @@ describe Projects::CreateFromTemplateService do
end
context 'when custom_project_template does not exist' do
let(:project_name) { 'whatever' }
let(:template_name) { 'whatever' }
it 'does not attempt to import a project' do
expect(::Projects::GitlabProjectsImportService).not_to receive(:new)
......@@ -88,10 +91,18 @@ describe Projects::CreateFromTemplateService do
end
end
shared_examples 'creates project from custom template' do |subgroup_id|
# If we move the project inside a let block it throws a SEGFAULT error
where(:use_template_name) { [true, false] }
with_them do
before do
project_params[:group_with_project_templates_id] = subgroup_id
if use_template_name
project_params[:template_name] = template_name
project_params.delete(:template_project_id)
else
project_params.delete(:template_name)
project_params[:template_project_id] = project_template.id
end
@project = subject.execute
end
......@@ -109,10 +120,6 @@ describe Projects::CreateFromTemplateService do
expect(@project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
end
end
end
it_behaves_like 'creates project from custom template', nil
it_behaves_like 'creates project from custom template', ''
describe 'creating project from a Group project template' do
let(:project_name) { project_template.name }
......@@ -204,4 +211,5 @@ describe Projects::CreateFromTemplateService do
end
end
end
end
end
......@@ -13,6 +13,20 @@ describe Projects::CreateService, '#execute' do
}
end
context 'with a template project ID' do
before do
opts.merge!(
template_project_id: 1
)
end
it 'creates a project using the template service' do
expect(::Projects::CreateFromTemplateService).to receive_message_chain(:new, :execute)
create_project(user, opts)
end
end
context 'with a CI/CD only project' do
before do
opts.merge!(
......
......@@ -358,6 +358,9 @@ msgstr[1] ""
msgid "%{tabname} changed"
msgstr ""
msgid "%{template_project_id} is unknown or invalid"
msgstr ""
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
msgstr[0] ""
......
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