diff options
author | Patrick Derichs <pderichs@gitlab.com> | 2019-06-07 12:15:55 +0300 |
---|---|---|
committer | Patrick Derichs <pderichs@gitlab.com> | 2019-06-07 12:15:55 +0300 |
commit | b70d23c25a4bc54fda22135b0a76bae102cfd88b (patch) | |
tree | 3c3e7a33a22bdc0b73659446375c855d74f44414 | |
parent | d842d80e30000b9efa57e8e69117431b38c6784a (diff) |
Add task count and completed count to responses of Issue and MR
Add spec for task_completion_status
Add test cases for task_completion_status result
Extracted shared samples
Add new spec file for task completion status response
Fix style errors
Add changelog entry
Changed samples to Hashes
Remove test for successful request
Remove not nil expectation
Add task_completion_status to api documentation for issues
Add task_completion_status to api documentation for merge_requests
Refactor spec so it just requests one specific item
Add task_completion_status to Taskable
Simplified task completion status in entities
Refactor spec so it separates status code check and content check
Fix spec description text and field name
-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 |