summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--doc/api/issues.md51
-rw-r--r--lib/api/issues.rb23
-rw-r--r--spec/requests/api/issues_spec.rb53
4 files changed, 127 insertions, 1 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 9b0c6ba4609..771d7e4799d 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,7 @@ v 8.7.0 (unreleased)
- Expose label description in API (Mariusz Jachimowicz)
- Allow back dating on issues when created through the API
- API: Ability to update a group (Robert Schilling)
+ - API: Ability to move issues
- Fix Error 500 after renaming a project path (Stan Hu)
- Fix avatar stretching by providing a cropping feature
- API: Expose `subscribed` for issues and merge requests (Robert Schilling)
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 1c635a6cdcf..a540a27ce11 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -351,6 +351,57 @@ DELETE /projects/:id/issues/:issue_id
curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85
```
+## Move an issue
+
+Moves an issue to a different project. If the operation is successful, a status code `200` together with moved issue is returned. If the project, issue, or target project is not found, error `404` is returned. If the target project equals the source project or the user has insufficient permissions to move an issue, error `400` together with an explaining error message is returned.
+
+```
+POST /projects/:id/issues/:issue_id/move
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of a project |
+| `issue_id` | integer | yes | The ID of a project's issue |
+| `new_project_id` | integer | yes | The ID the new project |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/4/issues/85/move
+```
+
+Example response:
+
+```json
+{
+ "id": 92,
+ "iid": 11,
+ "project_id": 5,
+ "title": "Sit voluptas tempora quisquam aut doloribus et.",
+ "description": "Repellat voluptas quibusdam voluptatem exercitationem.",
+ "state": "opened",
+ "created_at": "2016-04-05T21:41:45.652Z",
+ "updated_at": "2016-04-07T12:20:17.596Z",
+ "labels": [],
+ "milestone": null,
+ "assignee": {
+ "name": "Miss Monserrate Beier",
+ "username": "axel.block",
+ "id": 12,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/46f6f7dc858ada7be1853f7fb96e81da?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/axel.block"
+ },
+ "author": {
+ "name": "Kris Steuber",
+ "username": "solon.cremin",
+ "id": 10,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/7a190fecbaa68212a4b68aeb6e3acd10?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/solon.cremin"
+ }
+}
+```
+
## Comments on issues
Comments are done via the [notes](notes.md) resource.
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index c4ea05ee6cf..894d9794322 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -195,6 +195,29 @@ module API
end
end
+ # Move an existing issue
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # issue_id (required) - The ID of a project issue
+ # new_project_id (required) - The ID of the new project
+ # Example Request:
+ # POST /projects/:id/issues/:issue_id/move
+ post ":id/issues/:issue_id/move" do
+ required_attributes! [:new_project_id]
+
+ issue = user_project.issues.find(params[:issue_id])
+ new_project = Project.find(params[:new_project_id])
+
+ begin
+ issue = ::Issues::MoveService.new(user_project, current_user).execute(issue, new_project)
+ present issue, with: Entities::Issue
+ rescue ::Issues::MoveService::MoveError => error
+ render_api_error!(error.message, 400)
+ end
+ end
+
+ #
# Delete a project issue
#
# Parameters:
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 822d3ad3017..db4ee46975a 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -7,7 +7,7 @@ describe API::API, api: true do
let(:author) { create(:author) }
let(:assignee) { create(:assignee) }
let(:admin) { create(:user, :admin) }
- let!(:project) { create(:project, :public, namespace: user.namespace ) }
+ let!(:project) { create(:project, :public, creator_id: user.id, namespace: user.namespace ) }
let!(:closed_issue) do
create :closed_issue,
author: user,
@@ -501,4 +501,55 @@ describe API::API, api: true do
end
end
end
+
+ describe '/projects/:id/issues/:issue_id/move' do
+ let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
+ let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
+
+ it 'moves an issue' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
+ new_project_id: target_project.id
+
+ expect(response.status).to eq(201)
+ expect(json_response['project_id']).to eq(target_project.id)
+ end
+
+ it 'returns an error if target and source project are the same' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
+ new_project_id: project.id
+
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Cannot move issue to project it originates from!')
+ end
+
+ it "returns an error if I don't have the permission" do
+ post api("/projects/#{project.id}/issues/#{issue.id}/move", user),
+ new_project_id: target_project2.id
+
+ expect(response.status).to eq(400)
+ expect(json_response['message']).to eq('Cannot move issue due to insufficient permissions!')
+ end
+
+ it 'moves the issue to another namespace if I am admin' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/move", admin),
+ new_project_id: target_project2.id
+
+ expect(response.status).to eq(201)
+ expect(json_response['project_id']).to eq(target_project2.id)
+ end
+
+ it 'returns 404 if the source issue is not found' do
+ post api("/projects/#{project.id}/issues/123/move", user),
+ new_project_id: target_project.id
+
+ expect(response.status).to eq(404)
+ end
+
+ it 'returns 404 if the target project is not found' do
+ post api("/projects/1234/issues/#{issue.id}/move", user),
+ new_project_id: target_project.id
+
+ expect(response.status).to eq(404)
+ end
+ end
end