# 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) 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! if from_address add_email_participant send_thank_you_email! end 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 end def create_issue! @issue = Issues::CreateService.new( project, User.support_bot, title: issue_title, description: message_including_template, confidential: true, external_author: 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 def add_email_participant @issue.issue_email_participants.create(email: from_address) end end end end end