diff options
-rw-r--r-- | doc/api/issues.md | 93 | ||||
-rw-r--r-- | doc/api/merge_requests.md | 98 | ||||
-rw-r--r-- | lib/api/todos.rb | 30 | ||||
-rw-r--r-- | spec/requests/api/todos_spec.rb | 49 |
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 |