summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Schilling <rschilling@student.tugraz.at>2016-06-28 18:04:44 +0200
committerRobert Schilling <rschilling@student.tugraz.at>2016-07-01 14:52:04 +0200
commit87ac9c9850d602fd18654498ab3fa005d2b85ac7 (patch)
treeb9fe37acee0d9dae2f931504ebdf3ff7a2ec06e2
parent3942621329b20307c1676d60324c8f47ea1e1b37 (diff)
downloadgitlab-ce-todos-api.tar.gz
Support creating a todo on issuables via APItodos-api
-rw-r--r--doc/api/issues.md93
-rw-r--r--doc/api/merge_requests.md98
-rw-r--r--lib/api/todos.rb30
-rw-r--r--spec/requests/api/todos_spec.rb49
4 files changed, 268 insertions, 2 deletions
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 708fc691f67..3ced787b23e 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -594,12 +594,103 @@ Example response:
"id": 11,
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/5224fd70153710e92fb8bcf79ac29d67?s=80&d=identicon",
- "web_url": "http://lgitlab.example.com/u/orville"
+ "web_url": "https://gitlab.example.com/u/orville"
},
"subscribed": false
}
```
+## Create a todo
+
+Manually creates a todo for the current user on an issue. If the request is
+successful, status code `200` together with the created todo is returned. If
+there already exists a todo for the user on that issue, status code `304` is
+returned.
+
+```
+POST /projects/:id/issues/:issue_id/todo
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of a project |
+| `issue_id` | integer | yes | The ID of a project's issue |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/issues/93/todo
+```
+
+Example response:
+
+```json
+{
+ "id": 112,
+ "project": {
+ "id": 5,
+ "name": "Gitlab Ci",
+ "name_with_namespace": "Gitlab Org / Gitlab Ci",
+ "path": "gitlab-ci",
+ "path_with_namespace": "gitlab-org/gitlab-ci"
+ },
+ "author": {
+ "name": "Administrator",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/root"
+ },
+ "action_name": "marked",
+ "target_type": "Issue",
+ "target": {
+ "id": 93,
+ "iid": 10,
+ "project_id": 5,
+ "title": "Vel voluptas atque dicta mollitia adipisci qui at.",
+ "description": "Tempora laboriosam sint magni sed voluptas similique.",
+ "state": "closed",
+ "created_at": "2016-06-17T07:47:39.486Z",
+ "updated_at": "2016-07-01T11:09:13.998Z",
+ "labels": [],
+ "milestone": {
+ "id": 26,
+ "iid": 1,
+ "project_id": 5,
+ "title": "v0.0",
+ "description": "Accusantium nostrum rerum quae quia quis nesciunt suscipit id.",
+ "state": "closed",
+ "created_at": "2016-06-17T07:47:33.832Z",
+ "updated_at": "2016-06-17T07:47:33.832Z",
+ "due_date": null
+ },
+ "assignee": {
+ "name": "Jarret O'Keefe",
+ "username": "francisca",
+ "id": 14,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/a7fa515d53450023c83d62986d0658a8?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/francisca"
+ },
+ "author": {
+ "name": "Maxie Medhurst",
+ "username": "craig_rutherford",
+ "id": 12,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/a0d477b3ea21970ce6ffcbb817b0b435?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/craig_rutherford"
+ },
+ "subscribed": true,
+ "user_notes_count": 7,
+ "upvotes": 0,
+ "downvotes": 0
+ },
+ "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10",
+ "body": "Vel voluptas atque dicta mollitia adipisci qui at.",
+ "state": "pending",
+ "created_at": "2016-07-01T11:09:13.992Z"
+}
+```
+
## Comments on issues
Comments are done via the [notes](notes.md) resource.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 2930f615fc1..f60b0d0ebc6 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -776,3 +776,101 @@ Example response:
"subscribed": false
}
```
+
+## Create a todo
+
+Manually creates a todo for the current user on a merge request. If the
+request is successful, status code `200` together with the created todo is
+returned. If there already exists a todo for the user on that merge request,
+status code `304` is returned.
+
+```
+POST /projects/:id/merge_requests/:merge_request_id/todo
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer | yes | The ID of a project |
+| `merge_request_id` | integer | yes | The ID of the merge request |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/merge_requests/27/todo
+```
+
+Example response:
+
+```json
+{
+ "id": 113,
+ "project": {
+ "id": 3,
+ "name": "Gitlab Ci",
+ "name_with_namespace": "Gitlab Org / Gitlab Ci",
+ "path": "gitlab-ci",
+ "path_with_namespace": "gitlab-org/gitlab-ci"
+ },
+ "author": {
+ "name": "Administrator",
+ "username": "root",
+ "id": 1,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/root"
+ },
+ "action_name": "marked",
+ "target_type": "MergeRequest",
+ "target": {
+ "id": 27,
+ "iid": 7,
+ "project_id": 3,
+ "title": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
+ "description": "Veniam sunt nihil modi earum cumque illum delectus. Nihil ad quis distinctio quia. Autem eligendi at quibusdam repellendus.",
+ "state": "opened",
+ "created_at": "2016-06-17T07:48:04.330Z",
+ "updated_at": "2016-07-01T11:14:15.537Z",
+ "target_branch": "allow_regex_for_project_skip_ref",
+ "source_branch": "backup",
+ "upvotes": 0,
+ "downvotes": 0,
+ "author": {
+ "name": "Jarret O'Keefe",
+ "username": "francisca",
+ "id": 14,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/a7fa515d53450023c83d62986d0658a8?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/francisca"
+ },
+ "assignee": {
+ "name": "Dr. Gabrielle Strosin",
+ "username": "barrett.krajcik",
+ "id": 4,
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/733005fcd7e6df12d2d8580171ccb966?s=80&d=identicon",
+ "web_url": "https://gitlab.example.com/u/barrett.krajcik"
+ },
+ "source_project_id": 3,
+ "target_project_id": 3,
+ "labels": [],
+ "work_in_progress": false,
+ "milestone": {
+ "id": 27,
+ "iid": 2,
+ "project_id": 3,
+ "title": "v1.0",
+ "description": "Quis ea accusantium animi hic fuga assumenda.",
+ "state": "active",
+ "created_at": "2016-06-17T07:47:33.840Z",
+ "updated_at": "2016-06-17T07:47:33.840Z",
+ "due_date": null
+ },
+ "merge_when_build_succeeds": false,
+ "merge_status": "unchecked",
+ "subscribed": true,
+ "user_notes_count": 7
+ },
+ "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
+ "body": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
+ "state": "pending",
+ "created_at": "2016-07-01T11:14:15.530Z"
+}
+```
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 8334baad1b9..2a6bfa98ca4 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -3,6 +3,36 @@ module API
class Todos < Grape::API
before { authenticate! }
+ ISSUABLE_TYPES = {
+ 'merge_requests' => ->(id) { user_project.merge_requests.find(id) },
+ 'issues' => ->(id) { find_project_issue(id) }
+ }
+
+ resource :projects do
+ ISSUABLE_TYPES.each do |type, finder|
+ type_id_str = "#{type.singularize}_id".to_sym
+
+ # Create a todo on an issuable
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # issuable_id (required) - The ID of an issuable
+ # Example Request:
+ # POST /projects/:id/issues/:issuable_id/todo
+ # POST /projects/:id/merge_requests/:issuable_id/todo
+ post ":id/#{type}/:#{type_id_str}/todo" do
+ issuable = instance_exec(params[type_id_str], &finder)
+ todo = TodoService.new.mark_todo(issuable, current_user).first
+
+ if todo
+ present todo, with: Entities::Todo, current_user: current_user
+ else
+ not_modified!
+ end
+ end
+ end
+ end
+
resource :todos do
helpers do
def find_todos
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index f93f37e3591..92a4fa216cd 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -11,7 +11,7 @@ describe API::Todos, api: true do
let(:merge_request) { create(:merge_request, source_project: project_1) }
let!(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) }
let!(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) }
- let!(:pending_3) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
+ let!(:pending_3) { create(:todo, project: project_1, author: author_2, user: john_doe) }
let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
before do
@@ -59,6 +59,8 @@ describe API::Todos, api: true do
context 'and using the type filter' do
it 'filters based on type param' do
+ create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request)
+
get api('/todos', john_doe), { type: 'MergeRequest' }
expect(response.status).to eq(200)
@@ -140,4 +142,49 @@ describe API::Todos, api: true do
end
end
end
+
+ shared_examples 'an issuable' do |issuable_type|
+ it 'creates a todo on an issuable' do
+ post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.id}/todo", john_doe)
+
+ expect(response.status).to eq(201)
+ expect(json_response['project']).to be_a Hash
+ expect(json_response['author']).to be_a Hash
+ expect(json_response['target_type']).to eq(issuable.class.name)
+ expect(json_response['target']).to be_a Hash
+ expect(json_response['target_url']).to be_present
+ expect(json_response['body']).to be_present
+ expect(json_response['state']).to eq('pending')
+ expect(json_response['action_name']).to eq('marked')
+ expect(json_response['created_at']).to be_present
+ end
+
+ it 'returns 304 there already exist a todo on that issuable' do
+ create(:todo, project: project_1, author: author_1, user: john_doe, target: issuable)
+
+ post api("/projects/#{project_1.id}/#{issuable_type}/#{issuable.id}/todo", john_doe)
+
+ expect(response.status).to eq(304)
+ end
+
+ it 'returns 404 if the issuable is not found' do
+ post api("/projects/#{project_1.id}/#{issuable_type}/123/todo", john_doe)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+ describe 'POST :id/issuable_type/:issueable_id/todo' do
+ context 'for an issue' do
+ it_behaves_like 'an issuable', 'issues' do
+ let(:issuable) { create(:issue, author: author_1, project: project_1) }
+ end
+ end
+
+ context 'for a merge request' do
+ it_behaves_like 'an issuable', 'merge_requests' do
+ let(:issuable) { merge_request }
+ end
+ end
+ end
end