+
+
diff --git a/app/services/contacts/bulk_action_service.rb b/app/services/contacts/bulk_action_service.rb
index c759e95a5..a0e11ad9a 100644
--- a/app/services/contacts/bulk_action_service.rb
+++ b/app/services/contacts/bulk_action_service.rb
@@ -6,6 +6,7 @@ class Contacts::BulkActionService
end
def perform
+ return delete_contacts if delete_requested?
return assign_labels if labels_to_add.any?
Rails.logger.warn("Unknown contact bulk operation payload: #{@params.keys}")
@@ -22,6 +23,13 @@ class Contacts::BulkActionService
).perform
end
+ def delete_contacts
+ Contacts::BulkDeleteService.new(
+ account: @account,
+ contact_ids: ids
+ ).perform
+ end
+
def ids
Array(@params[:ids]).compact
end
@@ -29,4 +37,8 @@ class Contacts::BulkActionService
def labels_to_add
@labels_to_add ||= Array(@params.dig(:labels, :add)).reject(&:blank?)
end
+
+ def delete_requested?
+ @params[:action_name] == 'delete'
+ end
end
diff --git a/app/services/contacts/bulk_delete_service.rb b/app/services/contacts/bulk_delete_service.rb
new file mode 100644
index 000000000..d197f17f6
--- /dev/null
+++ b/app/services/contacts/bulk_delete_service.rb
@@ -0,0 +1,18 @@
+class Contacts::BulkDeleteService
+ def initialize(account:, contact_ids: [])
+ @account = account
+ @contact_ids = Array(contact_ids).compact
+ end
+
+ def perform
+ return if @contact_ids.blank?
+
+ contacts.find_each(&:destroy!)
+ end
+
+ private
+
+ def contacts
+ @account.contacts.where(id: @contact_ids)
+ end
+end
diff --git a/spec/controllers/api/v1/accounts/bulk_actions_controller_spec.rb b/spec/controllers/api/v1/accounts/bulk_actions_controller_spec.rb
index 9c53099fd..1ab490a96 100644
--- a/spec/controllers/api/v1/accounts/bulk_actions_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/bulk_actions_controller_spec.rb
@@ -195,37 +195,6 @@ RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
expect(Conversation.first.label_list).to contain_exactly('support', 'priority_customer')
expect(Conversation.second.label_list).to contain_exactly('support', 'priority_customer')
end
-
- it 'enqueues contact bulk action job with permitted params' do
- contact_one = create(:contact, account: account)
- contact_two = create(:contact, account: account)
-
- previous_adapter = ActiveJob::Base.queue_adapter
- ActiveJob::Base.queue_adapter = :test
-
- expect do
- post "/api/v1/accounts/#{account.id}/bulk_actions",
- headers: agent.create_new_auth_token,
- params: {
- type: 'Contact',
- ids: [contact_one.id, contact_two.id],
- labels: { add: %w[vip support] },
- extra: 'ignored'
- }
- end.to have_enqueued_job(Contacts::BulkActionJob).with(
- account.id,
- agent.id,
- hash_including(
- 'ids' => [contact_one.id, contact_two.id],
- 'labels' => hash_including('add' => %w[vip support])
- )
- )
-
- expect(response).to have_http_status(:success)
- ensure
- ActiveJob::Base.queue_adapter = previous_adapter
- clear_enqueued_jobs
- end
end
end
@@ -256,4 +225,49 @@ RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
end
end
end
+
+ describe 'POST /api/v1/accounts/{account.id}/bulk_actions (contacts)' do
+ context 'when it is an authenticated user' do
+ let!(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'enqueues Contacts::BulkActionJob with permitted params' do
+ contact_one = create(:contact, account: account)
+ contact_two = create(:contact, account: account)
+
+ expect do
+ post "/api/v1/accounts/#{account.id}/bulk_actions",
+ headers: agent.create_new_auth_token,
+ params: {
+ type: 'Contact',
+ ids: [contact_one.id, contact_two.id],
+ labels: { add: %w[vip support] },
+ extra: 'ignored'
+ }
+ end.to have_enqueued_job(Contacts::BulkActionJob).with(
+ account.id,
+ agent.id,
+ hash_including(
+ 'ids' => [contact_one.id.to_s, contact_two.id.to_s],
+ 'labels' => hash_including('add' => %w[vip support])
+ )
+ )
+
+ expect(response).to have_http_status(:success)
+ end
+
+ it 'returns unauthorized for delete action when user is not admin' do
+ contact = create(:contact, account: account)
+
+ post "/api/v1/accounts/#{account.id}/bulk_actions",
+ headers: agent.create_new_auth_token,
+ params: {
+ type: 'Contact',
+ ids: [contact.id],
+ action_name: 'delete'
+ }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+ end
end
diff --git a/spec/services/contacts/bulk_action_service_spec.rb b/spec/services/contacts/bulk_action_service_spec.rb
new file mode 100644
index 000000000..0411c8455
--- /dev/null
+++ b/spec/services/contacts/bulk_action_service_spec.rb
@@ -0,0 +1,38 @@
+require 'rails_helper'
+
+RSpec.describe Contacts::BulkActionService do
+ subject(:service) { described_class.new(account: account, user: user, params: params) }
+
+ let(:account) { create(:account) }
+ let(:user) { create(:user, account: account) }
+
+ describe '#perform' do
+ context 'when delete action is requested via action_name' do
+ let(:params) { { ids: [1, 2], action_name: 'delete' } }
+
+ it 'delegates to the bulk delete service' do
+ bulk_delete_service = instance_double(Contacts::BulkDeleteService, perform: true)
+
+ expect(Contacts::BulkDeleteService).to receive(:new)
+ .with(account: account, contact_ids: [1, 2])
+ .and_return(bulk_delete_service)
+
+ service.perform
+ end
+ end
+
+ context 'when labels are provided' do
+ let(:params) { { ids: [10, 20], labels: { add: %w[vip support] }, extra: 'ignored' } }
+
+ it 'delegates to the bulk assign labels service with permitted params' do
+ bulk_assign_service = instance_double(Contacts::BulkAssignLabelsService, perform: true)
+
+ expect(Contacts::BulkAssignLabelsService).to receive(:new)
+ .with(account: account, contact_ids: [10, 20], labels: %w[vip support])
+ .and_return(bulk_assign_service)
+
+ service.perform
+ end
+ end
+ end
+end
diff --git a/spec/services/contacts/bulk_delete_service_spec.rb b/spec/services/contacts/bulk_delete_service_spec.rb
new file mode 100644
index 000000000..4eaecaa0f
--- /dev/null
+++ b/spec/services/contacts/bulk_delete_service_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+RSpec.describe Contacts::BulkDeleteService do
+ subject(:service) { described_class.new(account: account, contact_ids: contact_ids) }
+
+ let(:account) { create(:account) }
+ let!(:contact_one) { create(:contact, account: account) }
+ let!(:contact_two) { create(:contact, account: account) }
+ let(:contact_ids) { [contact_one.id, contact_two.id] }
+
+ describe '#perform' do
+ it 'deletes the provided contacts' do
+ expect { service.perform }
+ .to change { account.contacts.exists?(contact_one.id) }.from(true).to(false)
+ .and change { account.contacts.exists?(contact_two.id) }.from(true).to(false)
+ end
+
+ it 'returns when no contact ids are provided' do
+ empty_service = described_class.new(account: account, contact_ids: [])
+
+ expect { empty_service.perform }.not_to change(Contact, :count)
+ end
+ end
+end