Commit 365b5da3 authored by George Koltsov's avatar George Koltsov

Add email notification on group export complete

- When Group Export via UI is complete send
  exporter user a notification email with either
  success or failure about the export
parent 91ffb0be
......@@ -146,7 +146,7 @@ class GroupsController < Groups::ApplicationController
export_service = Groups::ImportExport::ExportService.new(group: @group, user: current_user)
if export_service.async_execute
redirect_to edit_group_path(@group), notice: _('Group export started.')
redirect_to edit_group_path(@group), notice: _('Group export started. A download link will be sent by email.')
else
redirect_to edit_group_path(@group), alert: _('Group export could not be started.')
end
......
# frozen_string_literal: true
module Emails
module Groups
def group_was_exported_email(current_user, group)
group_email(current_user, group, _('Group was exported'))
end
def group_was_not_exported_email(current_user, group, errors)
group_email(current_user, group, _('Group export error'), errors: errors)
end
def group_email(current_user, group, subj, errors: nil)
@group = group
@errors = errors
mail(to: current_user.notification_email_for(@group), subject: subject(subj))
end
end
end
......@@ -17,6 +17,7 @@ class Notify < ApplicationMailer
include Emails::AutoDevops
include Emails::RemoteMirrors
include Emails::Releases
include Emails::Groups
helper MilestonesHelper
helper MergeRequestsHelper
......
......@@ -92,6 +92,8 @@ module Groups
group_name: @group.name,
message: 'Group Import/Export: Export succeeded'
)
notification_service.group_was_exported(@group, @current_user)
end
def notify_error
......@@ -101,6 +103,12 @@ module Groups
error: @shared.errors.join(', '),
message: 'Group Import/Export: Export failed'
)
notification_service.group_was_not_exported(@group, @current_user, @shared.errors)
end
def notification_service
@notification_service ||= NotificationService.new
end
end
end
......
......@@ -537,6 +537,18 @@ class NotificationService
end
end
def group_was_exported(group, current_user)
return true unless notifiable?(current_user, :mention, group: group)
mailer.group_was_exported_email(current_user, group).deliver_later
end
def group_was_not_exported(group, current_user, errors)
return true unless notifiable?(current_user, :mention, group: group)
mailer.group_was_not_exported_email(current_user, group, errors).deliver_later
end
protected
def new_resource_email(target, method)
......
......@@ -17,7 +17,7 @@
%li= _('Projects')
%li= _('Runner tokens')
%li= _('SAML discovery tokens')
%p= _('Once the exported file is ready you can download it from this page.')
%p= _('Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page.')
- if group.export_file_exists?
= link_to _('Regenerate export'), export_group_path(group),
method: :post, class: 'btn btn-default', data: { qa_selector: 'regenerate_export_group_link' }
......
%p
= _('Group %{group_name} was exported successfully.') % { group_name: @group.name }
%p
= _('The group export can be downloaded from:')
= link_to download_export_group_url(@group), rel: 'nofollow', download: '' do
#{@group.full_name} export
%p
= _('The download link will expire in 24 hours.')
<%= _('Group %{group_name} was exported successfully.') % { group_name: @group.name } %>
<%= _('The group export can be downloaded from:') %>
<%= download_export_group_url(@group) %>
<%= _('The download link will expire in 24 hours.') %>
%p
= _("Group %{group_name} couldn't be exported.") % { group_name: @group.name }
%p
= _('The errors we encountered were:')
%ul
- @errors.each do |error|
%li
#{error}
<%= _("Group %{group_name} couldn't be exported.") % { group_name: @group.name } %>
<%= _('The errors we encountered were:') %>
<% @errors.each do |error| -%>
- <%= error %>
<% end -%>
---
title: Add email notification on group export complete
merge_request: 30522
author:
type: added
......@@ -63,8 +63,11 @@ For more details on the specific data persisted in a group export, see the
![Export group panel](img/export_panel.png)
1. Once the export is generated, you can click **Download export** to download the [exported contents](#exported-contents)
in a compressed tar archive, with contents in JSON format. You can also return to this page to regenerate the export data.
1. Once the export is generated, you should receive an e-mail with a link to the [exported contents](#exported-contents)
in a compressed tar archive, with contents in JSON format.
1. Alternatively, you can come back to the project settings and download the
file from there by clicking **Download export**, or generate a new file by clicking **Regenerate export**.
## Rate Limits
......
......@@ -10424,6 +10424,12 @@ msgstr ""
msgid "Group"
msgstr ""
msgid "Group %{group_name} couldn't be exported."
msgstr ""
msgid "Group %{group_name} was exported successfully."
msgstr ""
msgid "Group %{group_name} was scheduled for deletion."
msgstr ""
......@@ -10475,10 +10481,13 @@ msgstr ""
msgid "Group export could not be started."
msgstr ""
msgid "Group export error"
msgstr ""
msgid "Group export link has expired. Please generate a new export from your group settings."
msgstr ""
msgid "Group export started."
msgid "Group export started. A download link will be sent by email."
msgstr ""
msgid "Group has been already marked for deletion"
......@@ -10523,6 +10532,9 @@ msgstr ""
msgid "Group variables (inherited)"
msgstr ""
msgid "Group was exported"
msgstr ""
msgid "Group was successfully updated."
msgstr ""
......@@ -14390,9 +14402,6 @@ msgstr ""
msgid "Once removed, the fork relationship cannot be restored and you will no longer be able to send merge requests to the source."
msgstr ""
msgid "Once the exported file is ready you can download it from this page."
msgstr ""
msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page."
msgstr ""
......@@ -20889,9 +20898,15 @@ msgstr ""
msgid "The domain you entered is not allowed."
msgstr ""
msgid "The download link will expire in 24 hours."
msgstr ""
msgid "The entered user map is not a valid JSON user map."
msgstr ""
msgid "The errors we encountered were:"
msgstr ""
msgid "The file has been successfully created."
msgstr ""
......@@ -20930,6 +20945,9 @@ msgstr ""
msgid "The group can be fully restored"
msgstr ""
msgid "The group export can be downloaded from:"
msgstr ""
msgid "The group has already been shared with this group"
msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
require 'email_spec'
describe Emails::Groups do
include EmailSpec::Matchers
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
group.add_owner(user)
end
describe '#group_was_exported_email' do
subject { Notify.group_was_exported_email(user, group) }
it 'sends success email' do
expect(subject).to have_subject "#{group.name} | Group was exported"
expect(subject).to have_body_text 'The download link will expire in 24 hours.'
expect(subject).to have_body_text "groups/#{group.path}/-/download_export"
end
end
describe '#group_was_not_exported_email' do
let(:shared) { Gitlab::ImportExport::Shared.new(group) }
let(:error) { Gitlab::ImportExport::Error.new('Error!') }
before do
shared.error(error)
end
subject { Notify.group_was_not_exported_email(user, group, shared.errors) }
it 'sends failure email' do
expect(subject).to have_subject "#{group.name} | Group export error"
expect(subject).to have_body_text "Group #{group.name} couldn't be exported."
end
end
end
......@@ -65,6 +65,14 @@ describe Groups::ImportExport::ExportService do
service.execute
end
it 'notifies the user' do
expect_next_instance_of(NotificationService) do |instance|
expect(instance).to receive(:group_was_exported)
end
service.execute
end
context 'when saver succeeds' do
it 'saves the group in the file system' do
service.execute
......@@ -108,16 +116,26 @@ describe Groups::ImportExport::ExportService do
context 'when export fails' do
context 'when file saver fails' do
it 'removes the remaining exported data' do
before do
allow_next_instance_of(Gitlab::ImportExport::Saver) do |saver|
allow(saver).to receive(:save).and_return(false)
end
end
it 'removes the remaining exported data' do
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
expect(group.import_export_upload).to be_nil
expect(File.exist?(shared.archive_path)).to eq(false)
end
it 'notifies the user about failed group export' do
expect_next_instance_of(NotificationService) do |instance|
expect(instance).to receive(:group_was_not_exported)
end
expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
end
end
context 'when file compression fails' 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