Commit 6c8dcc5c authored by Winnie Hellmann's avatar Winnie Hellmann Committed by Dmitriy Zaporozhets

Expose epic_iid in issues API

parent 9da9ed00
......@@ -573,6 +573,18 @@ the `weight` parameter:
}
```
Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) will additionally see
the `epic_iid` property:
```json
{
"project_id" : 4,
"description" : "Omnis vero earum sunt corporis dolor et placeat.",
"epic_iid" : 42,
...
}
```
**Note**: `assignee` column is deprecated, now we show it as a single-sized array `assignees` to conform to the GitLab EE API.
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
......
---
title: Expose epic_iid in issues API
merge_request: 15998
author:
type: added
......@@ -122,6 +122,17 @@ module EE
end
end
module Issue
extend ActiveSupport::Concern
prepended do
expose :epic_iid,
if: -> (issue, options) { ::Ability.allowed?(options[:current_user], :read_epic, issue.project&.group) } do |issue|
issue.epic&.iid
end
end
end
module MergeRequestBasic
extend ActiveSupport::Concern
......@@ -194,9 +205,11 @@ module EE
extend ActiveSupport::Concern
def todo_target_class(target_type)
::EE::API::Entities.const_get(target_type, false)
rescue NameError
super
rescue NameError
# false as second argument prevents looking up in module hierarchy
# see also https://gitlab.com/gitlab-org/gitlab-ce/issues/59719
::EE::API::Entities.const_get(target_type, false)
end
end
......
......@@ -4,7 +4,8 @@
{ "$ref": "../../../../../../../spec/fixtures/api/schemas/public_api/v4/issues.json" },
{
"properties": {
"weight": { "type": ["integer", "null"] }
"weight": { "type": ["integer", "null"] },
"epic_iid": { "type": ["integer", "null"] }
}
}
]
......
......@@ -5,6 +5,9 @@ describe API::Issues, :mailer do
set(:project) do
create(:project, :public, creator_id: user.id, namespace: user.namespace)
end
set(:group) { create(:group) }
set(:epic) { create(:epic, group: group) }
set(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) }
let(:user2) { create(:user) }
set(:author) { create(:author) }
......@@ -28,6 +31,34 @@ describe API::Issues, :mailer do
project.add_reporter(user)
end
shared_examples 'exposes epic_iid' do
context 'with epics feature' do
before do
stub_licensed_features(epics: true)
end
it 'contains epic_iid in response' do
subject
expect(response).to have_gitlab_http_status(200)
expect(epic_issue_response_for(epic_issue)['epic_iid']).to eq(epic.iid)
end
end
context 'without epics feature' do
before do
stub_licensed_features(epics: false)
end
it 'does not contain epic_iid in response' do
subject
expect(response).to have_gitlab_http_status(200)
expect(epic_issue_response_for(epic_issue)).not_to have_key('epic_iid')
end
end
end
describe "GET /issues" do
context "when authenticated" do
it 'matches V4 response schema' do
......@@ -77,8 +108,7 @@ describe API::Issues, :mailer do
end
describe 'GET /groups/:id/issues' do
let!(:group) { create(:group) }
let!(:group_project) { create(:project, :public, creator_id: user.id, namespace: group) }
subject { get api("/groups/#{group.id}/issues", user) }
context 'filtering by assignee_username' do
let(:another_assignee) { create(:assignee) }
......@@ -86,29 +116,93 @@ describe API::Issues, :mailer do
let!(:issue2) { create(:issue, author: user2, project: group_project, weight: 5, created_at: 2.days.ago) }
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: group_project, weight: 3, created_at: 1.day.ago) }
it 'returns issues with multiple assignees' do
subject do
get api("/groups/#{group.id}/issues", user),
params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
end
it 'returns issues with multiple assignees' do
subject
expect_paginated_array_response(issue3.id)
end
end
include_examples 'exposes epic_iid' do
let!(:epic_issue) { create(:issue, project: group_project, epic: epic) }
end
end
describe "GET /projects/:id/issues" do
subject { get api("/projects/#{project.id}/issues", user) }
context 'filtering by assignee_username' do
let(:another_assignee) { create(:assignee) }
let!(:issue1) { create(:issue, author: user2, project: project, weight: 1, created_at: 3.days.ago) }
let!(:issue2) { create(:issue, author: user2, project: project, weight: 5, created_at: 2.days.ago) }
let!(:issue3) { create(:issue, author: user2, assignees: [assignee, another_assignee], project: project, weight: 3, created_at: 1.day.ago) }
it 'returns issues with multiple assignees' do
subject do
get api("/projects/#{project.id}/issues", user),
params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
params: { assignee_username: [assignee.username, another_assignee.username], scope: 'all' }
end
it 'returns issues with multiple assignees' do
subject
expect_paginated_array_response(issue3.id)
end
end
context 'on personal project' do
let!(:epic_issue) { create(:issue, project: project, epic: epic) }
before do
stub_licensed_features(epics: true)
end
it 'does not contain epic_iid in response' do
subject
expect(response).to have_gitlab_http_status(200)
expect(epic_issue_response_for(epic_issue)).not_to have_key('epic_iid')
end
end
context 'on group project' do
let!(:epic_issue) { create(:issue, project: group_project, epic: epic) }
subject { get api("/projects/#{group_project.id}/issues", user) }
include_examples 'exposes epic_iid'
end
end
describe 'GET /project/:id/issues/:issue_id' do
context 'on personal project' do
let!(:epic_issue) { create(:issue, project: project, epic: epic) }
subject { get api("/projects/#{project.id}/issues/#{epic_issue.iid}", user) }
before do
stub_licensed_features(epics: true)
end
it 'does not contain epic_iid in response' do
subject
expect(response).to have_gitlab_http_status(200)
expect(epic_issue_response_for(epic_issue)).not_to have_key('epic_iid')
end
end
context 'on group project' do
let!(:epic_issue) { create(:issue, project: group_project, epic: epic) }
subject { get api("/projects/#{group_project.id}/issues/#{epic_issue.iid}", user) }
include_examples 'exposes epic_iid'
end
end
describe "POST /projects/:id/issues" do
......@@ -174,4 +268,10 @@ describe API::Issues, :mailer do
end
end
end
private
def epic_issue_response_for(epic_issue)
Array.wrap(json_response).find { |issue| issue['id'] == epic_issue.id }
end
end
......@@ -978,7 +978,9 @@ module API
expose :created_at
def todo_target_class(target_type)
::API::Entities.const_get(target_type)
# false as second argument prevents looking up in module hierarchy
# see also https://gitlab.com/gitlab-org/gitlab-ce/issues/59719
::API::Entities.const_get(target_type, false)
end
end
......@@ -1731,6 +1733,7 @@ API::Entities.prepend_if_ee('EE::API::Entities::Entities')
::API::Entities::Group.prepend_if_ee('EE::API::Entities::Group', with_descendants: true)
::API::Entities::GroupDetail.prepend_if_ee('EE::API::Entities::GroupDetail')
::API::Entities::IssueBasic.prepend_if_ee('EE::API::Entities::IssueBasic', with_descendants: true)
::API::Entities::Issue.prepend_if_ee('EE::API::Entities::Issue')
::API::Entities::List.prepend_if_ee('EE::API::Entities::List')
::API::Entities::MergeRequestBasic.prepend_if_ee('EE::API::Entities::MergeRequestBasic', with_descendants: true)
::API::Entities::Namespace.prepend_if_ee('EE::API::Entities::Namespace')
......
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