# 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(?[^\+]*)\z/.freeze PROJECT_KEY_PATTERN = /\A(?.+)-(?[a-z0-9_]+)\z/.freeze def initialize(mail, mail_key, service_desk_key: nil) if service_desk_key mail_key ||= service_desk_key @service_desk_key = service_desk_key end super(mail, mail_key) match_project_slug || match_legacy_project_slug 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_or_note if from_address add_email_participant send_thank_you_email unless reply_email? end end def match_project_slug return if mail_key&.include?('/') return unless matched = HANDLER_REGEX.match(mail_key.to_s) @project_slug = matched[:project_slug] @project_id = matched[:project_id]&.to_i end def match_legacy_project_slug return unless matched = HANDLER_REGEX_LEGACY.match(mail_key.to_s) @project_path = matched[:project_path] end def metrics_event :receive_email_service_desk end def project strong_memoize(:project) do project_record = super project_record ||= project_from_key if service_desk_key project_record&.service_desk_enabled? ? project_record : nil end end private attr_reader :project_id, :project_path, :service_desk_key def project_from_key return unless match = service_desk_key.match(PROJECT_KEY_PATTERN) Project.with_service_desk_key(match[:key]).find do |project| valid_project_key?(project, match[:slug]) end end def valid_project_key?(project, slug) project.present? && slug == project.full_path_slug end def create_issue_or_note if reply_email? create_note_from_reply_email else create_issue! end end def create_issue! @issue = ::Issues::CreateService.new( project: project, current_user: User.support_bot, params: { title: mail.subject, description: message_including_template, confidential: true, external_author: from_address }, spam_params: nil ).execute raise InvalidIssueError unless @issue.persisted? begin ::Issue::Email.create!(issue: @issue, email_message_id: mail.message_id) rescue StandardError => e Gitlab::ErrorTracking.log_exception(e) end if service_desk_setting&.issue_template_missing? create_template_not_found_note end end def issue_from_reply_to strong_memoize(:issue_from_reply_to) do next unless mail.in_reply_to Issue::Email.find_by_email_message_id(mail.in_reply_to)&.issue end end def reply_email? issue_from_reply_to.present? end def create_note_from_reply_email @issue = issue_from_reply_to create_note(message_including_reply) end def send_thank_you_email Notify.service_desk_thank_you_email(@issue.id).deliver_later Gitlab::Metrics::BackgroundTransaction.current&.add_event(:service_desk_thank_you_email) end def message_including_template description = message_including_reply_or_only_quotes 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_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 create_note(warning_note) end def create_note(note) ::Notes::CreateService.new( project, User.support_bot, noteable: @issue, note: note ).execute end def from_address (mail.reply_to || []).first || mail.from.first || mail.sender end def can_handle_legacy_format? project_path && project_path.include?('/') && !mail_key.include?('+') end def author User.support_bot end def add_email_participant return if reply_email? && !Feature.enabled?(:issue_email_participants, @issue.project) @issue.issue_email_participants.create(email: from_address) end end end end end