Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/meteor/app/livechat/server/lib/RoutingManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type Routing = {
agent?: SelectedAgent | null,
options?: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any } },
room?: IOmnichannelRoom,
): Promise<(IOmnichannelRoom & { chatQueued?: boolean }) | null | void>;
): Promise<(IOmnichannelRoom & { chatQueued?: boolean }) | null | false>;
unassignAgent(
inquiry: ILivechatInquiryRecord,
departmentId?: string,
Expand All @@ -62,7 +62,7 @@ type Routing = {
agent: SelectedAgent | null,
options: { clientAction?: boolean; forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any } },
room: IOmnichannelRoom,
): Promise<IOmnichannelRoom | null | void>;
): Promise<IOmnichannelRoom | false>;
transferRoom(room: IOmnichannelRoom, guest: ILivechatVisitor, transferData: TransferData): Promise<boolean>;
delegateAgent(agent: SelectedAgent | undefined, inquiry: ILivechatInquiryRecord): Promise<SelectedAgent | null | undefined>;
removeAllRoomSubscriptions(room: Pick<IOmnichannelRoom, '_id'>, ignoreUser?: { _id: string }): Promise<void>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,22 @@ const handleOnAgentAssignmentFailed = async (
},
) => {
if (!inquiry || !room) {
return;
return false;
}

if (!settings.get('Livechat_waiting_queue')) {
return;
return false;
}

const { forwardingToDepartment: { oldDepartmentId } = {}, forwardingToDepartment } = options;
if (!forwardingToDepartment) {
return;
return false;
}

const { department: newDepartmentId } = inquiry;

if (!newDepartmentId || !oldDepartmentId || newDepartmentId === oldDepartmentId) {
return;
return false;
}

return { ...room, chatQueued: true } as IOmnichannelRoom & { chatQueued: boolean };
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/server/lib/callbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ type ChainedCallbackSignatures = {
};
options: { forwardingToDepartment?: { oldDepartmentId?: string; transferData?: any }; clientAction?: boolean };
},
) => Promise<(IOmnichannelRoom & { chatQueued: boolean }) | undefined>;
) => Promise<(IOmnichannelRoom & { chatQueued: boolean }) | false>;
'beforeGetMentions': (mentionIds: string[], teamMentions: MessageMention[]) => Promise<string[]>;
'livechat.manageDepartmentUnit': (params: { userId: string; departmentId: string; unitId?: string }) => void;
'afterRoomTopicChange': (
Expand Down
7 changes: 6 additions & 1 deletion apps/meteor/server/services/omnichannel/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,12 @@ export class OmnichannelQueue implements IOmnichannelQueue {

const room = await RoutingManager.delegateInquiry(inquiry, defaultAgent, undefined, roomFromDb);

if (room?.servedBy) {
if (!room) {
queueLogger.debug({ msg: 'RoutingManager failed to delegate inquiry', inquiry: inquiry._id, queue });
return false;
}

if (room.servedBy) {
const {
_id: rid,
servedBy: { _id: agentId },
Expand Down
121 changes: 121 additions & 0 deletions apps/meteor/tests/end-to-end/api/livechat/07-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getLivechatRoomInfo,
makeAgentAvailable,
updateDepartment,
startANewLivechatRoomAndTakeIt,
} from '../../../data/livechat/rooms';
import { createAnOnlineAgent, updateLivechatSettingsForUser } from '../../../data/livechat/users';
import { sleep } from '../../../data/livechat/utils';
Expand Down Expand Up @@ -644,6 +645,8 @@ describe('LIVECHAT - Queue', () => {
await makeAgentAvailable(credentials3);
await makeAgentAvailable(credentials4);

await updatePermission('transfer-livechat-guest', ['livechat-agent']);

testUser = {
user,
credentials: credentials3,
Expand Down Expand Up @@ -881,4 +884,122 @@ describe('LIVECHAT - Queue', () => {
expect(roomInfo.servedBy).to.be.an('object');
expect(roomInfo.servedBy?._id).to.be.equal(testUser2.user._id);
});

describe('when forwarding a chat to an agent already at their limit', () => {
let forwardingRoom: { _id: string };
let otherRoom: { _id: string };
let visitor1: ILivechatVisitor;
let visitor2: ILivechatVisitor;
let forwardingDept: ILivechatDepartment;
let forwardUserA: { user: IUser; credentials: Credentials };
let forwardUserB: { user: IUser; credentials: Credentials };

before(async () => {
await updateSetting('Livechat_Routing_Method', 'Manual_Selection');

const userA = await createUser();
const userB = await createUser();

await createAgent(userA.username);
await createAgent(userB.username);

const credA = await login(userA.username, password);
const credB = await login(userB.username, password);

await makeAgentAvailable(credA);
await makeAgentAvailable(credB);

forwardUserA = { user: userA, credentials: credA };
forwardUserB = { user: userB, credentials: credB };

forwardingDept = await createDepartment(
[{ agentId: forwardUserA.user._id }, { agentId: forwardUserB.user._id }],
`${new Date().toISOString()}-forward-limit`,
true,
{
maxNumberSimultaneousChat: 1,
},
);

await updateLivechatSettingsForUser(forwardUserA.user._id, { maxNumberSimultaneousChat: 1 }, [forwardingDept._id]);
await updateLivechatSettingsForUser(forwardUserB.user._id, { maxNumberSimultaneousChat: 1 }, [forwardingDept._id]);

// Force agent A to take a room on the dedicated department
const { room: room1, visitor } = await startANewLivechatRoomAndTakeIt({
departmentId: forwardingDept._id,
agent: forwardUserA.credentials,
});
visitor1 = visitor;

const roomInfo1 = await getLivechatRoomInfo(room1._id);
expect(roomInfo1.servedBy).to.be.an('object');
expect(roomInfo1.servedBy?._id).to.be.equal(forwardUserA.user._id);
otherRoom = room1;
});

before(async () => {
// Force agent B to take another room on the same department
const { room: room2, visitor } = await startANewLivechatRoomAndTakeIt({
departmentId: forwardingDept!._id,
agent: forwardUserB.credentials,
});
visitor2 = visitor;

const roomInfo2 = await getLivechatRoomInfo(room2._id);
expect(roomInfo2.servedBy).to.be.an('object');
expect(roomInfo2.servedBy?._id).to.be.equal(forwardUserB.user._id);

forwardingRoom = room2;
});

after(async () => {
await closeOmnichannelRoom(forwardingRoom._id);
await closeOmnichannelRoom(otherRoom._id);
await deleteDepartment(forwardingDept._id);
await deleteVisitor(visitor1._id);
await deleteVisitor(visitor2._id);
await deleteUser(forwardUserA.user);
await deleteUser(forwardUserB.user);
});

it('should not allow forwarding and should not add system messages to the room', async () => {
const res = await request.post(api('livechat/room.forward')).set(forwardUserB.credentials).send({
roomId: forwardingRoom._id,
userId: forwardUserA.user._id,
clientAction: true,
comment: 'forward to agent at limit',
});

expect(res.status).to.equal(400);
expect(res.body).to.have.property('success', false);

const messagesResponse = await request.get(api('channels.messages')).set(credentials).query({ roomId: forwardingRoom._id });

expect(messagesResponse.status).to.equal(200);
expect(messagesResponse.body).to.have.property('messages');
const systemMessages = messagesResponse.body.messages.filter((msg: any) => msg.t === 'livechat_transfer_history');
expect(systemMessages).to.have.length(0);
});

it('should allow forwarding with the waiting queue disabled', async () => {
await updateEESetting('Livechat_waiting_queue', false);

const res = await request.post(api('livechat/room.forward')).set(forwardUserB.credentials).send({
roomId: forwardingRoom._id,
userId: forwardUserA.user._id,
clientAction: true,
comment: 'forward to agent at limit with waiting queue disabled',
});

expect(res.status).to.equal(200);
expect(res.body).to.have.property('success', true);

const messagesResponse = await request.get(api('channels.messages')).set(credentials).query({ roomId: forwardingRoom._id });

expect(messagesResponse.status).to.equal(200);
expect(messagesResponse.body).to.have.property('messages');
const systemMessages = messagesResponse.body.messages.filter((msg: any) => msg.t === 'livechat_transfer_history');
expect(systemMessages).to.have.length.greaterThan(0);
});
});
});
Loading