summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--doc/api/projects.md3
-rw-r--r--lib/api/projects.rb16
-rw-r--r--spec/requests/api/fork_spec.rb54
4 files changed, 70 insertions, 4 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 3f52fab74d7..49c0c906e62 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
+ - Add abillity to fork to a specific namespace using API. @ritave <olaf@tomalka.me>
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Filter tags by name !6121
- Make push events have equal vertical spacing.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index a62aaee14d7..fe3c8709d13 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -514,7 +514,7 @@ invalid, 400 is returned.
### Fork project
-Forks a project into the user namespace of the authenticated user.
+Forks a project into the user namespace of the authenticated user or the one provided.
```
POST /projects/fork/:id
@@ -523,6 +523,7 @@ POST /projects/fork/:id
Parameters:
- `id` (required) - The ID or NAMESPACE/PROJECT_NAME of the project to be forked
+- `namespace` (optional) - The ID or path of the namespace that the project will be forked to
### Star a project
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index a1fd598414a..d35ec73c8c1 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -189,16 +189,28 @@ module API
end
end
- # Fork new project for the current user.
+ # Fork new project for the current user or provided namespace.
#
# Parameters:
# id (required) - The ID of a project
+ # namespace (optional) - The ID or name of the namespace that the project will be forked into.
# Example Request
# POST /projects/fork/:id
post 'fork/:id' do
+ attrs = {}
+ namespace_id = params[:namespace]
+ if namespace_id.present?
+ namespace = Namespace.find_by(id: namespace_id) || Namespace.find_by_path_or_name(namespace_id)
+ if namespace.nil?
+ not_found!('Target Namespace')
+ end
+ authorize! :create_projects, namespace
+ attrs[:namespace] = namespace
+ end
@forked_project =
::Projects::ForkService.new(user_project,
- current_user).execute
+ current_user,
+ attrs).execute
if @forked_project.errors.any?
conflict!(@forked_project.errors.messages)
else
diff --git a/spec/requests/api/fork_spec.rb b/spec/requests/api/fork_spec.rb
index f802fcd2d2e..97f17efc053 100644
--- a/spec/requests/api/fork_spec.rb
+++ b/spec/requests/api/fork_spec.rb
@@ -3,9 +3,15 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
- let(:user2) { create(:user) }
+ let(:user2) { create(:user, username: 'user2_name') }
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
+ let(:group) { create(:group, name: 'group_name') }
+ let(:group2) do
+ group = create(:group, name: 'group2_name')
+ group.add_owner(user2)
+ group
+ end
let(:project) do
create(:project, creator_id: user.id, namespace: user.namespace)
@@ -58,6 +64,52 @@ describe API::API, api: true do
expect(response).to have_http_status(404)
expect(json_response['message']).to eq('404 Project Not Found')
end
+
+ it 'forks with explicit own user namespace id' do
+ post api("/projects/fork/#{project.id}?namespace=#{user2.namespace.id}", user2)
+ expect(response).to have_http_status(201)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ end
+
+ it 'forks with explicit own user name as namespace' do
+ post api("/projects/fork/#{project.id}?namespace=#{user2.username}", user2)
+ expect(response).to have_http_status(201)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ end
+
+ it 'forks to another user when admin' do
+ post api("/projects/fork/#{project.id}?namespace=#{user2.username}", admin)
+ expect(response).to have_http_status(201)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ end
+
+ it 'fails if trying to fork to another user when not admin' do
+ post api("/projects/fork/#{project.id}?namespace=#{admin.namespace.id}", user2)
+ expect(response).to have_http_status(403)
+ end
+
+ it 'fails if trying to fork to non-existent namespace' do
+ post api("/projects/fork/#{project.id}?namespace=42424242", user2)
+ expect(response).to have_http_status(404)
+ expect(json_response['message']).to eq('404 Target Namespace Not Found')
+ end
+
+ it 'forks to owned group' do
+ post api("/projects/fork/#{project.id}?namespace=#{group2.name}", user2)
+ expect(response).to have_http_status(201)
+ expect(json_response['namespace']['name']).to eq(group2.name)
+ end
+
+ it 'fails to fork to not owned group' do
+ post api("/projects/fork/#{project.id}?namespace=#{group.name}", user2)
+ expect(response).to have_http_status(403)
+ end
+
+ it 'forks to not owned group when admin' do
+ post api("/projects/fork/#{project.id}?namespace=#{group.name}", admin)
+ expect(response).to have_http_status(201)
+ expect(json_response['namespace']['name']).to eq(group.name)
+ end
end
context 'when unauthenticated' do