feat: Enable Capacity Config UI (#5164)
- Enables Capacity Config in UI - Rewrite auto assignment Logic to consider only online agents fixes: #4990
This commit is contained in:
@@ -49,9 +49,8 @@
|
||||
</p>
|
||||
</label>
|
||||
|
||||
<!-- disabling this block temporarily -->
|
||||
<div
|
||||
v-if="enableAutoAssignment && isEnterprise && false"
|
||||
v-if="enableAutoAssignment && isEnterprise"
|
||||
class="max-assignment-container"
|
||||
>
|
||||
<woot-input
|
||||
|
||||
@@ -23,8 +23,8 @@ module AssignmentHandler
|
||||
def find_assignee_from_team
|
||||
return if team&.allow_auto_assign.blank?
|
||||
|
||||
team_members = inbox.members.ids & team.members.ids
|
||||
::RoundRobin::AssignmentService.new(conversation: self, allowed_member_ids: team_members).find_assignee
|
||||
team_members_with_capacity = inbox.member_ids_with_assignment_capacity & team.members.ids
|
||||
::AutoAssignment::AgentAssignmentService.new(conversation: self, allowed_agent_ids: team_members_with_capacity).find_assignee
|
||||
end
|
||||
|
||||
def notify_assignment_change
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
module RoundRobinHandler
|
||||
module AutoAssignmentHandler
|
||||
extend ActiveSupport::Concern
|
||||
include Events::Types
|
||||
|
||||
included do
|
||||
after_save :run_round_robin
|
||||
after_save :run_auto_assignment
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_round_robin
|
||||
def run_auto_assignment
|
||||
# Round robin kicks in on conversation create & update
|
||||
# run it only when conversation status changes to open
|
||||
return unless conversation_status_changed_to_open?
|
||||
return unless should_round_robin?
|
||||
return unless should_run_auto_assignment?
|
||||
|
||||
::RoundRobin::AssignmentService.new(conversation: self, allowed_member_ids: inbox.member_ids_with_assignment_capacity).perform
|
||||
::AutoAssignment::AgentAssignmentService.new(conversation: self, allowed_agent_ids: inbox.member_ids_with_assignment_capacity).perform
|
||||
end
|
||||
|
||||
def should_round_robin?
|
||||
def should_run_auto_assignment?
|
||||
return false unless inbox.enable_auto_assignment?
|
||||
|
||||
# run only if assignee is blank or doesn't have access to inbox
|
||||
@@ -41,7 +41,7 @@
|
||||
class Conversation < ApplicationRecord
|
||||
include Labelable
|
||||
include AssignmentHandler
|
||||
include RoundRobinHandler
|
||||
include AutoAssignmentHandler
|
||||
include ActivityMessageHandler
|
||||
include UrlHelper
|
||||
include SortHandler
|
||||
|
||||
@@ -136,7 +136,7 @@ class Inbox < ApplicationRecord
|
||||
end
|
||||
|
||||
def delete_round_robin_agents
|
||||
::RoundRobin::ManageService.new(inbox: self).clear_queue
|
||||
::AutoAssignment::InboxRoundRobinService.new(inbox: self).clear_queue
|
||||
end
|
||||
|
||||
def check_channel_type?
|
||||
|
||||
@@ -28,10 +28,10 @@ class InboxMember < ApplicationRecord
|
||||
private
|
||||
|
||||
def add_agent_to_round_robin
|
||||
::RoundRobin::ManageService.new(inbox: inbox).add_agent_to_queue(user_id)
|
||||
::AutoAssignment::InboxRoundRobinService.new(inbox: inbox).add_agent_to_queue(user_id)
|
||||
end
|
||||
|
||||
def remove_agent_from_round_robin
|
||||
::RoundRobin::ManageService.new(inbox: inbox).remove_agent_from_queue(user_id) if inbox.present?
|
||||
::AutoAssignment::InboxRoundRobinService.new(inbox: inbox).remove_agent_from_queue(user_id) if inbox.present?
|
||||
end
|
||||
end
|
||||
|
||||
38
app/services/auto_assignment/agent_assignment_service.rb
Normal file
38
app/services/auto_assignment/agent_assignment_service.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
class AutoAssignment::AgentAssignmentService
|
||||
# Allowed agent ids: array
|
||||
# This is the list of agents from which an agent can be assigned to this conversation
|
||||
# examples: Agents with assignment capacity, Agents who are members of a team etc
|
||||
pattr_initialize [:conversation!, :allowed_agent_ids!]
|
||||
|
||||
def find_assignee
|
||||
round_robin_manage_service.available_agent(allowed_agent_ids: allowed_online_agent_ids)
|
||||
end
|
||||
|
||||
def perform
|
||||
new_assignee = find_assignee
|
||||
conversation.update(assignee: new_assignee) if new_assignee
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def online_agent_ids
|
||||
online_agents = OnlineStatusTracker.get_available_users(conversation.account_id)
|
||||
online_agents.select { |_key, value| value.eql?('online') }.keys if online_agents.present?
|
||||
end
|
||||
|
||||
def allowed_online_agent_ids
|
||||
# We want to perform roundrobin only over online agents
|
||||
# Hence taking an intersection of online agents and allowed member ids
|
||||
|
||||
# the online user ids are string, since its from redis, allowed member ids are integer, since its from active record
|
||||
@allowed_online_agent_ids ||= online_agent_ids & allowed_agent_ids&.map(&:to_s)
|
||||
end
|
||||
|
||||
def round_robin_manage_service
|
||||
@round_robin_manage_service ||= AutoAssignment::InboxRoundRobinService.new(inbox: conversation.inbox)
|
||||
end
|
||||
|
||||
def round_robin_key
|
||||
format(::Redis::Alfred::ROUND_ROBIN_AGENTS, inbox_id: conversation.inbox_id)
|
||||
end
|
||||
end
|
||||
62
app/services/auto_assignment/inbox_round_robin_service.rb
Normal file
62
app/services/auto_assignment/inbox_round_robin_service.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
class AutoAssignment::InboxRoundRobinService
|
||||
pattr_initialize [:inbox!]
|
||||
|
||||
# called on inbox delete
|
||||
def clear_queue
|
||||
::Redis::Alfred.delete(round_robin_key)
|
||||
end
|
||||
|
||||
# called on inbox member create
|
||||
def add_agent_to_queue(user_id)
|
||||
::Redis::Alfred.lpush(round_robin_key, user_id)
|
||||
end
|
||||
|
||||
# called on inbox member delete
|
||||
def remove_agent_from_queue(user_id)
|
||||
::Redis::Alfred.lrem(round_robin_key, user_id)
|
||||
end
|
||||
|
||||
def reset_queue
|
||||
clear_queue
|
||||
add_agent_to_queue(inbox.inbox_members.map(&:user_id))
|
||||
end
|
||||
|
||||
# end of queue management functions
|
||||
|
||||
# allowed member ids = [assignable online agents supplied by the assignement service]
|
||||
# the values of allowed member ids should be in string format
|
||||
def available_agent(allowed_agent_ids: [])
|
||||
reset_queue unless validate_queue?
|
||||
user_id = get_member_from_allowed_agent_ids(allowed_agent_ids)
|
||||
inbox.inbox_members.find_by(user_id: user_id)&.user if user_id.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_member_from_allowed_agent_ids(allowed_agent_ids)
|
||||
return nil if allowed_agent_ids.blank?
|
||||
|
||||
user_id = queue.intersection(allowed_agent_ids).pop
|
||||
pop_push_to_queue(user_id)
|
||||
user_id
|
||||
end
|
||||
|
||||
def pop_push_to_queue(user_id)
|
||||
return if user_id.blank?
|
||||
|
||||
remove_agent_from_queue(user_id)
|
||||
add_agent_to_queue(user_id)
|
||||
end
|
||||
|
||||
def validate_queue?
|
||||
return true if inbox.inbox_members.map(&:user_id).sort == queue.map(&:to_i).sort
|
||||
end
|
||||
|
||||
def queue
|
||||
::Redis::Alfred.lrange(round_robin_key)
|
||||
end
|
||||
|
||||
def round_robin_key
|
||||
format(::Redis::Alfred::ROUND_ROBIN_AGENTS, inbox_id: inbox.id)
|
||||
end
|
||||
end
|
||||
@@ -1,28 +0,0 @@
|
||||
class RoundRobin::AssignmentService
|
||||
pattr_initialize [:conversation, { allowed_member_ids: [] }]
|
||||
|
||||
def find_assignee
|
||||
round_robin_manage_service.available_agent(priority_list: online_agents)
|
||||
end
|
||||
|
||||
def perform
|
||||
# online agents will get priority
|
||||
new_assignee = find_assignee
|
||||
conversation.update(assignee: new_assignee) if new_assignee
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def online_agents
|
||||
online_agents = OnlineStatusTracker.get_available_users(conversation.account_id)
|
||||
online_agents.select { |_key, value| value.eql?('online') }.keys if online_agents.present?
|
||||
end
|
||||
|
||||
def round_robin_manage_service
|
||||
@round_robin_manage_service ||= RoundRobin::ManageService.new(inbox: conversation.inbox, allowed_member_ids: allowed_member_ids)
|
||||
end
|
||||
|
||||
def round_robin_key
|
||||
format(::Redis::Alfred::ROUND_ROBIN_AGENTS, inbox_id: conversation.inbox_id)
|
||||
end
|
||||
end
|
||||
@@ -1,85 +0,0 @@
|
||||
# NOTE: available agent method now expect allowed_member_ids to be passed in always because of inbox limits feature
|
||||
# need to refactor this class and split the queue managment into a seperate class
|
||||
|
||||
# If allowed_member_ids are supplied round robin service will only fetch a member from member id
|
||||
# This is used in case of team assignment
|
||||
class RoundRobin::ManageService
|
||||
pattr_initialize [:inbox!, { allowed_member_ids: [] }]
|
||||
|
||||
# called on inbox delete
|
||||
def clear_queue
|
||||
::Redis::Alfred.delete(round_robin_key)
|
||||
end
|
||||
|
||||
# called on inbox member create
|
||||
def add_agent_to_queue(user_id)
|
||||
::Redis::Alfred.lpush(round_robin_key, user_id)
|
||||
end
|
||||
|
||||
# called on inbox member delete
|
||||
def remove_agent_from_queue(user_id)
|
||||
::Redis::Alfred.lrem(round_robin_key, user_id)
|
||||
end
|
||||
|
||||
def reset_queue
|
||||
clear_queue
|
||||
add_agent_to_queue(inbox.inbox_members.map(&:user_id))
|
||||
end
|
||||
|
||||
# end of queue management functions
|
||||
|
||||
def available_agent(priority_list: [])
|
||||
reset_queue unless validate_queue?
|
||||
user_id = get_member_via_priority_list(priority_list)
|
||||
# incase priority list was empty or inbox members weren't present
|
||||
user_id ||= fetch_user_id
|
||||
inbox.inbox_members.find_by(user_id: user_id)&.user if user_id.present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_user_id
|
||||
return nil if allowed_member_ids_in_str.blank?
|
||||
|
||||
user_id = queue.intersection(allowed_member_ids_in_str).pop
|
||||
pop_push_to_queue(user_id)
|
||||
user_id
|
||||
end
|
||||
|
||||
# priority list is usually the members who are online passed from assignment service
|
||||
def get_member_via_priority_list(priority_list)
|
||||
return if priority_list.blank?
|
||||
|
||||
# When allowed member ids is passed we will be looking to get members from that list alone
|
||||
priority_list = priority_list.intersection(allowed_member_ids_in_str)
|
||||
return if priority_list.blank?
|
||||
|
||||
user_id = queue.intersection(priority_list.map(&:to_s)).pop
|
||||
pop_push_to_queue(user_id)
|
||||
user_id
|
||||
end
|
||||
|
||||
def pop_push_to_queue(user_id)
|
||||
return if user_id.blank?
|
||||
|
||||
remove_agent_from_queue(user_id)
|
||||
add_agent_to_queue(user_id)
|
||||
end
|
||||
|
||||
def validate_queue?
|
||||
return true if inbox.inbox_members.map(&:user_id).sort == queue.map(&:to_i).sort
|
||||
end
|
||||
|
||||
def queue
|
||||
::Redis::Alfred.lrange(round_robin_key)
|
||||
end
|
||||
|
||||
def round_robin_key
|
||||
format(::Redis::Alfred::ROUND_ROBIN_AGENTS, inbox_id: inbox.id)
|
||||
end
|
||||
|
||||
def allowed_member_ids_in_str
|
||||
# NOTE: the values which are returned from redis for priority list are string
|
||||
@allowed_member_ids_in_str ||= allowed_member_ids.map(&:to_s)
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
module OnlineStatusTracker
|
||||
class OnlineStatusTracker
|
||||
# NOTE: You can customise the environment variable to keep your agents/contacts as online for longer
|
||||
PRESENCE_DURATION = ENV.fetch('PRESENCE_DURATION', 20).to_i.seconds
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ RSpec.describe 'Conversation Assignment API', type: :request do
|
||||
end
|
||||
|
||||
it 'assigns a team to the conversation' do
|
||||
team_member = create(:user, account: account, role: :agent)
|
||||
team_member = create(:user, account: account, role: :agent, auto_offline: false)
|
||||
create(:inbox_member, inbox: conversation.inbox, user: team_member)
|
||||
create(:team_member, team: team, user: team_member)
|
||||
params = { team_id: team.id }
|
||||
|
||||
@@ -220,7 +220,7 @@ RSpec.describe 'Conversations API', type: :request do
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
let(:agent) { create(:user, account: account, role: :agent, auto_offline: false) }
|
||||
let(:team) { create(:team, account: account) }
|
||||
|
||||
it 'will not create a new conversation if agent does not have access to inbox' do
|
||||
|
||||
@@ -5,6 +5,7 @@ FactoryBot.define do
|
||||
transient do
|
||||
skip_confirmation { true }
|
||||
role { 'agent' }
|
||||
auto_offline { true }
|
||||
account { nil }
|
||||
inviter { nil }
|
||||
end
|
||||
@@ -18,7 +19,10 @@ FactoryBot.define do
|
||||
|
||||
after(:build) do |user, evaluator|
|
||||
user.skip_confirmation! if evaluator.skip_confirmation
|
||||
create(:account_user, user: user, account: evaluator.account, role: evaluator.role, inviter: evaluator.inviter) if evaluator.account
|
||||
if evaluator.account
|
||||
create(:account_user, user: user, account: evaluator.account, role: evaluator.role, inviter: evaluator.inviter,
|
||||
auto_offline: evaluator.auto_offline)
|
||||
end
|
||||
end
|
||||
|
||||
trait :with_avatar do
|
||||
|
||||
@@ -6,7 +6,7 @@ shared_examples_for 'assignment_handler' do
|
||||
describe '#update_team' do
|
||||
let(:conversation) { create(:conversation, assignee: create(:user)) }
|
||||
let(:agent) do
|
||||
create(:user, email: 'agent@example.com', account: conversation.account, role: :agent)
|
||||
create(:user, email: 'agent@example.com', account: conversation.account, role: :agent, auto_offline: false)
|
||||
end
|
||||
let(:team) do
|
||||
create(:team, account: conversation.account, allow_auto_assign: false)
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
shared_examples_for 'round_robin_handler' do
|
||||
describe '#round robin' do
|
||||
shared_examples_for 'auto_assignment_handler' do
|
||||
describe '#auto assignment' do
|
||||
let(:account) { create(:account) }
|
||||
let(:agent) { create(:user, email: 'agent1@example.com', account: account) }
|
||||
let(:agent) { create(:user, email: 'agent1@example.com', account: account, auto_offline: false) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
let(:conversation) do
|
||||
create(
|
||||
@@ -23,14 +23,12 @@ shared_examples_for 'round_robin_handler' do
|
||||
end
|
||||
|
||||
it 'runs round robin on after_save callbacks' do
|
||||
# run_round_robin
|
||||
expect(conversation.reload.assignee).to eq(agent)
|
||||
end
|
||||
|
||||
it 'will not auto assign agent if enable_auto_assignment is false' do
|
||||
inbox.update(enable_auto_assignment: false)
|
||||
|
||||
# run_round_robin
|
||||
expect(conversation.reload.assignee).to be_nil
|
||||
end
|
||||
|
||||
@@ -44,7 +42,6 @@ shared_examples_for 'round_robin_handler' do
|
||||
assignee: nil
|
||||
)
|
||||
|
||||
# run_round_robin
|
||||
expect(conversation.reload.assignee).to be_nil
|
||||
end
|
||||
|
||||
@@ -55,7 +52,7 @@ shared_examples_for 'round_robin_handler' do
|
||||
inbox.inbox_members.where(user_id: agent.id).first.destroy!
|
||||
|
||||
# round robin changes assignee in this case since agent doesn't have access to inbox
|
||||
agent2 = create(:user, email: 'agent2@example.com', account: account)
|
||||
agent2 = create(:user, email: 'agent2@example.com', account: account, auto_offline: false)
|
||||
create(:inbox_member, inbox: inbox, user: agent2)
|
||||
allow(Redis::Alfred).to receive(:rpoplpush).and_return(agent2.id)
|
||||
conversation.status = 'open'
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
require 'rails_helper'
|
||||
require Rails.root.join 'spec/models/concerns/assignment_handler_shared.rb'
|
||||
require Rails.root.join 'spec/models/concerns/round_robin_handler_shared.rb'
|
||||
require Rails.root.join 'spec/models/concerns/auto_assignment_handler_shared.rb'
|
||||
|
||||
RSpec.describe Conversation, type: :model do
|
||||
describe 'associations' do
|
||||
@@ -12,7 +12,7 @@ RSpec.describe Conversation, type: :model do
|
||||
|
||||
describe 'concerns' do
|
||||
it_behaves_like 'assignment_handler'
|
||||
it_behaves_like 'round_robin_handler'
|
||||
it_behaves_like 'auto_assignment_handler'
|
||||
end
|
||||
|
||||
describe '.before_create' do
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AutoAssignment::AgentAssignmentService do
|
||||
let!(:account) { create(:account) }
|
||||
let!(:inbox) { create(:inbox, account: account, enable_auto_assignment: false) }
|
||||
let!(:inbox_members) { create_list(:inbox_member, 5, inbox: inbox) }
|
||||
let!(:conversation) { create(:conversation, inbox: inbox, account: account) }
|
||||
let!(:online_users) do
|
||||
{
|
||||
inbox_members[0].user_id.to_s => 'busy',
|
||||
inbox_members[1].user_id.to_s => 'busy',
|
||||
inbox_members[2].user_id.to_s => 'busy',
|
||||
inbox_members[3].user_id.to_s => 'online',
|
||||
inbox_members[4].user_id.to_s => 'online'
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
allow(::OnlineStatusTracker).to receive(:get_available_users).and_return(online_users)
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
it 'will assign an online agent to the conversation' do
|
||||
expect(conversation.reload.assignee).to be_nil
|
||||
described_class.new(conversation: conversation, allowed_agent_ids: inbox_members.map(&:user_id).map(&:to_s)).perform
|
||||
expect(conversation.reload.assignee).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_assignee' do
|
||||
it 'will return an online agent from the allowed agent ids in roud robin' do
|
||||
expect(described_class.new(conversation: conversation,
|
||||
allowed_agent_ids: inbox_members.map(&:user_id).map(&:to_s)).find_assignee).to eq(inbox_members[3].user)
|
||||
expect(described_class.new(conversation: conversation,
|
||||
allowed_agent_ids: inbox_members.map(&:user_id).map(&:to_s)).find_assignee).to eq(inbox_members[4].user)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,59 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe AutoAssignment::InboxRoundRobinService do
|
||||
subject(:inbox_round_robin_service) { described_class.new(inbox: inbox) }
|
||||
|
||||
let!(:account) { create(:account) }
|
||||
let!(:inbox) { create(:inbox, account: account) }
|
||||
let!(:inbox_members) { create_list(:inbox_member, 5, inbox: inbox) }
|
||||
|
||||
describe '#available_agent' do
|
||||
it 'returns nil if allowed_agent_ids is not passed or empty' do
|
||||
expect(described_class.new(inbox: inbox).available_agent).to be_nil
|
||||
end
|
||||
|
||||
it 'gets the first available agent in allowed_agent_ids and move agent to end of the list' do
|
||||
expected_queue = [inbox_members[0].user_id, inbox_members[4].user_id, inbox_members[3].user_id, inbox_members[2].user_id,
|
||||
inbox_members[1].user_id].map(&:to_s)
|
||||
described_class.new(inbox: inbox).available_agent(allowed_agent_ids: [inbox_members[0].user_id, inbox_members[4].user_id].map(&:to_s))
|
||||
expect(inbox_round_robin_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
|
||||
it 'constructs round_robin_queue if queue is not present' do
|
||||
inbox_round_robin_service.clear_queue
|
||||
expect(inbox_round_robin_service.send(:queue)).to eq([])
|
||||
inbox_round_robin_service.available_agent
|
||||
# the service constructed the redis queue before performing
|
||||
expect(inbox_round_robin_service.send(:queue).sort.map(&:to_i)).to eq(inbox_members.map(&:user_id).sort)
|
||||
end
|
||||
|
||||
it 'validates the queue and correct it before performing round robin' do
|
||||
# adding some invalid ids to queue
|
||||
inbox_round_robin_service.add_agent_to_queue([2, 3, 5, 9])
|
||||
expect(inbox_round_robin_service.send(:queue).sort.map(&:to_i)).not_to eq(inbox_members.map(&:user_id).sort)
|
||||
inbox_round_robin_service.available_agent
|
||||
# the service have refreshed the redis queue before performing
|
||||
expect(inbox_round_robin_service.send(:queue).sort.map(&:to_i)).to eq(inbox_members.map(&:user_id).sort)
|
||||
end
|
||||
|
||||
context 'when allowed_agent_ids is passed' do
|
||||
it 'will get the first allowed member and move it to the end of the queue' do
|
||||
expected_queue = [inbox_members[3].user_id, inbox_members[2].user_id, inbox_members[4].user_id, inbox_members[1].user_id,
|
||||
inbox_members[0].user_id].map(&:to_s)
|
||||
expect(described_class.new(inbox: inbox).available_agent(
|
||||
allowed_agent_ids: [
|
||||
inbox_members[3].user_id,
|
||||
inbox_members[2].user_id
|
||||
].map(&:to_s)
|
||||
)).to eq inbox_members[2].user
|
||||
expect(described_class.new(inbox: inbox).available_agent(
|
||||
allowed_agent_ids: [
|
||||
inbox_members[3].user_id,
|
||||
inbox_members[2].user_id
|
||||
].map(&:to_s)
|
||||
)).to eq inbox_members[3].user
|
||||
expect(inbox_round_robin_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,75 +0,0 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe RoundRobin::ManageService do
|
||||
subject(:round_robin_manage_service) { described_class.new(inbox: inbox) }
|
||||
|
||||
let!(:account) { create(:account) }
|
||||
let!(:inbox) { create(:inbox, account: account) }
|
||||
let!(:inbox_members) { create_list(:inbox_member, 5, inbox: inbox) }
|
||||
|
||||
describe '#available_agent' do
|
||||
it 'returns nil if allowed_member_ids is empty' do
|
||||
expect(described_class.new(inbox: inbox, allowed_member_ids: []).available_agent).to be_nil
|
||||
end
|
||||
|
||||
it 'gets the first available agent in allowed_member_ids and move agent to end of the list' do
|
||||
expected_queue = [inbox_members[0].user_id, inbox_members[4].user_id, inbox_members[3].user_id, inbox_members[2].user_id,
|
||||
inbox_members[1].user_id].map(&:to_s)
|
||||
described_class.new(inbox: inbox, allowed_member_ids: [inbox_members[0].user_id, inbox_members[4].user_id]).available_agent
|
||||
expect(round_robin_manage_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
|
||||
it 'gets intersection of priority list and agent queue. get and move agent to the end of the list' do
|
||||
expected_queue = [inbox_members[2].user_id, inbox_members[4].user_id, inbox_members[3].user_id, inbox_members[1].user_id,
|
||||
inbox_members[0].user_id].map(&:to_s)
|
||||
# prority list will be ids in string, since thats what redis supplies to us
|
||||
expect(described_class.new(inbox: inbox, allowed_member_ids: [inbox_members[2].user_id])
|
||||
.available_agent(priority_list: [inbox_members[3].user_id.to_s, inbox_members[2].user_id.to_s])).to eq inbox_members[2].user
|
||||
expect(round_robin_manage_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
|
||||
it 'constructs round_robin_queue if queue is not present' do
|
||||
round_robin_manage_service.clear_queue
|
||||
expect(round_robin_manage_service.send(:queue)).to eq([])
|
||||
round_robin_manage_service.available_agent
|
||||
# the service constructed the redis queue before performing
|
||||
expect(round_robin_manage_service.send(:queue).sort.map(&:to_i)).to eq(inbox_members.map(&:user_id).sort)
|
||||
end
|
||||
|
||||
it 'validates the queue and correct it before performing round robin' do
|
||||
# adding some invalid ids to queue
|
||||
round_robin_manage_service.add_agent_to_queue([2, 3, 5, 9])
|
||||
expect(round_robin_manage_service.send(:queue).sort.map(&:to_i)).not_to eq(inbox_members.map(&:user_id).sort)
|
||||
round_robin_manage_service.available_agent
|
||||
# the service have refreshed the redis queue before performing
|
||||
expect(round_robin_manage_service.send(:queue).sort.map(&:to_i)).to eq(inbox_members.map(&:user_id).sort)
|
||||
end
|
||||
|
||||
context 'when allowed_member_ids is passed' do
|
||||
it 'will get the first allowed member and move it to the end of the queue' do
|
||||
expected_queue = [inbox_members[3].user_id, inbox_members[2].user_id, inbox_members[4].user_id, inbox_members[1].user_id,
|
||||
inbox_members[0].user_id].map(&:to_s)
|
||||
expect(described_class.new(inbox: inbox,
|
||||
allowed_member_ids: [inbox_members[3].user_id,
|
||||
inbox_members[2].user_id]).available_agent).to eq inbox_members[2].user
|
||||
expect(described_class.new(inbox: inbox,
|
||||
allowed_member_ids: [inbox_members[3].user_id,
|
||||
inbox_members[2].user_id]).available_agent).to eq inbox_members[3].user
|
||||
expect(round_robin_manage_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
|
||||
it 'will get union of priority list and allowed_member_ids and move it to the end of the queue' do
|
||||
expected_queue = [inbox_members[3].user_id, inbox_members[4].user_id, inbox_members[2].user_id, inbox_members[1].user_id,
|
||||
inbox_members[0].user_id].map(&:to_s)
|
||||
# prority list will be ids in string, since thats what redis supplies to us
|
||||
expect(described_class.new(inbox: inbox,
|
||||
allowed_member_ids: [inbox_members[3].user_id,
|
||||
inbox_members[2].user_id])
|
||||
.available_agent(
|
||||
priority_list: [inbox_members[3].user_id.to_s]
|
||||
)).to eq inbox_members[3].user
|
||||
expect(round_robin_manage_service.send(:queue)).to eq(expected_queue)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user