Commit a2108973 authored by Stan Hu's avatar Stan Hu Committed by Mayra Cabrera

Fix project creation with templates using /projects/user/:id API

Previously creation with an instance or group template with POST
`/api/v4/projects worked`, but `/api/v4/projects/user/:id` did not
because the latter did not accept the `use_custom_templates` parameter
due to an oversight.

This commit fixes this by adding the `optional_create_project_params`
Grape API and add tests for project creation via the user endpoint.

Closes https://gitlab.com/gitlab-org/gitlab/issues/37004
parent 3fe00c56
---
title: Fix project creation with templates using /projects/user/:id API
merge_request: 20590
author:
type: fixed
...@@ -182,120 +182,165 @@ describe API::Projects do ...@@ -182,120 +182,165 @@ describe API::Projects do
end end
end end
describe 'POST /projects' do # Assumes the following variables are defined:
shared_examples 'creates projects with templates' do # group
before do # project
group.add_maintainer(user) # new_project_name
stub_licensed_features(custom_project_templates: true) # api_call
stub_ee_application_setting(custom_project_templates_group_id: group.id) shared_examples 'creates projects with templates' do
before do
group.add_maintainer(user)
stub_licensed_features(custom_project_templates: true)
stub_ee_application_setting(custom_project_templates_group_id: group.id)
end
it 'creates a project using a template' do
expect(ProjectExportWorker).to receive(:perform_async).and_call_original
Sidekiq::Testing.fake! do
expect { api_call }.to change { Project.count }.by(1)
end end
it 'creates a project using a template' do expect(response).to have_gitlab_http_status(201)
expect(ProjectExportWorker).to receive(:perform_async).and_call_original
Sidekiq::Testing.fake! do project = Project.find(json_response['id'])
expect { post api('/projects', user), params: project_params } expect(project.name).to eq(new_project_name)
.to change { Project.count }.by(1) end
end
expect(response).to have_gitlab_http_status(201) it 'returns a 400 error for an invalid template name' do
project_params.delete(:template_project_id)
project_params[:template_name] = 'bogus-template'
project = Project.find(json_response['id']) expect { api_call }.not_to change { Project.count }
expect(project.name).to eq(new_project_name)
end
it 'returns a 400 error for an invalid template name' do expect(response).to have_gitlab_http_status(400)
project_params.delete(:template_project_id) expect(json_response['message']['template_name']).to eq(["'bogus-template' is unknown or invalid"])
project_params[:template_name] = 'bogus-template' end
expect { post api('/projects', user), params: project_params } it 'returns a 400 error for an invalid template ID' do
.not_to change { Project.count } project_params.delete(:template_name)
new_project = create(:project)
project_params[:template_project_id] = new_project.id
expect(response).to have_gitlab_http_status(400) expect { api_call }.not_to change { Project.count }
expect(json_response['message']['template_name']).to eq(["'bogus-template' is unknown or invalid"])
end
it 'returns a 400 error for an invalid template ID' do expect(response).to have_gitlab_http_status(400)
project_params.delete(:template_name) expect(json_response['message']['template_project_id']).to eq(["#{new_project.id} is unknown or invalid"])
new_project = create(:project) end
project_params[:template_project_id] = new_project.id end
expect { post api('/projects', user), params: project_params } shared_context 'base instance template models' do
.not_to change { Project.count } let(:group) { create(:group) }
let!(:project) { create(:project, :public, namespace: group) }
let(:new_project_name) { "project-#{SecureRandom.hex}" }
end
expect(response).to have_gitlab_http_status(400) shared_context 'instance template name' do
expect(json_response['message']['template_project_id']).to eq(["#{new_project.id} is unknown or invalid"]) include_context 'base instance template models'
end
let(:project_params) do
{
template_name: project.name,
name: new_project_name,
path: new_project_name,
use_custom_template: true,
namespace_id: group.id
}
end end
end
context 'with instance-level templates' do shared_context 'instance template ID' do
let(:group) { create(:group) } include_context 'base instance template models'
let!(:project) { create(:project, :public, namespace: group) }
let(:new_project_name) { "project-#{SecureRandom.hex}" } let(:project_params) do
{
context 'using template name' do template_project_id: project.id,
let(:project_params) do name: new_project_name,
{ path: new_project_name,
template_name: project.name, use_custom_template: true,
name: new_project_name, namespace_id: group.id
path: new_project_name, }
use_custom_template: true, end
namespace_id: group.id end
}
end shared_context 'base group template models' do
let(:parent_group) { create(:group) }
let(:subgroup) { create(:group, :public, parent: parent_group) }
let(:group) { subgroup }
let!(:project) { create(:project, :public, namespace: subgroup) }
let(:new_project_name) { "project-#{SecureRandom.hex}" }
end
shared_context 'group template name' do
include_context 'base group template models'
let(:project_params) do
{
template_name: project.name,
name: new_project_name,
path: new_project_name,
use_custom_template: true,
group_with_project_templates_id: subgroup.id,
namespace_id: subgroup.id
}
end
end
shared_context 'group template ID' do
include_context 'base group template models'
let(:project_params) do
{
template_project_id: project.id,
name: new_project_name,
path: new_project_name,
use_custom_template: true,
group_with_project_templates_id: subgroup.id,
namespace_id: subgroup.id
}
end
end
describe 'POST /projects/user/:id' do
let(:admin) { create(:admin) }
let(:api_call) { post api("/projects/user/#{user.id}", admin), params: project_params }
context 'with templates' do
include_context 'instance template name' do
it_behaves_like 'creates projects with templates' it_behaves_like 'creates projects with templates'
end end
context 'using template project ID' do include_context 'instance template ID' do
let(:project_params) do it_behaves_like 'creates projects with templates'
{ end
template_project_id: project.id,
name: new_project_name, include_context 'group template name' do
path: new_project_name, it_behaves_like 'creates projects with templates'
use_custom_template: true, end
namespace_id: group.id
}
end
include_context 'group template ID' do
it_behaves_like 'creates projects with templates' it_behaves_like 'creates projects with templates'
end end
end end
end
context 'with group templates' do describe 'POST /projects' do
let(:parent_group) { create(:group) } let(:api_call) { post api('/projects', user), params: project_params }
let(:subgroup) { create(:group, :public, parent: parent_group) }
let(:group) { subgroup }
let!(:project) { create(:project, :public, namespace: subgroup) }
let(:new_project_name) { "project-#{SecureRandom.hex}" }
context 'using template name' do
let(:project_params) do
{
template_name: project.name,
name: new_project_name,
path: new_project_name,
use_custom_template: true,
group_with_project_templates_id: subgroup.id,
namespace_id: subgroup.id
}
end
context 'with templates' do
include_context 'instance template name' do
it_behaves_like 'creates projects with templates' it_behaves_like 'creates projects with templates'
end end
context 'using template project ID' do include_context 'instance template ID' do
let(:project_params) do it_behaves_like 'creates projects with templates'
{ end
template_project_id: project.id,
name: new_project_name, include_context 'group template name' do
path: new_project_name, it_behaves_like 'creates projects with templates'
use_custom_template: true, end
group_with_project_templates_id: subgroup.id,
namespace_id: subgroup.id
}
end
include_context 'group template ID' do
it_behaves_like 'creates projects with templates' it_behaves_like 'creates projects with templates'
end end
end end
......
...@@ -191,6 +191,7 @@ module API ...@@ -191,6 +191,7 @@ module API
optional :path, type: String, desc: 'The path of the repository' optional :path, type: String, desc: 'The path of the repository'
optional :default_branch, type: String, desc: 'The default branch of the project' optional :default_branch, type: String, desc: 'The default branch of the project'
use :optional_project_params use :optional_project_params
use :optional_create_project_params
use :create_params use :create_params
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
......
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