# frozen_string_literal: true module Issues class SetCrmContactsService < ::BaseProjectService MAX_ADDITIONAL_CONTACTS = 6 # Replacing contacts by email is not currently supported def execute(issue) @issue = issue @errors = [] return error_no_permissions unless allowed? return error_invalid_params unless valid_params? @existing_ids = issue.customer_relations_contact_ids determine_changes if params[:replace_ids].present? return error_too_many if too_many? @added_count = 0 @removed_count = 0 add if params[:add_ids].present? remove if params[:remove_ids].present? add_by_email if params[:add_emails].present? remove_by_email if params[:remove_emails].present? if issue.valid? GraphqlTriggers.issue_crm_contacts_updated(issue) issue.touch create_system_note ServiceResponse.success(payload: issue) else # The default error isn't very helpful: "Issue customer relations contacts is invalid" issue.errors.delete(:issue_customer_relations_contacts) issue.errors.add(:issue_customer_relations_contacts, errors.to_sentence) ServiceResponse.error(payload: issue, message: issue.errors.full_messages.to_sentence) end end private attr_accessor :issue, :errors, :existing_ids, :added_count, :removed_count def determine_changes params[:add_ids] = params[:replace_ids] - existing_ids params[:remove_ids] = existing_ids - params[:replace_ids] end def add add_by_id(params[:add_ids]) end def add_by_email contact_ids = ::CustomerRelations::Contact.find_ids_by_emails(project_group.root_ancestor, emails(:add_emails)) add_by_id(contact_ids) end def emails(key) params[key].map do |email| extract_email_from_request_param(email) end end def add_by_id(contact_ids) contact_ids -= existing_ids contact_ids.uniq.each do |contact_id| issue_contact = issue.issue_customer_relations_contacts.create(contact_id: contact_id) if issue_contact.persisted? @added_count += 1 else # The validation ensures that the id exists and the user has permission errors << "#{contact_id}: The resource that you are attempting to access does not exist or you don't have permission to perform this action" end end end def remove remove_by_id(params[:remove_ids]) end def remove_by_email contact_ids = ::CustomerRelations::IssueContact.find_contact_ids_by_emails(issue.id, emails(:remove_emails)) remove_by_id(contact_ids) end def remove_by_id(contact_ids) contact_ids &= existing_ids @removed_count += issue.issue_customer_relations_contacts .where(contact_id: contact_ids) # rubocop: disable CodeReuse/ActiveRecord .delete_all end def extract_email_from_request_param(email_param) email_param.delete_prefix(::CustomerRelations::Contact.reference_prefix_quoted) .delete_prefix(::CustomerRelations::Contact.reference_prefix) .delete_suffix(::CustomerRelations::Contact.reference_postfix) .tr('"', '') end def allowed? current_user&.can?(:set_issue_crm_contacts, issue) end def valid_params? set_present? ^ add_or_remove_present? end def set_present? params[:replace_ids].present? end def add_or_remove_present? add_present? || remove_present? end def add_present? params[:add_ids].present? || params[:add_emails].present? end def remove_present? params[:remove_ids].present? || params[:remove_emails].present? end def too_many? too_many_ids? || too_many_emails? end def too_many_ids? params[:add_ids] && params[:add_ids].length > MAX_ADDITIONAL_CONTACTS end def too_many_emails? params[:add_emails] && params[:add_emails].length > MAX_ADDITIONAL_CONTACTS end def create_system_note SystemNoteService.change_issuable_contacts( issue, issue.project, current_user, added_count, removed_count) end def error_no_permissions ServiceResponse.error(message: _('You have insufficient permissions to set customer relations contacts for this issue')) end def error_invalid_params ServiceResponse.error(message: _('You cannot combine replace_ids with add_ids or remove_ids')) end def error_too_many ServiceResponse.error(payload: issue, message: _("You can only add up to %{max_contacts} contacts at one time" % { max_contacts: MAX_ADDITIONAL_CONTACTS })) end end end