Commit 3ef0c549 authored by Sean McGivern's avatar Sean McGivern

Merge branch '51636-task-list-api-pderichs' into 'master'

Add task count and completed count to responses of Issue and MR

See merge request gitlab-org/gitlab-ce!28859
parents 2fc7c09c b70d23c2
...@@ -75,4 +75,11 @@ module Taskable ...@@ -75,4 +75,11 @@ module Taskable
def task_status_short def task_status_short
task_status(short: true) task_status(short: true)
end end
def task_completion_status
@task_completion_status ||= {
count: tasks.summary.item_count,
completed_count: tasks.summary.complete_count
}
end
end end
---
title: Add task count and completed count to responses of Issue and MR
merge_request: 28859
author:
type: added
...@@ -135,7 +135,11 @@ Example response: ...@@ -135,7 +135,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/1/issues/76/award_emoji", "award_emoji":"http://example.com/api/v4/projects/1/issues/76/award_emoji",
"project":"http://example.com/api/v4/projects/1" "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: ...@@ -265,7 +269,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4" "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: ...@@ -403,7 +411,11 @@ Example response:
"award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji", "award_emoji":"http://example.com/api/v4/projects/4/issues/41/award_emoji",
"project":"http://example.com/api/v4/projects/4" "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: ...@@ -500,6 +512,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://example.com/api/v4/projects/1"
},
"task_completion_status":{
"count":0,
"completed_count":0
} }
} }
``` ```
...@@ -583,6 +599,10 @@ Example response: ...@@ -583,6 +599,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://example.com/api/v4/projects/1"
},
"task_completion_status":{
"count":0,
"completed_count":0
} }
} }
``` ```
...@@ -674,6 +694,10 @@ Example response: ...@@ -674,6 +694,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://example.com/api/v4/projects/1"
},
"task_completion_status":{
"count":0,
"completed_count":0
} }
} }
``` ```
...@@ -780,6 +804,10 @@ Example response: ...@@ -780,6 +804,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://example.com/api/v4/projects/1"
},
"task_completion_status":{
"count":0,
"completed_count":0
} }
} }
``` ```
...@@ -865,6 +893,10 @@ Example response: ...@@ -865,6 +893,10 @@ Example response:
"notes": "http://example.com/api/v4/projects/1/issues/2/notes", "notes": "http://example.com/api/v4/projects/1/issues/2/notes",
"award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji", "award_emoji": "http://example.com/api/v4/projects/1/issues/2/award_emoji",
"project": "http://example.com/api/v4/projects/1" "project": "http://example.com/api/v4/projects/1"
},
"task_completion_status":{
"count":0,
"completed_count":0
} }
} }
``` ```
...@@ -931,7 +963,11 @@ Example response: ...@@ -931,7 +963,11 @@ Example response:
"due_date": null, "due_date": null,
"web_url": "http://example.com/example/example/issues/12", "web_url": "http://example.com/example/example/issues/12",
"confidential": false, "confidential": false,
"discussion_locked": false "discussion_locked": false,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
``` ```
...@@ -1029,7 +1065,11 @@ Example response: ...@@ -1029,7 +1065,11 @@ Example response:
"due_date": null, "due_date": null,
"web_url": "http://example.com/example/example/issues/110", "web_url": "http://example.com/example/example/issues/110",
"confidential": false, "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", "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/issues/10",
"body": "Vel voluptas atque dicta mollitia adipisci qui at.", "body": "Vel voluptas atque dicta mollitia adipisci qui at.",
......
...@@ -138,7 +138,11 @@ Parameters: ...@@ -138,7 +138,11 @@ Parameters:
"human_time_estimate": null, "human_time_estimate": null,
"human_total_time_spent": null "human_total_time_spent": null
}, },
"squash": false "squash": false,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
] ]
``` ```
...@@ -280,7 +284,11 @@ Parameters: ...@@ -280,7 +284,11 @@ Parameters:
"human_time_estimate": null, "human_time_estimate": null,
"human_total_time_spent": null "human_total_time_spent": null
}, },
"squash": false "squash": false,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
] ]
``` ```
...@@ -410,7 +418,11 @@ Parameters: ...@@ -410,7 +418,11 @@ Parameters:
"human_time_estimate": null, "human_time_estimate": null,
"human_total_time_spent": null "human_total_time_spent": null
}, },
"squash": false "squash": false,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
] ]
``` ```
...@@ -545,7 +557,11 @@ Parameters: ...@@ -545,7 +557,11 @@ Parameters:
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
}, },
"diverged_commits_count": 2, "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: ...@@ -579,7 +595,7 @@ Parameters:
"state": "active", "state": "active",
"avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon", "avatar_url": "http://www.gravatar.com/avatar/10fc7f102be8de7657fb4d80898bbfe3?s=80&d=identicon",
"web_url": "http://localhost/user2" "web_url": "http://localhost/user2"
}, }
] ]
``` ```
...@@ -702,7 +718,11 @@ Parameters: ...@@ -702,7 +718,11 @@ Parameters:
"total_time_spent": 0, "total_time_spent": 0,
"human_time_estimate": null, "human_time_estimate": null,
"human_total_time_spent": null "human_total_time_spent": null
} },
"task_completion_status":{
"count":0,
"completed_count":0
},
"changes": [ "changes": [
{ {
"old_path": "VERSION", "old_path": "VERSION",
...@@ -865,7 +885,11 @@ POST /projects/:id/merge_requests ...@@ -865,7 +885,11 @@ POST /projects/:id/merge_requests
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "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. ...@@ -1002,7 +1026,11 @@ Must include at least one non-required attribute from above.
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
}, },
"diverged_commits_count": 2 "diverged_commits_count": 2,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
``` ```
...@@ -1155,13 +1183,17 @@ Parameters: ...@@ -1155,13 +1183,17 @@ Parameters:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "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 ## 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 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. a regular merge action was taken.
...@@ -1309,7 +1341,11 @@ Parameters: ...@@ -1309,7 +1341,11 @@ Parameters:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "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: ...@@ -1345,7 +1381,7 @@ If the rebase operation is ongoing, the response will include the following:
```json ```json
{ {
"rebase_in_progress": true "rebase_in_progress": true,
"merge_error": null "merge_error": null
} }
``` ```
...@@ -1356,7 +1392,7 @@ the following: ...@@ -1356,7 +1392,7 @@ the following:
```json ```json
{ {
"rebase_in_progress": false, "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: ...@@ -1365,7 +1401,7 @@ If the rebase operation fails, the response will include the following:
```json ```json
{ {
"rebase_in_progress": false, "rebase_in_progress": false,
"merge_error": "Rebase failed. Please rebase locally", "merge_error": "Rebase failed. Please rebase locally"
} }
``` ```
...@@ -1572,7 +1608,11 @@ Example response: ...@@ -1572,7 +1608,11 @@ Example response:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "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: ...@@ -1701,7 +1741,11 @@ Example response:
"head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f", "head_sha": "2be7ddb704c7b6b83732fdd5b9f09d5a397b5f8f",
"start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00" "start_sha": "c380d3acebd181f13629a25d2e2acca46ffe1e00"
}, },
"diverged_commits_count": 2 "diverged_commits_count": 2,
"task_completion_status":{
"count":0,
"completed_count":0
}
} }
``` ```
......
...@@ -576,6 +576,8 @@ module API ...@@ -576,6 +576,8 @@ module API
expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue| expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
issue issue
end end
expose :task_completion_status
end end
class Issue < IssueBasic class Issue < IssueBasic
...@@ -724,6 +726,8 @@ module API ...@@ -724,6 +726,8 @@ module API
end end
expose :squash expose :squash
expose :task_completion_status
end end
class MergeRequest < MergeRequestBasic class MergeRequest < MergeRequestBasic
......
# 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment