feat: bulk actions to update conversation objects (#3934)

Added the endpoints for bulk updating conversation objects

Fixes: #3845 #3940 #3943
This commit is contained in:
Tejaswini Chile 2022-02-23 16:53:36 +05:30 committed by GitHub
parent 9059f5906a
commit 1ca1b4d36b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 295 additions and 1 deletions

View file

@ -0,0 +1,26 @@
class Api::V1::Accounts::BulkActionsController < Api::V1::Accounts::BaseController
before_action :type_matches?
def create
if type_matches?
::BulkActionsJob.perform_later(
account: @current_account,
user: current_user,
params: permitted_params
)
head :ok
else
render json: { success: false }, status: :unprocessable_entity
end
end
private
def type_matches?
['Conversation'].include?(params[:type])
end
def permitted_params
params.permit(:type, ids: [], fields: [:status, :assignee_id, :team_id], labels: [add: [], remove: []])
end
end

View file

@ -0,0 +1,59 @@
class BulkActionsJob < ApplicationJob
queue_as :medium
attr_accessor :records
MODEL_TYPE = ['Conversation'].freeze
def perform(account:, params:, user:)
@account = account
Current.user = user
@params = params
@records = records_to_updated(params[:ids])
bulk_update
ensure
Current.reset
end
def bulk_update
bulk_remove_labels
bulk_conversation_update
end
def bulk_conversation_update
params = available_params(@params)
records.each do |conversation|
bulk_add_labels(conversation)
conversation.update(params) if params
end
end
def bulk_remove_labels
records.each do |conversation|
remove_labels(conversation)
end
end
def available_params(params)
return unless params[:fields]
params[:fields].delete_if { |_k, v| v.nil? }
end
def bulk_add_labels(conversation)
conversation.add_labels(@params[:labels][:add]) if @params[:labels] && @params[:labels][:add]
end
def remove_labels(conversation)
return unless @params[:labels] && @params[:labels][:remove]
labels = conversation.label_list - @params[:labels][:remove]
conversation.update(label_list: labels)
end
def records_to_updated(ids)
current_model = @params[:type].camelcase
return unless MODEL_TYPE.include?(current_model)
current_model.constantize&.where(account_id: @account.id, display_id: ids)
end
end

View file

@ -40,6 +40,7 @@ Rails.application.routes.draw do
resource :contact_merge, only: [:create] resource :contact_merge, only: [:create]
end end
resource :bulk_actions, only: [:create]
resources :agents, only: [:index, :create, :update, :destroy] resources :agents, only: [:index, :create, :update, :destroy]
resources :agent_bots, only: [:index, :create, :show, :update, :destroy] resources :agent_bots, only: [:index, :create, :show, :update, :destroy]

View file

@ -0,0 +1,145 @@
require 'rails_helper'
RSpec.describe 'Api::V1::Accounts::BulkActionsController', type: :request do
include ActiveJob::TestHelper
let(:account) { create(:account) }
let(:agent_1) { create(:user, account: account, role: :agent) }
let(:agent_2) { create(:user, account: account, role: :agent) }
before do
create(:conversation, account_id: account.id, status: :open)
create(:conversation, account_id: account.id, status: :open)
create(:conversation, account_id: account.id, status: :open)
create(:conversation, account_id: account.id, status: :open)
end
describe 'POST /api/v1/accounts/{account.id}/bulk_action' do
context 'when it is an unauthenticated user' do
let(:agent) { create(:user) }
it 'returns unauthorized' do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: { type: 'Conversation', fields: { status: 'open' }, ids: [1, 2, 3] }
expect(response).to have_http_status(:unauthorized)
end
end
context 'when it is an authenticated user' do
let(:agent) { create(:user, account: account, role: :agent) }
it 'Ignores bulk_actions for wrong type' do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: { type: 'Test', fields: { status: 'snoozed' }, ids: %w[1 2 3] }
expect(response).to have_http_status(:unprocessable_entity)
end
it 'Bulk update conversation status' do
expect(Conversation.first.status).to eq('open')
expect(Conversation.last.status).to eq('open')
expect(Conversation.first.assignee_id).to eq(nil)
perform_enqueued_jobs do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: { type: 'Conversation', fields: { status: 'snoozed' }, ids: Conversation.first(3).pluck(:display_id) }
expect(response).to have_http_status(:success)
end
expect(Conversation.first.status).to eq('snoozed')
expect(Conversation.last.status).to eq('open')
expect(Conversation.first.assignee_id).to eq(nil)
end
it 'Bulk update conversation assignee id' do
params = { type: 'Conversation', fields: { assignee_id: agent_1.id }, ids: Conversation.first(3).pluck(:display_id) }
expect(Conversation.first.status).to eq('open')
expect(Conversation.first.assignee_id).to eq(nil)
expect(Conversation.second.assignee_id).to eq(nil)
perform_enqueued_jobs do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: params
expect(response).to have_http_status(:success)
end
expect(Conversation.first.assignee_id).to eq(agent_1.id)
expect(Conversation.second.assignee_id).to eq(agent_1.id)
expect(Conversation.first.status).to eq('open')
end
it 'Bulk update conversation status and assignee id' do
params = { type: 'Conversation', fields: { assignee_id: agent_1.id, status: 'snoozed' }, ids: Conversation.first(3).pluck(:display_id) }
expect(Conversation.first.status).to eq('open')
expect(Conversation.second.assignee_id).to eq(nil)
perform_enqueued_jobs do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: params
expect(response).to have_http_status(:success)
end
expect(Conversation.first.assignee_id).to eq(agent_1.id)
expect(Conversation.second.assignee_id).to eq(agent_1.id)
expect(Conversation.first.status).to eq('snoozed')
expect(Conversation.second.status).to eq('snoozed')
end
it 'Bulk update conversation labels' do
params = { type: 'Conversation', ids: Conversation.first(3).pluck(:display_id), labels: { add: %w[support priority_customer] } }
expect(Conversation.first.labels).to eq([])
expect(Conversation.second.labels).to eq([])
perform_enqueued_jobs do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: params
expect(response).to have_http_status(:success)
end
expect(Conversation.first.label_list).to eq(%w[support priority_customer])
expect(Conversation.second.label_list).to eq(%w[support priority_customer])
end
end
end
describe 'POST /api/v1/accounts/{account.id}/bulk_actions' do
context 'when it is an authenticated user' do
let(:agent) { create(:user, account: account, role: :agent) }
it 'Bulk delete conversation labels' do
Conversation.first.add_labels(%w[support priority_customer])
Conversation.second.add_labels(%w[support priority_customer])
Conversation.third.add_labels(%w[support priority_customer])
params = { type: 'Conversation', ids: Conversation.first(3).pluck(:display_id), labels: { remove: %w[support] } }
expect(Conversation.first.label_list).to eq(%w[support priority_customer])
expect(Conversation.second.label_list).to eq(%w[support priority_customer])
perform_enqueued_jobs do
post "/api/v1/accounts/#{account.id}/bulk_actions",
headers: agent.create_new_auth_token,
params: params
expect(response).to have_http_status(:success)
end
expect(Conversation.first.label_list).to eq(['priority_customer'])
expect(Conversation.second.label_list).to eq(['priority_customer'])
end
end
end
end

View file

@ -0,0 +1,63 @@
require 'rails_helper'
RSpec.describe BulkActionsJob, type: :job do
params = {
type: 'Conversation',
fields: { status: 'snoozed' },
ids: Conversation.first(3).pluck(:display_id)
}
subject(:job) { described_class.perform_later(account: account, params: params, user: agent) }
let(:account) { create(:account) }
let!(:agent) { create(:user, account: account, role: :agent) }
let!(:conversation_1) { create(:conversation, account_id: account.id, status: :open) }
let!(:conversation_2) { create(:conversation, account_id: account.id, status: :open) }
let!(:conversation_3) { create(:conversation, account_id: account.id, status: :open) }
it 'enqueues the job' do
expect { job }.to have_enqueued_job(described_class)
.with(account: account, params: params, user: agent)
.on_queue('medium')
end
context 'when job is triggered' do
let(:bulk_action_job) { double }
before do
allow(bulk_action_job).to receive(:perform)
end
it 'bulk updates the status' do
params = {
type: 'Conversation',
fields: { status: 'snoozed', assignee_id: agent.id },
ids: Conversation.first(3).pluck(:display_id)
}
expect(Conversation.first.status).to eq('open')
described_class.perform_now(account: account, params: params, user: agent)
expect(conversation_1.reload.status).to eq('snoozed')
expect(conversation_2.reload.status).to eq('snoozed')
expect(conversation_3.reload.status).to eq('snoozed')
end
it 'bulk updates the assignee_id' do
params = {
type: 'Conversation',
fields: { status: 'snoozed', assignee_id: agent.id },
ids: Conversation.first(3).pluck(:display_id)
}
expect(Conversation.first.assignee_id).to eq(nil)
described_class.perform_now(account: account, params: params, user: agent)
expect(Conversation.first.assignee_id).to eq(agent.id)
expect(Conversation.second.assignee_id).to eq(agent.id)
expect(Conversation.third.assignee_id).to eq(agent.id)
end
end
end

View file

@ -162,7 +162,7 @@ describe ::Contacts::FilterService do
expected_count = Contact.where("created_at < ? AND custom_attributes->>'customer_type' = ?", Date.tomorrow, 'platinum').count expected_count = Contact.where("created_at < ? AND custom_attributes->>'customer_type' = ?", Date.tomorrow, 'platinum').count
expect(result[:contacts].length).to be expected_count expect(result[:contacts].length).to be expected_count
expect(result[:contacts].first.id).to eq(el_contact.id) expect(result[:contacts].pluck(:id)).to include(el_contact.id)
end end
context 'with x_days_before filter' do context 'with x_days_before filter' do