Commit c668cc55 authored by Mark Chao's avatar Mark Chao

Add emails for new epic

parent abb412d4
......@@ -181,6 +181,7 @@
- elastic_indexer
- export_csv
- ldap_group_sync
- new_epic
- project_import_schedule
- project_update_repository_storage
- rebase
......
......@@ -85,6 +85,7 @@
- [chat_notification, 2]
- [geo, 1]
- [repository_update_mirror, 1]
- [new_epic, 2]
- [project_import_schedule, 1]
- [project_update_repository_storage, 1]
- [admin_emails, 1]
......
......@@ -7,6 +7,7 @@ module EE
include ::Emails::AdminNotification
include ::Emails::CsvExport
include ::Emails::ServiceDesk
include ::Emails::Epics
attr_reader :group
end
......
# frozen_string_literal: true
module Emails
module Epics
def new_epic_email(recipient_id, epic_id, reason = nil)
@epic = Epic.find_by_id(epic_id)
return unless @epic
setup_epic_mail(recipient_id)
mail_new_thread(@epic, epic_thread_options(@epic.author_id, recipient_id, reason))
end
private
def setup_epic_mail(recipient_id)
@group = @epic.group
@target_url = group_epic_url(@epic.group, @epic)
@sent_notification = SentNotification.record(@epic, recipient_id, reply_key)
end
def epic_thread_options(sender_id, recipient_id, reason)
{
from: sender(sender_id),
to: recipient(recipient_id),
subject: subject("#{@epic.title} (#{@epic.to_reference})"),
'X-GitLab-NotificationReason' => reason
}
end
end
end
......@@ -38,6 +38,10 @@ module EE
end
end
def new_epic(epic)
new_resource_email(epic, :new_epic_email)
end
def project_mirror_user_changed(new_mirror_user, deleted_user_name, project)
mailer.project_mirror_user_changed_email(new_mirror_user.id, deleted_user_name, project.id).deliver_later
end
......
......@@ -7,6 +7,14 @@ module Epics
private
def before_create(epic)
# current_user (defined in BaseService) is not available within run_after_commit block
user = current_user
epic.run_after_commit do
NewEpicWorker.perform_async(epic.id, user.id)
end
end
def whitelisted_epic_params
params.slice(:title, :description, :start_date, :end_date)
end
......
- if Gitlab::CurrentSettings.email_author_in_body
%p.details
#{link_to @epic.author_name, user_url(@epic.author)} created an epic:
- if @epic.assignee
%p
Assignee: #{@epic.assignee.name}
- if @epic.description
%div
= markdown(@epic.description, pipeline: :email, author: @epic.author)
New Epic <%= @epic.to_reference(full: true) %> was created.
<%= url_for(group_epic_url(@epic.group, @epic)) %>
Author: <%= @epic.author_name %>
<% if @epic.assignee %>
Assignee: <%= @epic.assignee.name %>
<% end %>
<%= @epic.description %>
# frozen_string_literal: true
class NewEpicWorker
include ApplicationWorker
include NewIssuable
def perform(epic_id, user_id)
return unless objects_found?(epic_id, user_id)
NotificationService.new.new_epic(issuable)
issuable.create_cross_references!(user)
end
def issuable_class
Epic
end
end
......@@ -151,46 +151,77 @@ describe Notify do
end
context 'for a group' do
context 'for epic notes' do
describe 'for epics' do
set(:group) { create(:group) }
set(:epic) { create(:epic, group: group) }
set(:note) { create(:note, project: nil, noteable: epic) }
let(:note_author) { note.author }
let(:epic_note_path) { group_epic_path(group, epic, anchor: "note_#{note.id}") }
subject { described_class.note_epic_email(recipient.id, note.id) }
context 'that are new' do
subject { described_class.new_epic_email(recipient.id, epic.id) }
it_behaves_like 'a note email'
it_behaves_like 'an epic email starting a new thread with reply-by-email enabled' do
let(:model) { epic }
end
it_behaves_like 'it should show Gmail Actions View Epic link'
it_behaves_like 'an unsubscribeable thread'
it 'has the correct subject and body' do
prefix = "#{epic.group.name} | "
suffix = "#{epic.title} (#{epic.to_reference})"
it_behaves_like 'an unsubscribeable thread'
aggregate_failures do
is_expected.to have_subject [prefix, suffix].compact.join
is_expected.to have_body_text(group_epic_path(group, epic))
end
end
it 'has the characteristics of a threaded reply' do
host = Gitlab.config.gitlab.host
route_key = "#{epic.class.model_name.singular_route_key}_#{epic.id}"
context 'got deleted before notification' do
subject { described_class.new_epic_email(recipient.id, 0) }
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
it 'does not send email' do
expect(subject.message).to be_a_kind_of ActionMailer::Base::NullMail
end
end
end
context 'when reply-by-email is enabled with incoming address with %{key}' do
it 'has a Reply-To header' do
is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
context 'for epic notes' do
set(:note) { create(:note, project: nil, noteable: epic) }
let(:note_author) { note.author }
let(:epic_note_path) { group_epic_path(group, epic, anchor: "note_#{note.id}") }
subject { described_class.note_epic_email(recipient.id, note.id) }
it_behaves_like 'a note email'
it_behaves_like 'an unsubscribeable thread'
it 'has the characteristics of a threaded reply' do
host = Gitlab.config.gitlab.host
route_key = "#{epic.class.model_name.singular_route_key}_#{epic.id}"
aggregate_failures do
is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
is_expected.to have_header('References', /\A<reply\-.*@#{host}> <#{route_key}@#{host}>\Z/ )
is_expected.to have_subject(/^Re: /)
end
end
end
it { is_expected.to have_body_text('View Epic') }
context 'when reply-by-email is enabled with incoming address with %{key}' do
it 'has a Reply-To header' do
is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
end
end
it 'has the correct subject and body' do
prefix = "Re: #{epic.group.name} | "
suffix = "#{epic.title} (#{epic.to_reference})"
it_behaves_like 'it should show Gmail Actions View Epic link'
aggregate_failures do
is_expected.to have_subject [prefix, suffix].compact.join
is_expected.to have_body_text(epic_note_path)
it 'has the correct subject and body' do
prefix = "Re: #{epic.group.name} | "
suffix = "#{epic.title} (#{epic.to_reference})"
aggregate_failures do
is_expected.to have_subject [prefix, suffix].compact.join
is_expected.to have_body_text(epic_note_path)
end
end
end
end
......
......@@ -9,12 +9,15 @@ describe Epics::CreateService do
describe '#execute' do
it 'creates one issue correctly' do
allow(NewEpicWorker).to receive(:perform_async)
expect { subject }.to change { Epic.count }.from(0).to(1)
epic = Epic.last
expect(epic).to be_persisted
expect(epic.title).to eq('new epic')
expect(epic.description).to eq('epic description')
expect(NewEpicWorker).to have_received(:perform_async).with(epic.id, user.id)
end
end
end
# frozen_string_literal: true
shared_examples 'it should show Gmail Actions View Epic link' do
it_behaves_like 'it should have Gmail Actions links'
it { is_expected.to have_body_text('View Epic') }
end
shared_examples 'an epic email starting a new thread with reply-by-email enabled' do
include_examples 'a new thread email with reply-by-email enabled'
context 'when reply-by-email is enabled with incoming address with %{key}' do
it 'has a Reply-To header' do
is_expected.to have_header 'Reply-To', /<reply+(.*)@#{Gitlab.config.gitlab.host}>\Z/
end
end
context 'when reply-by-email is enabled with incoming address without %{key}' do
include_context 'reply-by-email is enabled with incoming address without %{key}'
include_examples 'a new thread email with reply-by-email enabled'
it 'has a Reply-To header' do
is_expected.to have_header 'Reply-To', /<reply@#{Gitlab.config.gitlab.host}>\Z/
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe NewEpicWorker do
describe '#perform' do
let(:worker) { described_class.new }
context 'when an epic not found' do
it 'does not call Services' do
expect(NotificationService).not_to receive(:new)
worker.perform(99, create(:user).id)
end
it 'logs an error' do
expect(Rails.logger).to receive(:error).with('NewEpicWorker: couldn\'t find Epic with ID=99, skipping job')
worker.perform(99, create(:user).id)
end
end
context 'when a user not found' do
it 'does not call Services' do
expect(NotificationService).not_to receive(:new)
worker.perform(create(:epic).id, 99)
end
it 'logs an error' do
expect(Rails.logger).to receive(:error).with('NewEpicWorker: couldn\'t find User with ID=99, skipping job')
worker.perform(create(:epic).id, 99)
end
end
context 'when everything is ok' do
let(:mentioned) { create(:user) }
let(:user) { create(:user) }
let(:epic) { create(:epic, description: "epic for #{mentioned.to_reference}") }
before do
stub_licensed_features(epics: true)
end
it 'creates a notification for the mentioned user' do
expect(Notify).to receive(:new_epic_email).with(mentioned.id, epic.id, NotificationReason::MENTIONED)
.and_return(double(deliver_later: true))
worker.perform(epic.id, user.id)
end
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