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,7 +1183,11 @@ Parameters: ...@@ -1155,7 +1183,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
}
} }
``` ```
...@@ -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