Commit b3a5416b authored by Nick Thomas's avatar Nick Thomas

Merge branch '195177-epic-id-in-csv' into 'master'

Add information about epic to Issue list csv export

See merge request gitlab-org/gitlab!22662
parents 8b28e659 b93bd460
...@@ -69,6 +69,8 @@ Data will be encoded with a comma as the column delimiter, with `"` used to quot ...@@ -69,6 +69,8 @@ Data will be encoded with a comma as the column delimiter, with `"` used to quot
| Labels | Title of any labels joined with a `,` | | Labels | Title of any labels joined with a `,` |
| Time Estimate | [Time estimate](../time_tracking.md#estimates) in seconds | | Time Estimate | [Time estimate](../time_tracking.md#estimates) in seconds |
| Time Spent | [Time spent](../time_tracking.md#time-spent) in seconds | | Time Spent | [Time spent](../time_tracking.md#time-spent) in seconds |
| Epic ID | Id of the parent epic **(ULTIMATE)**, introduced in 12.7 |
| Epic Title | Title of the parent epic **(ULTIMATE)**, introduced in 12.7 |
## Limitations ## Limitations
......
...@@ -8,23 +8,26 @@ module Issues ...@@ -8,23 +8,26 @@ module Issues
# Target attachment size before base64 encoding # Target attachment size before base64 encoding
TARGET_FILESIZE = 15000000 TARGET_FILESIZE = 15000000
def initialize(issues_relation) attr_reader :project
def initialize(issues_relation, project)
@issues = issues_relation @issues = issues_relation
@labels = @issues.labels_hash @labels = @issues.labels_hash
@project = project
end end
def csv_data def csv_data
csv_builder.render(TARGET_FILESIZE) csv_builder.render(TARGET_FILESIZE)
end end
def email(user, project) def email(user)
Notify.issues_csv_email(user, project, csv_data, csv_builder.status).deliver_now Notify.issues_csv_email(user, project, csv_data, csv_builder.status).deliver_now
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def csv_builder def csv_builder
@csv_builder ||= @csv_builder ||=
CsvBuilder.new(@issues.preload(:author, :assignees, :timelogs), header_to_value_hash) CsvBuilder.new(@issues.preload(:author, :assignees, :timelogs, :epic), header_to_value_hash)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -49,10 +52,25 @@ module Issues ...@@ -49,10 +52,25 @@ module Issues
'Closed At (UTC)' => -> (issue) { issue.closed_at&.to_s(:csv) }, 'Closed At (UTC)' => -> (issue) { issue.closed_at&.to_s(:csv) },
'Milestone' => -> (issue) { issue.milestone&.title }, 'Milestone' => -> (issue) { issue.milestone&.title },
'Weight' => -> (issue) { issue.weight }, 'Weight' => -> (issue) { issue.weight },
'Labels' => -> (issue) { @labels[issue.id].sort.join(',').presence }, 'Labels' => -> (issue) { issue_labels(issue) },
'Time Estimate' => ->(issue) { issue.time_estimate.to_s(:csv) }, 'Time Estimate' => ->(issue) { issue.time_estimate.to_s(:csv) },
'Time Spent' => -> (issue) { issue.timelogs.map(&:time_spent).inject(0, :+)} 'Time Spent' => -> (issue) { issue_time_spent(issue) }
} }.tap do |hash|
if project.group&.feature_available?(:epics)
hash['Epic ID'] = -> (issue) { issue.epic&.id }
hash['Epic Title'] = -> (issue) { issue.epic&.title }
end
end
end
def issue_labels(issue)
@labels[issue.id].sort.join(',').presence
end end
# rubocop: disable CodeReuse/ActiveRecord
def issue_time_spent(issue)
issue.timelogs.map(&:time_spent).sum
end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
...@@ -16,6 +16,6 @@ class ExportCsvWorker ...@@ -16,6 +16,6 @@ class ExportCsvWorker
issues = IssuesFinder.new(@current_user, params).execute issues = IssuesFinder.new(@current_user, params).execute
Issues::ExportCsvService.new(issues).email(@current_user, @project) Issues::ExportCsvService.new(issues, @project).email(@current_user)
end end
end end
---
title: Add information about epic to Issue list csv export
merge_request: 22662
author:
type: added
...@@ -3,11 +3,12 @@ ...@@ -3,11 +3,12 @@
require 'spec_helper' require 'spec_helper'
describe Issues::ExportCsvService do describe Issues::ExportCsvService do
let(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:project) { create(:project, :public) } let(:group) { create(:group) }
let(:project) { create(:project, :public, group: group) }
let!(:issue) { create(:issue, project: project, author: user) } let!(:issue) { create(:issue, project: project, author: user) }
let!(:bad_issue) { create(:issue, project: project, author: user) } let!(:bad_issue) { create(:issue, project: project, author: user) }
let(:subject) { described_class.new(Issue.all) } let(:subject) { described_class.new(Issue.all, project) }
it 'renders csv to string' do it 'renders csv to string' do
expect(subject.csv_data).to be_a String expect(subject.csv_data).to be_a String
...@@ -15,13 +16,13 @@ describe Issues::ExportCsvService do ...@@ -15,13 +16,13 @@ describe Issues::ExportCsvService do
describe '#email' do describe '#email' do
it 'emails csv' do it 'emails csv' do
expect { subject.email(user, project) }.to change(ActionMailer::Base.deliveries, :count) expect { subject.email(user) }.to change(ActionMailer::Base.deliveries, :count)
end end
it 'renders with a target filesize' do it 'renders with a target filesize' do
expect(subject.csv_builder).to receive(:render).with(described_class::TARGET_FILESIZE) expect(subject.csv_builder).to receive(:render).with(described_class::TARGET_FILESIZE)
subject.email(user, project) subject.email(user)
end end
end end
...@@ -142,12 +143,42 @@ describe Issues::ExportCsvService do ...@@ -142,12 +143,42 @@ describe Issues::ExportCsvService do
expect(csv[1]['Time Spent']).to eq '0' expect(csv[1]['Time Spent']).to eq '0'
end end
context 'handling epics' do
let(:epic) { create(:epic, group: group) }
before do
create(:epic_issue, issue: issue, epic: epic)
end
context 'with epics disabled' do
it 'does not include epics information' do
expect(csv[0]).not_to have_key('Epic ID')
end
end
context 'with epics enabled' do
before do
stub_licensed_features(epics: true)
end
specify 'epic ID' do
expect(csv[0]['Epic ID']).to eq(epic.id.to_s)
expect(csv[1]['Epic ID']).to be_nil
end
specify 'epic Title' do
expect(csv[0]['Epic Title']).to eq(epic.title)
expect(csv[1]['Epic Title']).to be_nil
end
end
end
context 'with issues filtered by labels and project' do context 'with issues filtered by labels and project' do
let(:subject) do let(:subject) do
described_class.new( described_class.new(
IssuesFinder.new(user, IssuesFinder.new(user,
project_id: project.id, project_id: project.id,
label_name: %w(Idea Feature)).execute) label_name: %w(Idea Feature)).execute, project)
end end
it 'returns only filtered objects' do it 'returns only filtered objects' do
......
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