Maintenance: Improved notifications flow for replacement agents.
This commit is contained in:
parent
82b61e4811
commit
7d56a6a975
5 changed files with 168 additions and 19 deletions
|
@ -75,10 +75,11 @@ class Transaction::Notification
|
||||||
# apply out of office agents
|
# apply out of office agents
|
||||||
possible_recipients_additions = Set.new
|
possible_recipients_additions = Set.new
|
||||||
possible_recipients.each do |user|
|
possible_recipients.each do |user|
|
||||||
recursive_ooo_replacements(
|
ooo_replacements(
|
||||||
user: user,
|
user: user,
|
||||||
replacements: possible_recipients_additions,
|
replacements: possible_recipients_additions,
|
||||||
reasons: recipients_reason,
|
reasons: recipients_reason,
|
||||||
|
ticket: ticket,
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -339,26 +340,16 @@ class Transaction::Notification
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def recursive_ooo_replacements(user:, replacements:, reasons:, level: 0)
|
def ooo_replacements(user:, replacements:, ticket:, reasons:)
|
||||||
if level == 10
|
|
||||||
Rails.logger.warn("Found more than 10 replacement levels for agent #{user}.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
replacement = user.out_of_office_agent
|
replacement = user.out_of_office_agent
|
||||||
|
|
||||||
return if !replacement
|
return if !replacement
|
||||||
# return for already found, added and checked users
|
|
||||||
# to prevent re-doing complete lookup paths
|
return if !TicketPolicy.new(replacement, ticket).agent_read_access?
|
||||||
|
|
||||||
return if !replacements.add?(replacement)
|
return if !replacements.add?(replacement)
|
||||||
|
|
||||||
reasons[replacement.id] = 'are the out-of-office replacement of the owner'
|
reasons[replacement.id] = 'are the out-of-office replacement of the owner'
|
||||||
|
|
||||||
recursive_ooo_replacements(
|
|
||||||
user: replacement,
|
|
||||||
replacements: replacements,
|
|
||||||
reasons: reasons,
|
|
||||||
level: level + 1
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def possible_recipients_of_group(group_id)
|
def possible_recipients_of_group(group_id)
|
||||||
|
|
|
@ -217,10 +217,15 @@ returns
|
||||||
|
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def out_of_office_agent(loop_user_ids: [])
|
def out_of_office_agent(loop_user_ids: [], stack_depth: 10)
|
||||||
return if !out_of_office?
|
return if !out_of_office?
|
||||||
return if out_of_office_replacement_id.blank?
|
return if out_of_office_replacement_id.blank?
|
||||||
|
|
||||||
|
if stack_depth.zero?
|
||||||
|
Rails.logger.warn("Found more than 10 replacement levels for agent #{self}.")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
user = User.find_by(id: out_of_office_replacement_id)
|
user = User.find_by(id: out_of_office_replacement_id)
|
||||||
|
|
||||||
# stop if users are occuring multiple times to prevent endless loops
|
# stop if users are occuring multiple times to prevent endless loops
|
||||||
|
@ -228,7 +233,7 @@ returns
|
||||||
|
|
||||||
loop_user_ids |= [out_of_office_replacement_id]
|
loop_user_ids |= [out_of_office_replacement_id]
|
||||||
|
|
||||||
ooo_agent = user.out_of_office_agent(loop_user_ids: loop_user_ids)
|
ooo_agent = user.out_of_office_agent(loop_user_ids: loop_user_ids, stack_depth: stack_depth - 1)
|
||||||
return ooo_agent if ooo_agent.present?
|
return ooo_agent if ooo_agent.present?
|
||||||
|
|
||||||
user
|
user
|
||||||
|
|
|
@ -58,6 +58,18 @@ FactoryBot.define do
|
||||||
user.define_singleton_method(:password_plain, -> { password_plain })
|
user.define_singleton_method(:password_plain, -> { password_plain })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :groupable do
|
||||||
|
transient do
|
||||||
|
group { nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
after(:create) do |user, context|
|
||||||
|
Array(context.group).each do |group|
|
||||||
|
user.groups << group
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
trait :preferencable do
|
trait :preferencable do
|
||||||
transient do
|
transient do
|
||||||
notification_group_ids { [] }
|
notification_group_ids { [] }
|
||||||
|
@ -77,6 +89,17 @@ FactoryBot.define do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
trait :ooo do
|
||||||
|
transient do
|
||||||
|
ooo_agent { nil }
|
||||||
|
end
|
||||||
|
|
||||||
|
out_of_office { true }
|
||||||
|
out_of_office_start_at { 1.day.ago }
|
||||||
|
out_of_office_end_at { 1.day.from_now }
|
||||||
|
out_of_office_replacement_id { ooo_agent.id }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sequence(:password_valid) do |n|
|
sequence(:password_valid) do |n|
|
||||||
|
|
|
@ -31,7 +31,77 @@ RSpec.describe Transaction::Notification, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe '#ooo_replacements' do
|
||||||
|
subject(:notification_instance) { build(ticket, user) }
|
||||||
|
|
||||||
|
let(:group) { create(:group) }
|
||||||
|
let(:user) { create(:agent, :ooo, :groupable, ooo_agent: replacement_1, group: group) }
|
||||||
|
let(:ticket) { create(:ticket, owner: user, group: group, state_name: 'open', pending_time: Time.current) }
|
||||||
|
|
||||||
|
context 'when replacement has access' do
|
||||||
|
let(:replacement_1) { create(:agent, :groupable, group: group) }
|
||||||
|
|
||||||
|
it 'is added to list' do
|
||||||
|
replacements = Set.new
|
||||||
|
|
||||||
|
ooo(notification_instance, user, replacements: replacements)
|
||||||
|
|
||||||
|
expect(replacements).to include replacement_1
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when replacement has replacement' do
|
||||||
|
let(:replacement_1) { create(:agent, :ooo, :groupable, ooo_agent: replacement_2, group: group) }
|
||||||
|
let(:replacement_2) { create(:agent, :groupable, group: group) }
|
||||||
|
|
||||||
|
it 'replacement\'s replacement added to list' do
|
||||||
|
replacements = Set.new
|
||||||
|
|
||||||
|
ooo(notification_instance, user, replacements: replacements)
|
||||||
|
|
||||||
|
expect(replacements).to include replacement_2
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'intermediary replacement is not in list' do
|
||||||
|
replacements = Set.new
|
||||||
|
|
||||||
|
ooo(notification_instance, user, replacements: replacements)
|
||||||
|
|
||||||
|
expect(replacements).not_to include replacement_1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when replacement does not have access' do
|
||||||
|
let(:replacement_1) { create(:agent) }
|
||||||
|
|
||||||
|
it 'is not added to list' do
|
||||||
|
replacements = Set.new
|
||||||
|
|
||||||
|
ooo(notification_instance, user, replacements: replacements)
|
||||||
|
|
||||||
|
expect(replacements).not_to include replacement_1
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when replacement has replacement with access' do
|
||||||
|
let(:replacement_1) { create(:agent, :ooo, ooo_agent: replacement_2) }
|
||||||
|
let(:replacement_2) { create(:agent, :groupable, group: group) }
|
||||||
|
|
||||||
|
it 'his replacement may be added' do
|
||||||
|
replacements = Set.new
|
||||||
|
|
||||||
|
ooo(notification_instance, user, replacements: replacements)
|
||||||
|
|
||||||
|
expect(replacements).to include replacement_2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def run(ticket, user, type)
|
def run(ticket, user, type)
|
||||||
|
build(ticket, user, type).perform
|
||||||
|
end
|
||||||
|
|
||||||
|
def build(ticket, user, type = 'reminder_reached')
|
||||||
described_class.new(
|
described_class.new(
|
||||||
object: ticket.class.name,
|
object: ticket.class.name,
|
||||||
type: type,
|
type: type,
|
||||||
|
@ -40,6 +110,10 @@ RSpec.describe Transaction::Notification, type: :model do
|
||||||
changes: nil,
|
changes: nil,
|
||||||
created_at: Time.current,
|
created_at: Time.current,
|
||||||
user_id: user.id
|
user_id: user.id
|
||||||
).perform
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ooo(instance, user, replacements: Set.new, reasons: [])
|
||||||
|
instance.send(:ooo_replacements, user: user, replacements: replacements, ticket: ticket, reasons: reasons)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -248,6 +248,62 @@ RSpec.describe User, type: :model do
|
||||||
expect(user.out_of_office_agent).to eq(substitute)
|
expect(user.out_of_office_agent).to eq(substitute)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with stack depth exceeding limit' do
|
||||||
|
let(:replacement_chain) do
|
||||||
|
user = create(:agent)
|
||||||
|
|
||||||
|
14
|
||||||
|
.times
|
||||||
|
.each_with_object([user]) do |_, memo|
|
||||||
|
memo << create(:agent, :ooo, ooo_agent: memo.last)
|
||||||
|
end
|
||||||
|
.reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:ids_executed) { [] }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(described_class).to receive(:out_of_office_agent).and_wrap_original do |method, *args| # rubocop:disable RSpec/AnyInstance
|
||||||
|
ids_executed << method.receiver.id
|
||||||
|
method.call(*args)
|
||||||
|
end
|
||||||
|
|
||||||
|
allow(Rails.logger).to receive(:warn)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns the last agent at the limit' do
|
||||||
|
expect(replacement_chain.first.out_of_office_agent).to eq replacement_chain[10]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not evaluate element beyond the limit' do
|
||||||
|
user_beyond_limit = replacement_chain[11]
|
||||||
|
|
||||||
|
replacement_chain.first.out_of_office_agent
|
||||||
|
|
||||||
|
expect(ids_executed).not_to include(user_beyond_limit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does evaluate element within the limit' do
|
||||||
|
user_within_limit = replacement_chain[5]
|
||||||
|
|
||||||
|
replacement_chain.first.out_of_office_agent
|
||||||
|
|
||||||
|
expect(ids_executed).to include(user_within_limit.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'logs error below the limit' do
|
||||||
|
replacement_chain.first.out_of_office_agent
|
||||||
|
|
||||||
|
expect(Rails.logger).to have_received(:warn).with(%r{#{Regexp.escape('Found more than 10 replacement levels for agent')}})
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not logs warn within the limit' do
|
||||||
|
replacement_chain[10].out_of_office_agent
|
||||||
|
|
||||||
|
expect(Rails.logger).not_to have_received(:warn)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue