diff options
-rw-r--r-- | app/models/concerns/taskable.rb | 7 | ||||
-rw-r--r-- | changelogs/unreleased/51636-task-list-api-pderichs.yml | 5 | ||||
-rw-r--r-- | doc/api/issues.md | 50 | ||||
-rw-r--r-- | doc/api/merge_requests.md | 76 | ||||
-rw-r--r-- | lib/api/entities.rb | 4 | ||||
-rw-r--r-- | spec/requests/api/task_completion_status_spec.rb | 85 |
6 files changed, 206 insertions, 21 deletions
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb index 2f0e078c807..b42adad94ba 100644 --- a/app/models/concerns/taskable.rb +++ b/app/models/concerns/taskable.rb @@ -75,4 +75,11 @@ module Taskable def task_status_short task_status(short: true) end + + def task_completion_status + @task_completion_status ||= { + count: tasks.summary.item_count, + completed_count: tasks.summary.complete_count + } + end end diff --git a/changelogs/unreleased/51636-task-list-api-pderichs.yml b/changelogs/unreleased/51636-task-list-api-pderichs.yml new file mode 100644 index 00000000000..f18a0936ab2 --- /dev/null +++ b/changelogs/unreleased/51636-task-list-api-pderichs.yml @@ -0,0 +1,5 @@ +--- +title: Add task count and completed count to responses of Issue and MR +merge_request: 28859 +author: +type: added diff --git a/doc/api/issues.md b/doc/api/issues.md index 4fb3626f637..0d96cfa1b21 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -135,7 +135,11 @@ Example response: "award_emoji":"http://example.com/api/v4/projects/1/issues/76/award_emoji", "project":"http://example.com/api/v4/projects/1" }, - "subscribed": false + "subscribed": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -265,7 +269,11 @@ Example response: "award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "project":"http://example.com/api/v4/projects/4" }, - "subscribed": false + "subscribed": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -403,7 +411,11 @@ Example response: "award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "project":"http://example.com/api/v4/projects/4" }, - "subscribed": false + "subscribed": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -500,6 +512,10 @@ Example response: "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 } } ``` @@ -583,6 +599,10 @@ Example response: "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 } } ``` @@ -674,6 +694,10 @@ Example response: "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 } } ``` @@ -780,6 +804,10 @@ Example response: "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 } } ``` @@ -865,6 +893,10 @@ Example response: "notes": "http://example.com/api/v4/projects/1/issues/2/notes", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "project": "http://example.com/api/v4/projects/1" + }, + "task_completion_status":{ + "count":0, + "completed_count":0 } } ``` @@ -931,7 +963,11 @@ Example response: "due_date": null, "web_url": "http://example.com/example/example/issues/12", "confidential": false, - "discussion_locked": false + "discussion_locked": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -1029,7 +1065,11 @@ Example response: "due_date": null, "web_url": "http://example.com/example/example/issues/110", "confidential": false, - "discussion_locked": false + "discussion_locked": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } }, "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10", "body": "Vel voluptas atque dicta mollitia adipisci qui at.", diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 9529a9ec1f5..96a956ad03a 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -138,7 +138,11 @@ Parameters: "human_time_estimate": null, "human_total_time_spent": null }, - "squash": false + "squash": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -280,7 +284,11 @@ Parameters: "human_time_estimate": null, "human_total_time_spent": null }, - "squash": false + "squash": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -410,7 +418,11 @@ Parameters: "human_time_estimate": null, "human_total_time_spent": null }, - "squash": false + "squash": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ] ``` @@ -545,7 +557,11 @@ Parameters: "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, "diverged_commits_count": 2, - "rebase_in_progress": false + "rebase_in_progress": false, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -579,7 +595,7 @@ Parameters: "state": "active", "avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon", "web_url": "http://localhost/user2" - }, + } ] ``` @@ -702,7 +718,11 @@ Parameters: "total_time_spent": 0, "human_time_estimate": null, "human_total_time_spent": null - } + }, + "task_completion_status":{ + "count":0, + "completed_count":0 + }, "changes": [ { "old_path": "VERSION", @@ -865,7 +885,11 @@ POST /projects/:id/merge_requests "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -1002,7 +1026,11 @@ Must include at least one non-required attribute from above. "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -1155,13 +1183,17 @@ Parameters: "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` ## Returns the up to date merge-ref HEAD commit -Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge` +Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge` ref, of the target project repository, if possible. This ref will have the state the target branch would have if a regular merge action was taken. @@ -1309,7 +1341,11 @@ Parameters: "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -1345,7 +1381,7 @@ If the rebase operation is ongoing, the response will include the following: ```json { - "rebase_in_progress": true + "rebase_in_progress": true, "merge_error": null } ``` @@ -1356,7 +1392,7 @@ the following: ```json { "rebase_in_progress": false, - "merge_error": null, + "merge_error": null } ``` @@ -1365,7 +1401,7 @@ If the rebase operation fails, the response will include the following: ```json { "rebase_in_progress": false, - "merge_error": "Rebase failed. Please rebase locally", + "merge_error": "Rebase failed. Please rebase locally" } ``` @@ -1572,7 +1608,11 @@ Example response: "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` @@ -1701,7 +1741,11 @@ Example response: "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" }, - "diverged_commits_count": 2 + "diverged_commits_count": 2, + "task_completion_status":{ + "count":0, + "completed_count":0 + } } ``` diff --git a/lib/api/entities.rb b/lib/api/entities.rb index b1b6e7bd7b9..f8b950cb55d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -576,6 +576,8 @@ module API expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue| issue end + + expose :task_completion_status end class Issue < IssueBasic @@ -724,6 +726,8 @@ module API end expose :squash + + expose :task_completion_status end class MergeRequest < MergeRequestBasic diff --git a/spec/requests/api/task_completion_status_spec.rb b/spec/requests/api/task_completion_status_spec.rb new file mode 100644 index 00000000000..ee2531197b1 --- /dev/null +++ b/spec/requests/api/task_completion_status_spec.rb @@ -0,0 +1,85 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'task completion status response' do + set(:user) { create(:user) } + set(:project) do + create(:project, :public, creator_id: user.id, namespace: user.namespace) + end + + shared_examples 'taskable completion status provider' do |path| + samples = [ + { + description: '', + expected_count: 0, + expected_completed_count: 0 + }, + { + description: 'Lorem ipsum', + expected_count: 0, + expected_completed_count: 0 + }, + { + description: %{- [ ] task 1 + - [x] task 2 }, + expected_count: 2, + expected_completed_count: 1 + }, + { + description: %{- [ ] task 1 + - [ ] task 2 }, + expected_count: 2, + expected_completed_count: 0 + }, + { + description: %{- [x] task 1 + - [x] task 2 }, + expected_count: 2, + expected_completed_count: 2 + }, + { + description: %{- [ ] task 1}, + expected_count: 1, + expected_completed_count: 0 + }, + { + description: %{- [x] task 1}, + expected_count: 1, + expected_completed_count: 1 + } + ] + samples.each do |sample_data| + context "with a description of #{sample_data[:description].inspect}" do + before do + taskable.update!(description: sample_data[:description]) + + get api("#{path}?iids[]=#{taskable.iid}", user) + end + + it { expect(response).to have_gitlab_http_status(200) } + + it 'returns the expected results' do + expect(json_response).to be_an Array + expect(json_response).not_to be_empty + + task_completion_status = json_response.first['task_completion_status'] + expect(task_completion_status['count']).to eq(sample_data[:expected_count]) + expect(task_completion_status['completed_count']).to eq(sample_data[:expected_completed_count]) + end + end + end + end + + context 'task list completion status for issues' do + it_behaves_like 'taskable completion status provider', '/issues' do + let(:taskable) { create(:issue, project: project, author: user) } + end + end + + context 'task list completion status for merge_requests' do + it_behaves_like 'taskable completion status provider', '/merge_requests' do + let(:taskable) { create(:merge_request, source_project: project, target_project: project, author: user) } + end + end +end |