Commit d5c07c72 authored by Lee Tickett's avatar Lee Tickett Committed by Robert Speicher

Support contact quick actions in issue description

parent 0acbdfa9
...@@ -56,6 +56,8 @@ class IssuableBaseService < ::BaseProjectService ...@@ -56,6 +56,8 @@ class IssuableBaseService < ::BaseProjectService
# confidential attribute is a special type of metadata and needs to be allowed to be set # confidential attribute is a special type of metadata and needs to be allowed to be set
# by non-members on issues in public projects so that security issues can be reported as confidential. # by non-members on issues in public projects so that security issues can be reported as confidential.
params.delete(:confidential) unless can?(current_user, :set_confidentiality, issuable) params.delete(:confidential) unless can?(current_user, :set_confidentiality, issuable)
params.delete(:add_contacts) unless can?(current_user, :set_issue_crm_contacts, issuable)
params.delete(:remove_contacts) unless can?(current_user, :set_issue_crm_contacts, issuable)
filter_assignees(issuable) filter_assignees(issuable)
filter_milestone filter_milestone
...@@ -206,6 +208,9 @@ class IssuableBaseService < ::BaseProjectService ...@@ -206,6 +208,9 @@ class IssuableBaseService < ::BaseProjectService
params[:assignee_ids] = process_assignee_ids(params, extra_assignee_ids: issuable.assignee_ids.to_a) params[:assignee_ids] = process_assignee_ids(params, extra_assignee_ids: issuable.assignee_ids.to_a)
end end
params.delete(:remove_contacts)
add_crm_contact_emails = params.delete(:add_contacts)
issuable.assign_attributes(allowed_create_params(params)) issuable.assign_attributes(allowed_create_params(params))
before_create(issuable) before_create(issuable)
...@@ -219,6 +224,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -219,6 +224,7 @@ class IssuableBaseService < ::BaseProjectService
handle_changes(issuable, { params: params }) handle_changes(issuable, { params: params })
after_create(issuable) after_create(issuable)
set_crm_contacts(issuable, add_crm_contact_emails)
execute_hooks(issuable) execute_hooks(issuable)
users_to_invalidate = issuable.allows_reviewers? ? issuable.assignees | issuable.reviewers : issuable.assignees users_to_invalidate = issuable.allows_reviewers? ? issuable.assignees | issuable.reviewers : issuable.assignees
...@@ -229,6 +235,12 @@ class IssuableBaseService < ::BaseProjectService ...@@ -229,6 +235,12 @@ class IssuableBaseService < ::BaseProjectService
issuable issuable
end end
def set_crm_contacts(issuable, add_crm_contact_emails, remove_crm_contact_emails = [])
return unless add_crm_contact_emails.present? || remove_crm_contact_emails.present?
::Issues::SetCrmContactsService.new(project: project, current_user: current_user, params: { add_emails: add_crm_contact_emails, remove_emails: remove_crm_contact_emails }).execute(issuable)
end
def before_create(issuable) def before_create(issuable)
# To be overridden by subclasses # To be overridden by subclasses
end end
...@@ -254,6 +266,7 @@ class IssuableBaseService < ::BaseProjectService ...@@ -254,6 +266,7 @@ class IssuableBaseService < ::BaseProjectService
assign_requested_labels(issuable) assign_requested_labels(issuable)
assign_requested_assignees(issuable) assign_requested_assignees(issuable)
assign_requested_crm_contacts(issuable)
if issuable.changed? || params.present? if issuable.changed? || params.present?
issuable.assign_attributes(allowed_update_params(params)) issuable.assign_attributes(allowed_update_params(params))
...@@ -414,6 +427,12 @@ class IssuableBaseService < ::BaseProjectService ...@@ -414,6 +427,12 @@ class IssuableBaseService < ::BaseProjectService
issuable.touch issuable.touch
end end
def assign_requested_crm_contacts(issuable)
add_crm_contact_emails = params.delete(:add_contacts)
remove_crm_contact_emails = params.delete(:remove_contacts)
set_crm_contacts(issuable, add_crm_contact_emails, remove_crm_contact_emails)
end
def assign_requested_assignees(issuable) def assign_requested_assignees(issuable)
return if issuable.is_a?(Epic) return if issuable.is_a?(Epic)
......
...@@ -12,7 +12,7 @@ module Issues ...@@ -12,7 +12,7 @@ module Issues
return error_no_permissions unless allowed? return error_no_permissions unless allowed?
return error_invalid_params unless valid_params? return error_invalid_params unless valid_params?
@existing_ids = issue.issue_customer_relations_contacts.map(&:contact_id) @existing_ids = issue.customer_relations_contact_ids
determine_changes if params[:replace_ids].present? determine_changes if params[:replace_ids].present?
return error_too_many if too_many? return error_too_many if too_many?
...@@ -24,6 +24,7 @@ module Issues ...@@ -24,6 +24,7 @@ module Issues
if issue.valid? if issue.valid?
GraphqlTriggers.issue_crm_contacts_updated(issue) GraphqlTriggers.issue_crm_contacts_updated(issue)
issue.touch
ServiceResponse.success(payload: issue) ServiceResponse.success(payload: issue)
else else
# The default error isn't very helpful: "Issue customer relations contacts is invalid" # The default error isn't very helpful: "Issue customer relations contacts is invalid"
......
...@@ -292,17 +292,11 @@ module Gitlab ...@@ -292,17 +292,11 @@ module Gitlab
condition do condition do
current_user.can?(:set_issue_crm_contacts, quick_action_target) current_user.can?(:set_issue_crm_contacts, quick_action_target)
end end
execution_message do
_('One or more contacts were successfully added.')
end
command :add_contacts do |contact_emails| command :add_contacts do |contact_emails|
result = ::Issues::SetCrmContactsService @updates[:add_contacts] = contact_emails.split(' ')
.new(project: project, current_user: current_user, params: { add_emails: contact_emails.split(' ') })
.execute(quick_action_target)
@execution_message[:add_contacts] =
if result.success?
_('One or more contacts were successfully added.')
else
result.message
end
end end
desc _('Remove customer relation contacts') desc _('Remove customer relation contacts')
...@@ -312,17 +306,11 @@ module Gitlab ...@@ -312,17 +306,11 @@ module Gitlab
condition do condition do
current_user.can?(:set_issue_crm_contacts, quick_action_target) current_user.can?(:set_issue_crm_contacts, quick_action_target)
end end
execution_message do
_('One or more contacts were successfully removed.')
end
command :remove_contacts do |contact_emails| command :remove_contacts do |contact_emails|
result = ::Issues::SetCrmContactsService @updates[:remove_contacts] = contact_emails.split(' ')
.new(project: project, current_user: current_user, params: { remove_emails: contact_emails.split(' ') })
.execute(quick_action_target)
@execution_message[:remove_contacts] =
if result.success?
_('One or more contacts were successfully removed.')
else
result.message
end
end end
private private
......
...@@ -5,7 +5,8 @@ require 'spec_helper' ...@@ -5,7 +5,8 @@ require 'spec_helper'
RSpec.describe Issues::CreateService do RSpec.describe Issues::CreateService do
include AfterNextHelpers include AfterNextHelpers
let_it_be_with_reload(:project) { create(:project) } let_it_be(:group) { create(:group) }
let_it_be_with_reload(:project) { create(:project, group: group) }
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
let(:spam_params) { double } let(:spam_params) { double }
...@@ -430,25 +431,29 @@ RSpec.describe Issues::CreateService do ...@@ -430,25 +431,29 @@ RSpec.describe Issues::CreateService do
end end
context 'Quick actions' do context 'Quick actions' do
context 'with assignee and milestone in params and command' do context 'with assignee, milestone, and contact in params and command' do
let_it_be(:contact) { create(:contact, group: group) }
let(:opts) do let(:opts) do
{ {
assignee_ids: [create(:user).id], assignee_ids: [create(:user).id],
milestone_id: 1, milestone_id: 1,
title: 'Title', title: 'Title',
description: %(/assign @#{assignee.username}\n/milestone %"#{milestone.name}") description: %(/assign @#{assignee.username}\n/milestone %"#{milestone.name}"),
add_contacts: [contact.email]
} }
end end
before_all do before_all do
project.add_maintainer(user) group.add_maintainer(user)
project.add_maintainer(assignee) project.add_maintainer(assignee)
end end
it 'assigns and sets milestone to issuable from command' do it 'assigns, sets milestone, and sets contact to issuable from command' do
expect(issue).to be_persisted expect(issue).to be_persisted
expect(issue.assignees).to eq([assignee]) expect(issue.assignees).to eq([assignee])
expect(issue.milestone).to eq(milestone) expect(issue.milestone).to eq(milestone)
expect(issue.issue_customer_relations_contacts.last.contact).to eq(contact)
end end
end end
end end
......
...@@ -29,6 +29,8 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -29,6 +29,8 @@ RSpec.describe Issues::UpdateService, :mailer do
end end
describe 'execute' do describe 'execute' do
let_it_be(:contact) { create(:contact, group: group) }
def find_note(starting_with) def find_note(starting_with)
issue.notes.find do |note| issue.notes.find do |note|
note && note.note.start_with?(starting_with) note && note.note.start_with?(starting_with)
...@@ -57,7 +59,8 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -57,7 +59,8 @@ RSpec.describe Issues::UpdateService, :mailer do
due_date: Date.tomorrow, due_date: Date.tomorrow,
discussion_locked: true, discussion_locked: true,
severity: 'low', severity: 'low',
milestone_id: milestone.id milestone_id: milestone.id,
add_contacts: [contact.email]
} }
end end
...@@ -76,6 +79,7 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -76,6 +79,7 @@ RSpec.describe Issues::UpdateService, :mailer do
expect(issue.discussion_locked).to be_truthy expect(issue.discussion_locked).to be_truthy
expect(issue.confidential).to be_falsey expect(issue.confidential).to be_falsey
expect(issue.milestone).to eq milestone expect(issue.milestone).to eq milestone
expect(issue.issue_customer_relations_contacts.last.contact).to eq contact
end end
it 'updates issue milestone when passing `milestone` param' do it 'updates issue milestone when passing `milestone` param' do
......
...@@ -2253,35 +2253,29 @@ RSpec.describe QuickActions::InterpretService do ...@@ -2253,35 +2253,29 @@ RSpec.describe QuickActions::InterpretService do
end end
it 'add_contacts command does not add the contact' do it 'add_contacts command does not add the contact' do
add_command _, updates, _ = add_command
expect(issue.reload.customer_relations_contacts).to match_array([existing_contact]) expect(updates).to be_empty
end end
it 'remove_contacts command does not remove the contact' do it 'remove_contacts command does not remove the contact' do
remove_command _, updates, _ = remove_command
expect(issue.reload.customer_relations_contacts).to match_array([existing_contact]) expect(updates).to be_empty
end end
end end
it 'add_contacts command adds the contact' do it 'add_contacts command adds the contact' do
_, _, message = add_command _, updates, message = add_command
expect(issue.reload.customer_relations_contacts).to match_array([existing_contact, new_contact]) expect(updates).to eq(add_contacts: [new_contact.email])
expect(message).to eq('One or more contacts were successfully added.') expect(message).to eq('One or more contacts were successfully added.')
end end
it 'add_contacts command returns the correct error when something goes wrong' do
_, _, message = service.execute("/add_contacts #{new_contact.email} #{new_contact.email} #{new_contact.email} #{new_contact.email} #{new_contact.email} #{new_contact.email} #{new_contact.email}", issue)
expect(message).to eq('You can only add up to 6 contacts at one time')
end
it 'remove_contacts command removes the contact' do it 'remove_contacts command removes the contact' do
_, _, message = remove_command _, updates, message = remove_command
expect(issue.reload.customer_relations_contacts).to be_empty expect(updates).to eq(remove_contacts: [existing_contact.email])
expect(message).to eq('One or more contacts were successfully removed.') expect(message).to eq('One or more contacts were successfully removed.')
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