Commit 12b98dc5 authored by Felipe Artur's avatar Felipe Artur

Move mailers/workers service desk code to core

Part of the plan to move service desk feature
into core tier.

More info: https://gitlab.com/gitlab-org/gitlab/-/issues/215364
parent 61a23c2e
...@@ -19,6 +19,7 @@ class Notify < ApplicationMailer ...@@ -19,6 +19,7 @@ class Notify < ApplicationMailer
include Emails::Releases include Emails::Releases
include Emails::Groups include Emails::Groups
include Emails::Reviews include Emails::Reviews
include Emails::ServiceDesk
helper TimeboxesHelper helper TimeboxesHelper
helper MergeRequestsHelper helper MergeRequestsHelper
......
...@@ -165,6 +165,18 @@ class NotifyPreview < ActionMailer::Preview ...@@ -165,6 +165,18 @@ class NotifyPreview < ActionMailer::Preview
Notify.unknown_sign_in_email(user, '127.0.0.1', Time.current).message Notify.unknown_sign_in_email(user, '127.0.0.1', Time.current).message
end end
def service_desk_new_note_email
cleanup do
note = create_note(noteable_type: 'Issue', noteable_id: issue.id, note: 'Issue note content')
Notify.service_desk_new_note_email(issue.id, note.id).message
end
end
def service_desk_thank_you_email
Notify.service_desk_thank_you_email(issue.id).message
end
private private
def project def project
......
%html{ lang: "en" } %html{ lang: "en" }
%head %head
%meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" } %meta{ content: "text/html; charset=utf-8", "http-equiv" => "Content-Type" }
-# haml-lint:disable NoPlainNodes
%title %title
GitLab GitLab
-# haml-lint:enable NoPlainNodes
= stylesheet_link_tag 'notify' = stylesheet_link_tag 'notify'
= yield :head = yield :head
%body %body
......
...@@ -3,4 +3,4 @@ New response for issue #<%= @issue.iid %>: ...@@ -3,4 +3,4 @@ New response for issue #<%= @issue.iid %>:
Author: <%= sanitize_name(@note.author_name) %> Author: <%= sanitize_name(@note.author_name) %>
<%= @note.note %> <%= @note.note %>
<%# EE-specific start %><%= render 'layouts/mailer/additional_text' %><%# EE-specific end %> <%# EE-specific start %><%= render_if_exists 'layouts/mailer/additional_text'%><%# EE-specific end %>
...@@ -3,4 +3,4 @@ Thank you for your support request! We are tracking your request as ticket #<%= ...@@ -3,4 +3,4 @@ Thank you for your support request! We are tracking your request as ticket #<%=
To unsubscribe from this issue, please paste the following link into your browser: To unsubscribe from this issue, please paste the following link into your browser:
<%= @unsubscribe_url %> <%= @unsubscribe_url %>
<%# EE-specific start %><%= render 'layouts/mailer/additional_text' %><%# EE-specific end %> <%# EE-specific start %><%= render_if_exists 'layouts/mailer/additional_text' %><%# EE-specific end %>
\ No newline at end of file
...@@ -1660,6 +1660,14 @@ ...@@ -1660,6 +1660,14 @@
:weight: 2 :weight: 2
:idempotent: :idempotent:
:tags: [] :tags: []
- :name: service_desk_email_receiver
:feature_category: :issue_tracking
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: system_hook_push - :name: system_hook_push
:feature_category: :source_code_management :feature_category: :source_code_management
:has_external_dependencies: :has_external_dependencies:
......
...@@ -10,7 +10,6 @@ module EE ...@@ -10,7 +10,6 @@ module EE
# See https://gitlab.com/gitlab-org/gitlab/issues/7846 # See https://gitlab.com/gitlab-org/gitlab/issues/7846
prepended do prepended do
include ::Emails::AdminNotification include ::Emails::AdminNotification
include ::Emails::ServiceDesk
include ::Emails::Epics include ::Emails::Epics
end end
......
...@@ -39,18 +39,6 @@ module EE ...@@ -39,18 +39,6 @@ module EE
def send_unsubscribed_notification def send_unsubscribed_notification
::Notify.send_unsubscribed_notification(user.id).message ::Notify.send_unsubscribed_notification(user.id).message
end end
def service_desk_new_note_email
cleanup do
note = create_note(noteable_type: 'Issue', noteable_id: issue.id, note: 'Issue note content')
::Notify.service_desk_new_note_email(issue.id, note.id).message
end
end
def service_desk_thank_you_email
::Notify.service_desk_thank_you_email(issue.id).message
end
end end
private private
......
...@@ -683,14 +683,6 @@ ...@@ -683,14 +683,6 @@
:weight: 1 :weight: 1
:idempotent: true :idempotent: true
:tags: [] :tags: []
- :name: service_desk_email_receiver
:feature_category: :issue_tracking
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent:
:tags: []
- :name: status_page_publish - :name: status_page_publish
:feature_category: :incident_management :feature_category: :incident_management
:has_external_dependencies: true :has_external_dependencies: true
......
# frozen_string_literal: true
module EE
module Gitlab
module Email
module Handler
extend ActiveSupport::Concern
class_methods do
extend ::Gitlab::Utils::Override
override :load_handlers
def load_handlers
[
*super,
::Gitlab::Email::Handler::EE::ServiceDeskHandler
]
end
end
end
end
end
end
...@@ -9,22 +9,6 @@ module EE ...@@ -9,22 +9,6 @@ module EE
private private
# Support bot is specifically forbidden
# from using slash commands.
def strip_quick_actions(content)
return content unless author.support_bot?
command_definitions = ::QuickActions::InterpretService.command_definitions
extractor = ::Gitlab::QuickActions::Extractor.new(command_definitions)
extractor.redact_commands(content)
end
override :process_message
def process_message(**kwargs)
strip_quick_actions(super(kwargs))
end
override :upload_params override :upload_params
def upload_params def upload_params
return super unless try(:noteable)&.is_a?(Epic) return super unless try(:noteable)&.is_a?(Epic)
......
# frozen_string_literal: true
# handles service desk issue creation emails with these formats:
# incoming+gitlab-org-gitlab-ce-20-issue-@incoming.gitlab.com
# incoming+gitlab-org/gitlab-ce@incoming.gitlab.com (legacy)
module Gitlab
module Email
module Handler
module EE
class ServiceDeskHandler < BaseHandler
include ReplyProcessing
include Gitlab::Utils::StrongMemoize
HANDLER_REGEX = /\A#{HANDLER_ACTION_BASE_REGEX}-issue-\z/.freeze
HANDLER_REGEX_LEGACY = /\A(?<project_path>[^\+]*)\z/.freeze
PROJECT_KEY_PATTERN = /\A(?<slug>.+)-(?<key>[a-z0-9_]+)\z/.freeze
def initialize(mail, mail_key, service_desk_key: nil)
super(mail, mail_key)
if service_desk_key.present?
@service_desk_key = service_desk_key
elsif !mail_key&.include?('/') && (matched = HANDLER_REGEX.match(mail_key.to_s))
@project_slug = matched[:project_slug]
@project_id = matched[:project_id]&.to_i
elsif matched = HANDLER_REGEX_LEGACY.match(mail_key.to_s)
@project_path = matched[:project_path]
end
end
def can_handle?
Gitlab::ServiceDesk.supported? && (project_id || can_handle_legacy_format? || service_desk_key)
end
def execute
raise ProjectNotFound if project.nil?
create_issue!
send_thank_you_email! if from_address
end
def metrics_params
super.merge(project: project&.full_path)
end
def metrics_event
:receive_email_service_desk
end
private
attr_reader :project_id, :project_path, :service_desk_key
def project
strong_memoize(:project) do
@project = service_desk_key ? project_from_key : super
@project = nil unless @project&.service_desk_enabled?
@project
end
end
def project_from_key
return unless match = service_desk_key.match(PROJECT_KEY_PATTERN)
project = Project.find_by_service_desk_project_key(match[:key])
return unless valid_project_key?(project, match[:slug])
project
end
def valid_project_key?(project, slug)
project.present? && slug == project.full_path_slug && Feature.enabled?(:service_desk_custom_address, project)
end
def create_issue!
@issue = Issues::CreateService.new(
project,
User.support_bot,
title: issue_title,
description: message_including_template,
confidential: true,
service_desk_reply_to: from_address
).execute
raise InvalidIssueError unless @issue.persisted?
if service_desk_setting&.issue_template_missing?
create_template_not_found_note(@issue)
end
end
def send_thank_you_email!
Notify.service_desk_thank_you_email(@issue.id).deliver_later!
end
def message_including_template
description = message_including_reply
template_content = service_desk_setting&.issue_template_content
if template_content.present?
description += " \n" + template_content
end
description
end
def service_desk_setting
strong_memoize(:service_desk_setting) do
project.service_desk_setting
end
end
def create_template_not_found_note(issue)
issue_template_key = service_desk_setting&.issue_template_key
warning_note = <<-MD.strip_heredoc
WARNING: The template file #{issue_template_key}.md used for service desk issues is empty or could not be found.
Please check service desk settings and update the file to be used.
MD
note_params = {
noteable: issue,
note: warning_note
}
::Notes::CreateService.new(
project,
User.support_bot,
note_params
).execute
end
def from_address
(mail.reply_to || []).first || mail.from.first || mail.sender
end
def issue_title
from = "(from #{from_address})" if from_address
"Service Desk #{from}: #{mail.subject}"
end
def can_handle_legacy_format?
project_path && project_path.include?('/') && !mail_key.include?('+')
end
def author
User.support_bot
end
end
end
end
end
end
...@@ -128,69 +128,4 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do ...@@ -128,69 +128,4 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
end end
end end
end end
context 'when the service desk' do
let(:project) { create(:project, :public, service_desk_enabled: true) }
let(:support_bot) { User.support_bot }
let(:noteable) { create(:issue, project: project, author: support_bot, title: 'service desk issue') }
let(:note) { create(:note, project: project, noteable: noteable) }
let(:email_raw) { fixture_file('emails/valid_reply_with_quick_actions.eml', dir: 'ee') }
let!(:sent_notification) do
SentNotification.record_note(note, support_bot.id, mail_key)
end
context 'is enabled' do
before do
allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(true)
project.project_feature.update!(issues_access_level: issues_access_level)
end
context 'when issues are enabled for everyone' do
let(:issues_access_level) { ProjectFeature::ENABLED }
it 'creates a comment' do
expect { receiver.execute }.to change { noteable.notes.count }.by(1)
end
context 'when quick actions are present' do
it 'encloses quick actions with code span markdown' do
receiver.execute
noteable.reload
note = Note.last
expect(note.note).to include("Jake out\n\n`/close`\n`/title test`")
expect(noteable.title).to eq('service desk issue')
expect(noteable).to be_opened
end
end
end
context 'when issues are protected members only' do
let(:issues_access_level) { ProjectFeature::PRIVATE }
it 'creates a comment' do
expect { receiver.execute }.to change { noteable.notes.count }.by(1)
end
end
context 'when issues are disabled' do
let(:issues_access_level) { ProjectFeature::DISABLED }
it 'does not create a comment' do
expect { receiver.execute }.to raise_error(::Gitlab::Email::UserNotAuthorizedError)
end
end
end
context 'is disabled' do
before do
allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(false)
end
it 'does not create a comment' do
expect { receiver.execute }.to raise_error(::Gitlab::Email::ProjectNotFound)
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Email::Handler do
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
describe '.for' do
def handler_for(fixture, mail_key)
described_class.for(fixture_file(fixture), mail_key)
end
def ee_handler_for(fixture, mail_key)
described_class.for(fixture_file(fixture, dir: 'ee'), mail_key)
end
context 'a Service Desk email' do
it 'uses the Service Desk handler' do
expect(ee_handler_for('emails/service_desk.eml', 'some/project')).to be_instance_of(Gitlab::Email::Handler::EE::ServiceDeskHandler)
end
end
context 'a new issue email' do
let!(:user) { create(:user, email: 'jake@adventuretime.ooo', incoming_email_token: 'auth_token') }
it 'uses the create issue handler when Service Desk is enabled' do
allow(License).to receive(:feature_available?).and_call_original
allow(License).to receive(:feature_available?).with(:service_desk).and_return(true)
expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end
it 'uses the create issue handler when Service Desk is disabled' do
allow(License).to receive(:feature_available?).and_call_original
allow(License).to receive(:feature_available?).with(:service_desk).and_return(false)
expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end
end
end
end
...@@ -72,78 +72,6 @@ RSpec.describe Notify do ...@@ -72,78 +72,6 @@ RSpec.describe Notify do
end end
context 'for a project' do context 'for a project' do
context 'for service desk issues' do
before do
issue.update!(service_desk_reply_to: 'service.desk@example.com')
end
def expect_sender(username)
sender = subject.header[:from].addrs[0]
expect(sender.display_name).to eq(username)
expect(sender.address).to eq(gitlab_sender)
end
describe 'thank you email' do
subject { described_class.service_desk_thank_you_email(issue.id) }
it_behaves_like 'an unsubscribeable thread'
it 'has the correct recipient' do
is_expected.to deliver_to('service.desk@example.com')
end
it 'has the correct subject and body' do
aggregate_failures do
is_expected.to have_referable_subject(issue, include_project: false, reply: true)
is_expected.to have_body_text("Thank you for your support request! We are tracking your request as ticket #{issue.to_reference}, and will respond as soon as we can.")
end
end
it 'uses service bot name by default' do
expect_sender(User.support_bot.name)
end
context 'when custom outgoing name is set' do
let_it_be(:settings) { create(:service_desk_setting, project: project, outgoing_name: 'some custom name') }
it 'uses custom name in "from" header' do
expect_sender('some custom name')
end
end
context 'when custom outgoing name is empty' do
let_it_be(:settings) { create(:service_desk_setting, project: project, outgoing_name: '') }
it 'uses service bot name' do
expect_sender(User.support_bot.name)
end
end
end
describe 'new note email' do
let_it_be(:first_note) { create(:discussion_note_on_issue, note: 'Hello world') }
subject { described_class.service_desk_new_note_email(issue.id, first_note.id) }
it_behaves_like 'an unsubscribeable thread'
it 'has the correct recipient' do
is_expected.to deliver_to('service.desk@example.com')
end
it 'uses author\'s name in "from" header' do
expect_sender(first_note.author.name)
end
it 'has the correct subject and body' do
aggregate_failures do
is_expected.to have_referable_subject(issue, include_project: false, reply: true)
is_expected.to have_body_text(first_note.note)
end
end
end
end
context 'for merge requests' do context 'for merge requests' do
describe "that are new with approver" do describe "that are new with approver" do
before do before do
......
...@@ -185,15 +185,6 @@ RSpec.describe API::Projects do ...@@ -185,15 +185,6 @@ RSpec.describe API::Projects do
end end
end end
describe 'service desk attributes' do
it 'are exposed when the feature is available' do
get api("/projects/#{project.id}", user)
expect(json_response).to have_key 'service_desk_enabled'
expect(json_response).to have_key 'service_desk_address'
end
end
context 'project soft-deletion' do context 'project soft-deletion' do
subject { get api("/projects/#{project.id}", user) } subject { get api("/projects/#{project.id}", user) }
......
...@@ -12,7 +12,8 @@ module Gitlab ...@@ -12,7 +12,8 @@ module Gitlab
CreateNoteHandler, CreateNoteHandler,
CreateIssueHandler, CreateIssueHandler,
UnsubscribeHandler, UnsubscribeHandler,
CreateMergeRequestHandler CreateMergeRequestHandler,
ServiceDeskHandler
] ]
end end
...@@ -25,5 +26,3 @@ module Gitlab ...@@ -25,5 +26,3 @@ module Gitlab
end end
end end
end end
Gitlab::Email::Handler.prepend_if_ee('::EE::Gitlab::Email::Handler')
...@@ -37,7 +37,11 @@ module Gitlab ...@@ -37,7 +37,11 @@ module Gitlab
def process_message(**kwargs) def process_message(**kwargs)
message = ReplyParser.new(mail, **kwargs).execute.strip message = ReplyParser.new(mail, **kwargs).execute.strip
add_attachments(message) message_with_attachments = add_attachments(message)
# Support bot is specifically forbidden
# from using slash commands.
strip_quick_actions(message_with_attachments)
end end
def add_attachments(reply) def add_attachments(reply)
...@@ -82,6 +86,15 @@ module Gitlab ...@@ -82,6 +86,15 @@ module Gitlab
def valid_project_slug?(found_project) def valid_project_slug?(found_project)
project_slug == found_project.full_path_slug project_slug == found_project.full_path_slug
end end
def strip_quick_actions(content)
return content unless author.support_bot?
command_definitions = ::QuickActions::InterpretService.command_definitions
extractor = ::Gitlab::QuickActions::Extractor.new(command_definitions)
extractor.redact_commands(content)
end
end end
end end
end end
......
# frozen_string_literal: true
# handles service desk issue creation emails with these formats:
# incoming+gitlab-org-gitlab-ce-20-issue-@incoming.gitlab.com
# incoming+gitlab-org/gitlab-ce@incoming.gitlab.com (legacy)
module Gitlab
module Email
module Handler
class ServiceDeskHandler < BaseHandler
include ReplyProcessing
include Gitlab::Utils::StrongMemoize
HANDLER_REGEX = /\A#{HANDLER_ACTION_BASE_REGEX}-issue-\z/.freeze
HANDLER_REGEX_LEGACY = /\A(?<project_path>[^\+]*)\z/.freeze
PROJECT_KEY_PATTERN = /\A(?<slug>.+)-(?<key>[a-z0-9_]+)\z/.freeze
def initialize(mail, mail_key, service_desk_key: nil)
super(mail, mail_key)
if service_desk_key.present?
@service_desk_key = service_desk_key
elsif !mail_key&.include?('/') && (matched = HANDLER_REGEX.match(mail_key.to_s))
@project_slug = matched[:project_slug]
@project_id = matched[:project_id]&.to_i
elsif matched = HANDLER_REGEX_LEGACY.match(mail_key.to_s)
@project_path = matched[:project_path]
end
end
def can_handle?
Gitlab::ServiceDesk.supported? && (project_id || can_handle_legacy_format? || service_desk_key)
end
def execute
raise ProjectNotFound if project.nil?
create_issue!
send_thank_you_email! if from_address
end
def metrics_params
super.merge(project: project&.full_path)
end
def metrics_event
:receive_email_service_desk
end
private
attr_reader :project_id, :project_path, :service_desk_key
def project
strong_memoize(:project) do
@project = service_desk_key ? project_from_key : super
@project = nil unless @project&.service_desk_enabled?
@project
end
end
def project_from_key
return unless match = service_desk_key.match(PROJECT_KEY_PATTERN)
project = Project.find_by_service_desk_project_key(match[:key])
return unless valid_project_key?(project, match[:slug])
project
end
def valid_project_key?(project, slug)
project.present? && slug == project.full_path_slug && Feature.enabled?(:service_desk_custom_address, project)
end
def create_issue!
@issue = Issues::CreateService.new(
project,
User.support_bot,
title: issue_title,
description: message_including_template,
confidential: true,
service_desk_reply_to: from_address
).execute
raise InvalidIssueError unless @issue.persisted?
if service_desk_setting&.issue_template_missing?
create_template_not_found_note(@issue)
end
end
def send_thank_you_email!
Notify.service_desk_thank_you_email(@issue.id).deliver_later!
end
def message_including_template
description = message_including_reply
template_content = service_desk_setting&.issue_template_content
if template_content.present?
description += " \n" + template_content
end
description
end
def service_desk_setting
strong_memoize(:service_desk_setting) do
project.service_desk_setting
end
end
def create_template_not_found_note(issue)
issue_template_key = service_desk_setting&.issue_template_key
warning_note = <<-MD.strip_heredoc
WARNING: The template file #{issue_template_key}.md used for service desk issues is empty or could not be found.
Please check service desk settings and update the file to be used.
MD
note_params = {
noteable: issue,
note: warning_note
}
::Notes::CreateService.new(
project,
User.support_bot,
note_params
).execute
end
def from_address
(mail.reply_to || []).first || mail.from.first || mail.sender
end
def issue_title
from = "(from #{from_address})" if from_address
"Service Desk #{from}: #{mail.subject}"
end
def can_handle_legacy_format?
project_path && project_path.include?('/') && !mail_key.include?('+')
end
def author
User.support_bot
end
end
end
end
end
...@@ -9,7 +9,7 @@ module Gitlab ...@@ -9,7 +9,7 @@ module Gitlab
key = service_desk_key(mail) key = service_desk_key(mail)
return unless key return unless key
::Gitlab::Email::Handler::EE::ServiceDeskHandler.new(mail, nil, service_desk_key: key) Gitlab::Email::Handler::ServiceDeskHandler.new(mail, nil, service_desk_key: key)
end end
def service_desk_key(mail) def service_desk_key(mail)
......
...@@ -242,4 +242,70 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do ...@@ -242,4 +242,70 @@ RSpec.describe Gitlab::Email::Handler::CreateNoteHandler do
it_behaves_like 'a reply to existing comment' it_behaves_like 'a reply to existing comment'
end end
context 'when the service desk' do
let(:project) { create(:project, :public, service_desk_enabled: true) }
let(:support_bot) { User.support_bot }
let(:noteable) { create(:issue, project: project, author: support_bot, title: 'service desk issue') }
let(:note) { create(:note, project: project, noteable: noteable) }
let(:email_raw) { fixture_file('emails/valid_reply_with_quick_actions.eml') }
let!(:sent_notification) do
SentNotification.record_note(note, support_bot.id, mail_key)
end
context 'is enabled' do
before do
allow(Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(true)
project.project_feature.update!(issues_access_level: issues_access_level)
end
context 'when issues are enabled for everyone' do
let(:issues_access_level) { ProjectFeature::ENABLED }
it 'creates a comment' do
expect { receiver.execute }.to change { noteable.notes.count }.by(1)
end
context 'when quick actions are present' do
it 'encloses quick actions with code span markdown' do
receiver.execute
noteable.reload
note = Note.last
expect(note.note).to include("Jake out\n\n`/close`\n`/title test`")
expect(noteable.title).to eq('service desk issue')
expect(noteable).to be_opened
end
end
end
context 'when issues are protected members only' do
let(:issues_access_level) { ProjectFeature::PRIVATE }
it 'creates a comment' do
expect { receiver.execute }.to change { noteable.notes.count }.by(1)
end
end
context 'when issues are disabled' do
let(:issues_access_level) { ProjectFeature::DISABLED }
it 'does not create a comment' do
expect { receiver.execute }.to raise_error(Gitlab::Email::UserNotAuthorizedError)
end
end
end
context 'is disabled' do
before do
allow(Gitlab::ServiceDesk).to receive(:enabled?).and_return(false)
allow(Gitlab::ServiceDesk).to receive(:enabled?).with(project: project).and_return(false)
end
it 'does not create a comment' do
expect { receiver.execute }.to raise_error(Gitlab::Email::ProjectNotFound)
end
end
end
end end
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do RSpec.describe Gitlab::Email::Handler::ServiceDeskHandler do
include_context :email_shared_context include_context :email_shared_context
before do before do
...@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -10,7 +10,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
stub_config_setting(host: 'localhost') stub_config_setting(host: 'localhost')
end end
let(:email_raw) { email_fixture('emails/service_desk.eml', dir: 'ee') } let(:email_raw) { email_fixture('emails/service_desk.eml') }
let_it_be(:namespace) { create(:namespace, name: "email") } let_it_be(:namespace) { create(:namespace, name: "email") }
let(:expected_description) do let(:expected_description) do
"Service desk stuff!\n\n```\na = b\n```\n\n`/label ~label1`\n`/assign @user1`\n`/close`\n![image](uploads/image.png)" "Service desk stuff!\n\n```\na = b\n```\n\n`/label ~label1`\n`/assign @user1`\n`/close`\n![image](uploads/image.png)"
...@@ -49,7 +49,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -49,7 +49,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
it_behaves_like 'a new issue request' it_behaves_like 'a new issue request'
context 'with legacy incoming email address' do context 'with legacy incoming email address' do
let(:email_raw) { fixture_file('emails/service_desk_legacy.eml', dir: 'ee') } let(:email_raw) { fixture_file('emails/service_desk_legacy.eml') }
it_behaves_like 'a new issue request' it_behaves_like 'a new issue request'
end end
...@@ -120,7 +120,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -120,7 +120,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
context 'and template cannot be found' do context 'and template cannot be found' do
before do before do
service = ServiceDeskSetting.new(project_id: project.id, issue_template_key: 'unknown') service = ServiceDeskSetting.new(project_id: project.id, issue_template_key: 'unknown')
service.save(validate: false) service.save!(validate: false)
end end
it 'does not append template text to issue description' do it 'does not append template text to issue description' do
...@@ -233,7 +233,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -233,7 +233,7 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
end end
context 'when there is a sender address and a from address' do context 'when there is a sender address and a from address' do
let(:email_raw) { email_fixture('emails/service_desk_sender_and_from.eml', dir: 'ee') } let(:email_raw) { email_fixture('emails/service_desk_sender_and_from.eml') }
it 'prefers the from address' do it 'prefers the from address' do
setup_attachment setup_attachment
...@@ -261,13 +261,13 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -261,13 +261,13 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
end end
context 'when the email is forwarded through an alias' do context 'when the email is forwarded through an alias' do
let(:email_raw) { email_fixture('emails/service_desk_forwarded.eml', dir: 'ee') } let(:email_raw) { email_fixture('emails/service_desk_forwarded.eml') }
it_behaves_like 'a new issue request' it_behaves_like 'a new issue request'
end end
context 'when the email is forwarded' do context 'when the email is forwarded' do
let(:email_raw) { email_fixture('emails/service_desk_forwarded_new_issue.eml', dir: 'ee') } let(:email_raw) { email_fixture('emails/service_desk_forwarded_new_issue.eml') }
it_behaves_like 'a new issue request' do it_behaves_like 'a new issue request' do
let(:expected_description) do let(:expected_description) do
...@@ -300,12 +300,12 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do ...@@ -300,12 +300,12 @@ RSpec.describe Gitlab::Email::Handler::EE::ServiceDeskHandler do
end end
end end
def email_fixture(path, dir:) def email_fixture(path)
fixture_file(path, dir: dir).gsub('project_id', project.project_id.to_s) fixture_file(path).gsub('project_id', project.project_id.to_s)
end end
def service_desk_fixture(path, slug: nil, key: 'mykey') def service_desk_fixture(path, slug: nil, key: 'mykey')
slug ||= project.full_path_slug.to_s slug ||= project.full_path_slug.to_s
fixture_file(path, dir: 'ee').gsub('project_slug', slug).gsub('project_key', key) fixture_file(path).gsub('project_slug', slug).gsub('project_key', key)
end end
end end
...@@ -33,12 +33,40 @@ RSpec.describe Gitlab::Email::Handler do ...@@ -33,12 +33,40 @@ RSpec.describe Gitlab::Email::Handler do
it 'returns nil if provided email is nil' do it 'returns nil if provided email is nil' do
expect(described_class.for(nil, '')).to be_nil expect(described_class.for(nil, '')).to be_nil
end end
context 'new issue email' do
def handler_for(fixture, mail_key)
described_class.for(fixture_file(fixture), mail_key)
end
before do
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.adventuretime.ooo")
stub_config_setting(host: 'localhost')
end
let!(:user) { create(:user, email: 'jake@adventuretime.ooo', incoming_email_token: 'auth_token') }
context 'a Service Desk email' do
it 'uses the Service Desk handler' do
expect(handler_for('emails/service_desk.eml', 'some/project')).to be_instance_of(Gitlab::Email::Handler::ServiceDeskHandler)
end
end
it 'return new issue handler' do
expect(handler_for('emails/valid_new_issue.eml', 'some/project+auth_token')).to be_instance_of(Gitlab::Email::Handler::CreateIssueHandler)
end
end
end end
describe 'regexps are set properly' do describe 'regexps are set properly' do
let(:addresses) do let(:addresses) do
%W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path-to-project-123-user_email_token-merge-request path-to-project-123-user_email_token-issue) + %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX} sent_notification_key path-to-project-123-user_email_token-merge-request) +
%W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX_LEGACY} sent_notification_key path/to/project+merge-request+user_email_token path/to/project+user_email_token) %W(sent_notification_key#{Gitlab::IncomingEmail::UNSUBSCRIBE_SUFFIX_LEGACY} sent_notification_key path-to-project-123-user_email_token-issue) +
%w(path/to/project+user_email_token path/to/project+merge-request+user_email_token some/project)
end
before do
allow(Gitlab::ServiceDesk).to receive(:supported?).and_return(true)
end end
it 'picks each handler at least once' do it 'picks each handler at least once' do
...@@ -46,12 +74,12 @@ RSpec.describe Gitlab::Email::Handler do ...@@ -46,12 +74,12 @@ RSpec.describe Gitlab::Email::Handler do
described_class.for(email, address).class described_class.for(email, address).class
end end
expect(matched_handlers.uniq).to match_array(ce_handlers) expect(matched_handlers.uniq).to match_array(Gitlab::Email::Handler.handlers)
end end
it 'can pick exactly one handler for each address' do it 'can pick exactly one handler for each address' do
addresses.each do |address| addresses.each do |address|
matched_handlers = ce_handlers.select do |handler| matched_handlers = Gitlab::Email::Handler.handlers.select do |handler|
handler.new(email, address).can_handle? handler.new(email, address).can_handle?
end end
...@@ -59,10 +87,4 @@ RSpec.describe Gitlab::Email::Handler do ...@@ -59,10 +87,4 @@ RSpec.describe Gitlab::Email::Handler do
end end
end end
end end
def ce_handlers
@ce_handlers ||= Gitlab::Email::Handler.handlers.reject do |handler|
handler.name.start_with?('Gitlab::Email::Handler::EE::')
end
end
end end
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Email::ServiceDeskReceiver do RSpec.describe Gitlab::Email::ServiceDeskReceiver do
let(:email) { fixture_file('emails/service_desk_custom_address.eml', dir: 'ee') } let(:email) { fixture_file('emails/service_desk_custom_address.eml') }
let(:receiver) { described_class.new(email) } let(:receiver) { described_class.new(email) }
context 'when the email contains a valid email address' do context 'when the email contains a valid email address' do
...@@ -18,7 +18,7 @@ RSpec.describe Gitlab::Email::ServiceDeskReceiver do ...@@ -18,7 +18,7 @@ RSpec.describe Gitlab::Email::ServiceDeskReceiver do
{ service_desk_key: 'project_slug-project_key' } { service_desk_key: 'project_slug-project_key' }
] ]
expect(::Gitlab::Email::Handler::EE::ServiceDeskHandler) expect(Gitlab::Email::Handler::ServiceDeskHandler)
.to receive(:new).with(*expected_params).and_return(handler) .to receive(:new).with(*expected_params).and_return(handler)
receiver.execute receiver.execute
......
...@@ -26,8 +26,6 @@ RSpec.describe Emails::ServiceDesk do ...@@ -26,8 +26,6 @@ RSpec.describe Emails::ServiceDesk do
helper GitlabRoutingHelper helper GitlabRoutingHelper
helper EmailsHelper helper EmailsHelper
append_view_path Rails.root.join('ee', 'app', 'views', 'notify')
# this method is implemented in Notify class, we don't need to test it # this method is implemented in Notify class, we don't need to test it
def reply_key def reply_key
'test-key' 'test-key'
......
...@@ -1253,6 +1253,78 @@ RSpec.describe Notify do ...@@ -1253,6 +1253,78 @@ RSpec.describe Notify do
it_behaves_like 'appearance header and footer not enabled' it_behaves_like 'appearance header and footer not enabled'
end end
end end
context 'for service desk issues' do
before do
issue.update!(service_desk_reply_to: 'service.desk@example.com')
end
def expect_sender(username)
sender = subject.header[:from].addrs[0]
expect(sender.display_name).to eq(username)
expect(sender.address).to eq(gitlab_sender)
end
describe 'thank you email' do
subject { described_class.service_desk_thank_you_email(issue.id) }
it_behaves_like 'an unsubscribeable thread'
it 'has the correct recipient' do
is_expected.to deliver_to('service.desk@example.com')
end
it 'has the correct subject and body' do
aggregate_failures do
is_expected.to have_referable_subject(issue, include_project: false, reply: true)
is_expected.to have_body_text("Thank you for your support request! We are tracking your request as ticket #{issue.to_reference}, and will respond as soon as we can.")
end
end
it 'uses service bot name by default' do
expect_sender(User.support_bot.name)
end
context 'when custom outgoing name is set' do
let_it_be(:settings) { create(:service_desk_setting, project: project, outgoing_name: 'some custom name') }
it 'uses custom name in "from" header' do
expect_sender('some custom name')
end
end
context 'when custom outgoing name is empty' do
let_it_be(:settings) { create(:service_desk_setting, project: project, outgoing_name: '') }
it 'uses service bot name' do
expect_sender(User.support_bot.name)
end
end
end
describe 'new note email' do
let_it_be(:first_note) { create(:discussion_note_on_issue, note: 'Hello world') }
subject { described_class.service_desk_new_note_email(issue.id, first_note.id) }
it_behaves_like 'an unsubscribeable thread'
it 'has the correct recipient' do
is_expected.to deliver_to('service.desk@example.com')
end
it 'uses author\'s name in "from" header' do
expect_sender(first_note.author.name)
end
it 'has the correct subject and body' do
aggregate_failures do
is_expected.to have_referable_subject(issue, include_project: false, reply: true)
is_expected.to have_body_text(first_note.note)
end
end
end
end
end end
context 'for a group' do context 'for a group' do
......
...@@ -1928,6 +1928,13 @@ RSpec.describe API::Projects do ...@@ -1928,6 +1928,13 @@ RSpec.describe API::Projects do
end end
end end
end end
it 'exposes service desk attributes' do
get api("/projects/#{project.id}", user)
expect(json_response).to have_key 'service_desk_enabled'
expect(json_response).to have_key 'service_desk_address'
end
end end
describe 'GET /projects/:id/users' do describe 'GET /projects/:id/users' do
......
...@@ -5,7 +5,7 @@ require "spec_helper" ...@@ -5,7 +5,7 @@ require "spec_helper"
RSpec.describe ServiceDeskEmailReceiverWorker, :mailer do RSpec.describe ServiceDeskEmailReceiverWorker, :mailer do
describe '#perform' do describe '#perform' do
let(:worker) { described_class.new } let(:worker) { described_class.new }
let(:email) { fixture_file('emails/service_desk_custom_address.eml', dir: 'ee') } let(:email) { fixture_file('emails/service_desk_custom_address.eml') }
context 'when service_desk_email config is enabled' do context 'when service_desk_email config is enabled' do
before do before 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