diff --git a/apps/meteor/app/lib/server/functions/deleteUser.ts b/apps/meteor/app/lib/server/functions/deleteUser.ts index 703738b6f59..64e35d7231f 100644 --- a/apps/meteor/app/lib/server/functions/deleteUser.ts +++ b/apps/meteor/app/lib/server/functions/deleteUser.ts @@ -1,11 +1,11 @@ import { Meteor } from 'meteor/meteor'; import { TAPi18n } from 'meteor/rocketchat:tap-i18n'; import type { FileProp } from '@rocket.chat/core-typings'; -import { Integrations, FederationServers, LivechatVisitors } from '@rocket.chat/models'; +import { Integrations, FederationServers, LivechatVisitors, LivechatDepartmentAgents } from '@rocket.chat/models'; import { api } from '@rocket.chat/core-services'; import { FileUpload } from '../../../file-upload/server'; -import { Users, Subscriptions, Messages, Rooms, LivechatDepartmentAgents } from '../../../models/server'; +import { Users, Subscriptions, Messages, Rooms } from '../../../models/server'; import { settings } from '../../../settings/server'; import { updateGroupDMsName } from './updateGroupDMsName'; import { relinquishRoomOwnerships } from './relinquishRoomOwnerships'; @@ -56,7 +56,7 @@ export async function deleteUser(userId: string, confirmRelinquish = false): Pro if (user.roles.includes('livechat-agent')) { // Remove user as livechat agent - LivechatDepartmentAgents.removeByAgentId(userId); + await LivechatDepartmentAgents.removeByAgentId(userId); } if (user.roles.includes('livechat-monitor')) { diff --git a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts index 31f920e219d..a0fcc5169da 100644 --- a/apps/meteor/app/lib/server/functions/saveUserIdentity.ts +++ b/apps/meteor/app/lib/server/functions/saveUserIdentity.ts @@ -1,10 +1,10 @@ /* eslint-disable @typescript-eslint/explicit-function-return-type */ import type { IMessage } from '@rocket.chat/core-typings'; -import { VideoConference } from '@rocket.chat/models'; +import { VideoConference, LivechatDepartmentAgents } from '@rocket.chat/models'; import { _setUsername } from './setUsername'; import { _setRealName } from './setRealName'; -import { Messages, Rooms, Subscriptions, LivechatDepartmentAgents, Users } from '../../../models/server'; +import { Messages, Rooms, Subscriptions, Users } from '../../../models/server'; import { FileUpload } from '../../../file-upload/server'; import { updateGroupDMsName } from './updateGroupDMsName'; import { validateName } from './validateName'; @@ -68,7 +68,7 @@ export async function saveUserIdentity({ Rooms.replaceUsernameOfUserByUserId(user._id, username); Subscriptions.setUserUsernameByUserId(user._id, username); - LivechatDepartmentAgents.replaceUsernameOfAgentByUserId(user._id, username); + await LivechatDepartmentAgents.replaceUsernameOfAgentByUserId(user._id, username); const fileStore = FileUpload.getStore('Avatars'); const previousFile = await fileStore.model.findOneByName(previousUsername); diff --git a/apps/meteor/app/livechat/imports/server/rest/departments.ts b/apps/meteor/app/livechat/imports/server/rest/departments.ts index ffb7a342fe5..f752e722e2b 100644 --- a/apps/meteor/app/livechat/imports/server/rest/departments.ts +++ b/apps/meteor/app/livechat/imports/server/rest/departments.ts @@ -125,7 +125,7 @@ API.v1.addRoute( } if (success && agents && permissionToAddAgents) { - success = Livechat.saveDepartmentAgents(_id, { upsert: agents }); + success = await Livechat.saveDepartmentAgents(_id, { upsert: agents }); } if (success) { @@ -280,7 +280,7 @@ API.v1.addRoute( remove: Array, }), ); - Livechat.saveDepartmentAgents(this.urlParams._id, this.bodyParams); + await Livechat.saveDepartmentAgents(this.urlParams._id, this.bodyParams); return API.v1.success(); }, diff --git a/apps/meteor/app/livechat/server/api/v1/agent.ts b/apps/meteor/app/livechat/server/api/v1/agent.ts index 31075d41878..91493d4e0fd 100644 --- a/apps/meteor/app/livechat/server/api/v1/agent.ts +++ b/apps/meteor/app/livechat/server/api/v1/agent.ts @@ -42,7 +42,7 @@ API.v1.addRoute( let { department } = this.queryParams; if (!department) { - const requireDeparment = Livechat.getRequiredDepartment(); + const requireDeparment = await Livechat.getRequiredDepartment(); if (requireDeparment) { department = requireDeparment._id; } diff --git a/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js b/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js index 9b13160afeb..51b2a973354 100644 --- a/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js +++ b/apps/meteor/app/livechat/server/hooks/beforeDelegateAgent.js @@ -1,6 +1,8 @@ +import { LivechatDepartmentAgents } from '@rocket.chat/models'; + import { callbacks } from '../../../../lib/callbacks'; import { settings } from '../../../settings/server'; -import { Users, LivechatDepartmentAgents } from '../../../models/server'; +import { Users } from '../../../models/server'; callbacks.add( 'livechat.beforeDelegateAgent', diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.js index 0081e47c818..47e9c0205fe 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.js @@ -5,18 +5,10 @@ import { LivechatTransferEventType } from '@rocket.chat/apps-engine/definition/l import { OmnichannelSourceType, DEFAULT_SLA_CONFIG } from '@rocket.chat/core-typings'; import { LivechatPriorityWeight } from '@rocket.chat/core-typings/src/ILivechatPriority'; import { api } from '@rocket.chat/core-services'; +import { LivechatDepartmentAgents, Users as UsersRaw } from '@rocket.chat/models'; import { hasRole } from '../../../authorization/server'; -import { - Messages, - LivechatRooms, - Rooms, - Subscriptions, - Users, - LivechatInquiry, - LivechatDepartment, - LivechatDepartmentAgents, -} from '../../../models/server'; +import { Messages, LivechatRooms, Rooms, Subscriptions, Users, LivechatInquiry, LivechatDepartment } from '../../../models/server'; import { Livechat } from './Livechat'; import { RoutingManager } from './RoutingManager'; import { callbacks } from '../../../../lib/callbacks'; @@ -276,7 +268,7 @@ export const dispatchAgentDelegated = (rid, agentId) => { }); }; -export const dispatchInquiryQueued = (inquiry, agent) => { +export const dispatchInquiryQueued = async (inquiry, agent) => { if (!inquiry?._id) { return; } @@ -295,7 +287,7 @@ export const dispatchInquiryQueued = (inquiry, agent) => { } // Alert only the online agents of the queued request - const onlineAgents = Livechat.getOnlineAgents(department, agent); + const onlineAgents = await Livechat.getOnlineAgents(department, agent); if (!onlineAgents) { logger.debug('Cannot notify agents of queued inquiry. No online agents found'); return; @@ -304,12 +296,12 @@ export const dispatchInquiryQueued = (inquiry, agent) => { logger.debug(`Notifying ${onlineAgents.count()} agents of new inquiry`); const notificationUserName = v && (v.name || v.username); - onlineAgents.forEach((agent) => { + for await (let agent of onlineAgents) { if (agent.agentId) { - agent = Users.findOneById(agent.agentId); + agent = await UsersRaw.findOneById(agent.agentId); } const { _id, active, emails, language, status, statusConnection, username } = agent; - sendNotification({ + await sendNotification({ // fake a subscription in order to make use of the function defined above subscription: { rid, @@ -337,7 +329,7 @@ export const dispatchInquiryQueued = (inquiry, agent) => { room: Object.assign(room, { name: TAPi18n.__('New_chat_in_queue', {}, language) }), mentionIds: [], }); - }); + } }; export const forwardRoomToAgent = async (room, transferData) => { @@ -456,7 +448,7 @@ export const forwardRoomToDepartment = async (room, guest, transferData) => { if (!user) { throw new Error('error-user-is-offline'); } - user = LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId); + user = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(agentId, departmentId); if (!user) { throw new Error('error-user-not-belong-to-department'); } @@ -550,7 +542,7 @@ export const checkServiceStatus = ({ guest, agent }) => { return users && users.count() > 0; }; -export const updateDepartmentAgents = (departmentId, agents, departmentEnabled) => { +export const updateDepartmentAgents = async (departmentId, agents, departmentEnabled) => { check(departmentId, String); check( agents, @@ -563,22 +555,22 @@ export const updateDepartmentAgents = (departmentId, agents, departmentEnabled) const { upsert = [], remove = [] } = agents; const agentsRemoved = []; const agentsAdded = []; - remove.forEach(({ agentId }) => { - LivechatDepartmentAgents.removeByDepartmentIdAndAgentId(departmentId, agentId); + for await (const { agentId } of remove) { + await LivechatDepartmentAgents.removeByDepartmentIdAndAgentId(departmentId, agentId); agentsRemoved.push(agentId); - }); + } if (agentsRemoved.length > 0) { callbacks.runAsync('livechat.removeAgentDepartment', { departmentId, agentsId: agentsRemoved }); } - upsert.forEach((agent) => { + for await (const agent of upsert) { const agentFromDb = Users.findOneById(agent.agentId, { fields: { _id: 1, username: 1 } }); if (!agentFromDb) { - return; + continue; } - LivechatDepartmentAgents.saveAgent({ + await LivechatDepartmentAgents.saveAgent({ agentId: agent.agentId, departmentId, username: agentFromDb.username, @@ -587,7 +579,7 @@ export const updateDepartmentAgents = (departmentId, agents, departmentEnabled) departmentEnabled, }); agentsAdded.push(agent.agentId); - }); + } if (agentsAdded.length > 0) { callbacks.runAsync('livechat.saveAgentDepartment', { @@ -598,7 +590,7 @@ export const updateDepartmentAgents = (departmentId, agents, departmentEnabled) if (agentsRemoved.length > 0 || agentsAdded.length > 0) { const numAgents = LivechatDepartmentAgents.find({ departmentId }).count(); - LivechatDepartment.updateNumAgentsById(departmentId, numAgents); + await LivechatDepartment.updateNumAgentsById(departmentId, numAgents); } return true; diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index f7c098acfe5..15fdf088532 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -19,6 +19,7 @@ import { Subscriptions as SubscriptionsRaw, Messages as MessagesRaw, LivechatDepartment as LivechatDepartmentRaw, + LivechatDepartmentAgents, } from '@rocket.chat/models'; import { VideoConf, api } from '@rocket.chat/core-services'; @@ -27,16 +28,7 @@ import { RoutingManager } from './RoutingManager'; import { Analytics } from './Analytics'; import { settings } from '../../../settings/server'; import { callbacks } from '../../../../lib/callbacks'; -import { - Users, - LivechatRooms, - Messages, - Subscriptions, - Rooms, - LivechatDepartmentAgents, - LivechatDepartment, - LivechatInquiry, -} from '../../../models/server'; +import { Users, LivechatRooms, Messages, Subscriptions, Rooms, LivechatDepartment, LivechatInquiry } from '../../../models/server'; import { Logger } from '../../../logger/server'; import { hasPermission, hasRole, canAccessRoomAsync, roomAccessAttributes } from '../../../authorization/server'; import * as Mailer from '../../../mailer/server/api'; @@ -109,7 +101,7 @@ export const Livechat = { return Users.findAgents(); }, - getOnlineAgents(department, agent) { + async getOnlineAgents(department, agent) { if (agent?.agentId) { return Users.findOnlineAgents(agent.agentId); } @@ -120,13 +112,13 @@ export const Livechat = { return Users.findOnlineAgents(); }, - checkOnlineAgents(department, agent, skipFallbackCheck = false) { + async checkOnlineAgents(department, agent, skipFallbackCheck = false) { if (agent?.agentId) { return Users.checkOnlineAgents(agent.agentId); } if (department) { - const onlineForDep = LivechatDepartmentAgents.checkOnlineForDepartment(department); + const onlineForDep = await LivechatDepartmentAgents.checkOnlineForDepartment(department); if (onlineForDep || skipFallbackCheck) { return onlineForDep; } @@ -142,7 +134,7 @@ export const Livechat = { return Users.checkOnlineAgents(); }, - getBotAgents(department) { + async getBotAgents(department) { if (department) { return LivechatDepartmentAgents.getBotsForDepartment(department); } @@ -150,19 +142,22 @@ export const Livechat = { return Users.findBotAgents(); }, - getRequiredDepartment(onlineRequired = true) { + async getRequiredDepartment(onlineRequired = true) { const departments = LivechatDepartment.findEnabledWithAgents(); - return departments.fetch().find((dept) => { + for await (const dept of departments.fetch()) { if (!dept.showOnRegistration) { - return false; + continue; } if (!onlineRequired) { - return true; + return dept; } - const onlineAgents = LivechatDepartmentAgents.getOnlineForDepartment(dept._id); - return onlineAgents && onlineAgents.count() > 0; - }); + + const onlineAgents = await LivechatDepartmentAgents.getOnlineForDepartment(dept._id); + if (onlineAgents && onlineAgents.length) { + return dept; + } + } }, async getRoom(guest, message, roomInfo, agent, extraData) { @@ -188,7 +183,7 @@ export const Livechat = { const defaultAgent = callbacks.run('livechat.checkDefaultAgentOnNewRoom', agent, guest); // if no department selected verify if there is at least one active and pick the first if (!defaultAgent && !guest.department) { - const department = this.getRequiredDepartment(); + const department = await this.getRequiredDepartment(); Livechat.logger.debug(`No department or default agent selected for ${guest._id}`); if (department) { @@ -721,7 +716,7 @@ export const Livechat = { return RoutingManager.transferRoom(room, guest, transferData); }, - returnRoomAsInquiry(rid, departmentId, overrideTransferData = {}) { + async returnRoomAsInquiry(rid, departmentId, overrideTransferData = {}) { Livechat.logger.debug(`Transfering room ${rid} to ${departmentId ? 'department' : ''} queue`); const room = LivechatRooms.findOneById(rid); if (!room) { @@ -764,7 +759,7 @@ export const Livechat = { const transferData = { roomId: rid, scope: 'queue', departmentId, transferredBy, ...overrideTransferData }; try { this.saveTransferHistory(room, transferData); - RoutingManager.unassignAgent(inquiry, departmentId); + await RoutingManager.unassignAgent(inquiry, departmentId); } catch (e) { this.logger.error(e); throw new Meteor.Error('error-returning-inquiry', 'Error returning inquiry to the queue', { @@ -915,8 +910,12 @@ export const Livechat = { Users.setOperator(_id, false); Users.removeLivechatData(_id); this.setUserStatusLivechat(_id, 'not-available'); - LivechatDepartmentAgents.removeByAgentId(_id); - Promise.await(Promise.all([LivechatVisitors.removeContactManagerByUsername(username), UsersRaw.unsetExtension(_id)])); + + await Promise.all([ + LivechatDepartmentAgents.removeByAgentId(_id), + LivechatVisitors.removeContactManagerByUsername(username), + UsersRaw.unsetExtension(_id), + ]); return true; } @@ -983,7 +982,7 @@ export const Livechat = { LivechatInquiry.removeByVisitorToken(token); }, - saveDepartmentAgents(_id, departmentAgents) { + async saveDepartmentAgents(_id, departmentAgents) { check(_id, String); check(departmentAgents, { upsert: Match.Maybe([ @@ -1032,7 +1031,10 @@ export const Livechat = { return true; }, - removeDepartment(_id) { + /* + * @deprecated - Use the equivalent from DepartmentHelpers class + */ + async removeDepartment(_id) { check(_id, String); const departmentRemovalEnabled = settings.get('Omnichannel_enable_department_removal'); @@ -1051,10 +1053,8 @@ export const Livechat = { }); } const ret = LivechatDepartment.removeById(_id); - const agentsIds = LivechatDepartmentAgents.findByDepartmentId(_id) - .fetch() - .map((agent) => agent.agentId); - LivechatDepartmentAgents.removeByDepartmentId(_id); + const agentsIds = (await LivechatDepartmentAgents.findByDepartmentId(_id).toArray()).map((agent) => agent.agentId); + await LivechatDepartmentAgents.removeByDepartmentId(_id); LivechatDepartment.unsetFallbackDepartmentByDepartmentId(_id); if (ret) { Meteor.defer(() => { diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.js b/apps/meteor/app/livechat/server/lib/QueueManager.js index c42e67adc78..855f1a600e1 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.js +++ b/apps/meteor/app/livechat/server/lib/QueueManager.js @@ -16,7 +16,7 @@ export const saveQueueInquiry = (inquiry) => { }; export const queueInquiry = async (room, inquiry, defaultAgent) => { - const inquiryAgent = RoutingManager.delegateAgent(defaultAgent, inquiry); + const inquiryAgent = await RoutingManager.delegateAgent(defaultAgent, inquiry); logger.debug(`Delegating inquiry with id ${inquiry._id} to agent ${defaultAgent?.username}`); await callbacks.run('livechat.beforeRouteChat', inquiry, inquiryAgent); diff --git a/apps/meteor/app/livechat/server/lib/RoutingManager.js b/apps/meteor/app/livechat/server/lib/RoutingManager.js index be5b01152e8..8da855edd94 100644 --- a/apps/meteor/app/livechat/server/lib/RoutingManager.js +++ b/apps/meteor/app/livechat/server/lib/RoutingManager.js @@ -113,7 +113,7 @@ export const RoutingManager = { return inquiry; }, - unassignAgent(inquiry, departmentId) { + async unassignAgent(inquiry, departmentId) { const { rid, department } = inquiry; const room = LivechatRooms.findOneById(rid); @@ -143,7 +143,7 @@ export const RoutingManager = { dispatchAgentDelegated(rid, null); } - dispatchInquiryQueued(inquiry); + await dispatchInquiryQueued(inquiry); return true; }, @@ -222,7 +222,7 @@ export const RoutingManager = { return false; }, - delegateAgent(agent, inquiry) { + async delegateAgent(agent, inquiry) { logger.debug(`Delegating Inquiry ${inquiry._id}`); const defaultAgent = callbacks.run('livechat.beforeDelegateAgent', agent, { department: inquiry?.department, @@ -234,7 +234,7 @@ export const RoutingManager = { } logger.debug(`Queueing inquiry ${inquiry._id}`); - dispatchInquiryQueued(inquiry, defaultAgent); + await dispatchInquiryQueued(inquiry, defaultAgent); return defaultAgent; }, diff --git a/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts b/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts index 7f5c9fd860c..e1c7b0b739f 100644 --- a/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts +++ b/apps/meteor/app/livechat/server/lib/routing/AutoSelection.ts @@ -1,8 +1,10 @@ import type { IRoutingMethod, RoutingMethodConfig, SelectedAgent } from '@rocket.chat/core-typings'; +import { LivechatDepartmentAgents } from '@rocket.chat/models'; import { RoutingManager } from '../RoutingManager'; -import { LivechatDepartmentAgents, Users } from '../../../../models/server'; +import { Users } from '../../../../models/server'; import { callbacks } from '../../../../../lib/callbacks'; +import { settings } from '../../../../settings/server'; /* Auto Selection Queuing method: * @@ -24,12 +26,17 @@ class AutoSelection implements IRoutingMethod { }; } - getNextAgent(department?: string, ignoreAgentId?: string): Promise { + async getNextAgent(department?: string, ignoreAgentId?: string): Promise { const extraQuery = callbacks.run('livechat.applySimultaneousChatRestrictions', undefined, { ...(department ? { departmentId: department } : {}), }); if (department) { - return Promise.resolve(LivechatDepartmentAgents.getNextAgentForDepartment(department, ignoreAgentId, extraQuery)); + return LivechatDepartmentAgents.getNextAgentForDepartment( + department, + settings.get('Livechat_enabled_when_agent_idle'), + ignoreAgentId, + extraQuery, + ); } return Users.getNextAgent(ignoreAgentId, extraQuery); diff --git a/apps/meteor/app/livechat/server/methods/getNextAgent.ts b/apps/meteor/app/livechat/server/methods/getNextAgent.ts index 0ed3b82c845..1b55088664c 100644 --- a/apps/meteor/app/livechat/server/methods/getNextAgent.ts +++ b/apps/meteor/app/livechat/server/methods/getNextAgent.ts @@ -25,7 +25,7 @@ Meteor.methods({ } if (!department) { - const requireDeparment = Livechat.getRequiredDepartment(); + const requireDeparment = await Livechat.getRequiredDepartment(); if (requireDeparment) { department = requireDeparment._id; } diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts index 7b1ce2c358b..b7b5758beb3 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.compatibility.ts @@ -1,7 +1,8 @@ -import type { IUser, ILivechatDepartment, ILivechatDepartmentAgents, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import type { IUser, ILivechatDepartment, IOmnichannelRoom } from '@rocket.chat/core-typings'; +import { LivechatDepartmentAgents } from '@rocket.chat/models'; import { hasPermission, hasRole } from '../../authorization/server'; -import { LivechatDepartment, LivechatDepartmentAgents, LivechatInquiry, LivechatRooms } from '../../models/server'; +import { LivechatDepartment, LivechatInquiry, LivechatRooms } from '../../models/server'; import { RoutingManager } from './lib/RoutingManager'; type OmniRoomAccessValidator = (room: IOmnichannelRoom, user?: Pick, extraData?: Record) => boolean; @@ -28,7 +29,7 @@ export const validators: OmniRoomAccessValidator[] = [ } return extraData?.visitorToken && room.v && room.v.token === extraData.visitorToken; }, - function (room, user) { + async function (room, user) { if (!user?._id) { return false; } @@ -39,9 +40,7 @@ export const validators: OmniRoomAccessValidator[] = [ let departmentIds; if (!hasRole(user._id, 'livechat-manager')) { - const departmentAgents = LivechatDepartmentAgents.findByAgentId(user._id) - .fetch() - .map((d: ILivechatDepartmentAgents) => d.departmentId); + const departmentAgents = (await LivechatDepartmentAgents.findByAgentId(user._id).toArray()).map((d) => d.departmentId); departmentIds = LivechatDepartment.find({ _id: { $in: departmentAgents }, enabled: true }) .fetch() .map((d: ILivechatDepartment) => d._id); @@ -65,11 +64,11 @@ export const validators: OmniRoomAccessValidator[] = [ const inquiry = LivechatInquiry.findOne(filter, { fields: { status: 1 } }); return inquiry && inquiry.status === 'queued'; }, - function (room, user) { + async function (room, user) { if (!room.departmentId || room.open || !user?._id) { return; } - const agentOfDepartment = LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId); + const agentOfDepartment = await LivechatDepartmentAgents.findOneByAgentIdAndDepartmentId(user._id, room.departmentId); if (!agentOfDepartment) { return; } diff --git a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts index 381a40d371f..bac9cb5bd7a 100644 --- a/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts +++ b/apps/meteor/app/livechat/server/roomAccessValidator.internalService.ts @@ -10,8 +10,8 @@ export class AuthorizationLivechat extends ServiceClassInternal implements IAuth protected internal = true; async canAccessRoom(room: IOmnichannelRoom, user?: Pick, extraData?: object): Promise { - for (const validator of validators) { - if (validator(room, user, extraData)) { + for await (const validator of validators) { + if (await validator(room, user, extraData)) { return true; } } diff --git a/apps/meteor/app/models/server/index.ts b/apps/meteor/app/models/server/index.ts index 0d4878333eb..e1710d6b956 100644 --- a/apps/meteor/app/models/server/index.ts +++ b/apps/meteor/app/models/server/index.ts @@ -5,7 +5,6 @@ import Rooms from './models/Rooms'; import Subscriptions from './models/Subscriptions'; import Users from './models/Users'; import LivechatDepartment from './models/LivechatDepartment'; -import LivechatDepartmentAgents from './models/LivechatDepartmentAgents'; import LivechatRooms from './models/LivechatRooms'; import LivechatInquiry from './models/LivechatInquiry'; import AppsModel from './models/apps-model'; @@ -13,16 +12,4 @@ import AppsModel from './models/apps-model'; export { AppsLogsModel } from './models/apps-logs-model'; export { AppsPersistenceModel } from './models/apps-persistence-model'; -export { - AppsModel, - Base, - BaseDb, - Messages, - Rooms, - Subscriptions, - Users, - LivechatDepartment, - LivechatDepartmentAgents, - LivechatRooms, - LivechatInquiry, -}; +export { AppsModel, Base, BaseDb, Messages, Rooms, Subscriptions, Users, LivechatDepartment, LivechatRooms, LivechatInquiry }; diff --git a/apps/meteor/app/models/server/models/LivechatDepartment.js b/apps/meteor/app/models/server/models/LivechatDepartment.js index 9fb5279dd87..2a315291d49 100644 --- a/apps/meteor/app/models/server/models/LivechatDepartment.js +++ b/apps/meteor/app/models/server/models/LivechatDepartment.js @@ -1,10 +1,11 @@ import _ from 'underscore'; +import { LivechatDepartmentAgents } from '@rocket.chat/models'; import { Base } from './_Base'; -import LivechatDepartmentAgents from './LivechatDepartmentAgents'; /** * Livechat Department model */ +// Promise.await added here will be removed when this model gets removed, dont panic :) export class LivechatDepartment extends Base { constructor(modelOrName) { super(modelOrName || 'livechat_department'); @@ -48,21 +49,19 @@ export class LivechatDepartment extends Base { _id = this.insert(record); } if (oldData && oldData.enabled !== data.enabled) { - LivechatDepartmentAgents.setDepartmentEnabledByDepartmentId(_id, data.enabled); + Promise.await(LivechatDepartmentAgents.setDepartmentEnabledByDepartmentId(_id, data.enabled)); } return _.extend(record, { _id }); } saveDepartmentsByAgent(agent, departments = []) { const { _id: agentId, username } = agent; - const savedDepartments = LivechatDepartmentAgents.findByAgentId(agentId) - .fetch() - .map((d) => d.departmentId); + const savedDepartments = Promise.await(LivechatDepartmentAgents.findByAgentId(agentId).toArray()).map((d) => d.departmentId); const incNumAgents = (_id, numAgents) => this.update(_id, { $inc: { numAgents } }); // remove other departments _.difference(savedDepartments, departments).forEach((departmentId) => { - LivechatDepartmentAgents.removeByDepartmentIdAndAgentId(departmentId, agentId); + Promise.await(LivechatDepartmentAgents.removeByDepartmentIdAndAgentId(departmentId, agentId)); incNumAgents(departmentId, -1); }); @@ -70,14 +69,16 @@ export class LivechatDepartment extends Base { const { enabled: departmentEnabled } = this.findOneById(departmentId, { fields: { enabled: 1 }, }); - const saveResult = LivechatDepartmentAgents.saveAgent({ - agentId, - departmentId, - username, - departmentEnabled, - count: 0, - order: 0, - }); + const saveResult = Promise.await( + LivechatDepartmentAgents.saveAgent({ + agentId, + departmentId, + username, + departmentEnabled, + count: 0, + order: 0, + }), + ); if (saveResult.insertedId) { incNumAgents(departmentId, 1); diff --git a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js b/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js deleted file mode 100644 index c76542f7ac1..00000000000 --- a/apps/meteor/app/models/server/models/LivechatDepartmentAgents.js +++ /dev/null @@ -1,231 +0,0 @@ -import _ from 'underscore'; - -import { Base } from './_Base'; -import Users from './Users'; -/** - * Livechat Department model - */ -class LivechatDepartmentAgents extends Base { - constructor() { - super('livechat_department_agents'); - - this.tryEnsureIndex({ departmentId: 1 }); - this.tryEnsureIndex({ departmentEnabled: 1 }); - this.tryEnsureIndex({ agentId: 1 }); - this.tryEnsureIndex({ username: 1 }); - } - - findByDepartmentId(departmentId) { - return this.find({ departmentId }); - } - - findByAgentId(agentId) { - return this.find({ agentId }); - } - - findOneByAgentIdAndDepartmentId(agentId, departmentId) { - return this.findOne({ agentId, departmentId }); - } - - saveAgent(agent) { - return this.upsert( - { - agentId: agent.agentId, - departmentId: agent.departmentId, - }, - { - $set: { - username: agent.username, - departmentEnabled: agent.departmentEnabled, - count: parseInt(agent.count), - order: parseInt(agent.order), - }, - }, - ); - } - - removeByAgentId(agentId) { - this.remove({ agentId }); - } - - removeByDepartmentIdAndAgentId(departmentId, agentId) { - this.remove({ departmentId, agentId }); - } - - getNextAgentForDepartment(departmentId, ignoreAgentId, extraQuery) { - const agents = this.findByDepartmentId(departmentId).fetch(); - - if (agents.length === 0) { - return; - } - - const onlineUsers = Users.findOnlineUserFromList(_.pluck(agents, 'username')); - - const onlineUsernames = _.pluck(onlineUsers.fetch(), 'username'); - - // get fully booked agents, to ignore them from the query - const currentUnavailableAgents = Promise.await(Users.getUnavailableAgents(departmentId, extraQuery)).map((u) => u.username); - - const query = { - departmentId, - username: { - $in: onlineUsernames, - $nin: currentUnavailableAgents, - }, - ...(ignoreAgentId && { agentId: { $ne: ignoreAgentId } }), - }; - - const sort = { - count: 1, - order: 1, - username: 1, - }; - const update = { - $inc: { - count: 1, - }, - }; - - const collectionObj = this.model.rawCollection(); - - const agent = Promise.await(collectionObj.findOneAndUpdate(query, update, { sort, returnNewDocument: 'after' })); - if (agent && agent.value) { - return { - agentId: agent.value.agentId, - username: agent.value.username, - }; - } - return null; - } - - checkOnlineForDepartment(departmentId) { - const agents = this.findByDepartmentId(departmentId).fetch(); - - if (agents.length === 0) { - return false; - } - - const onlineUser = Users.findOneOnlineAgentByUserList(_.pluck(agents, 'username')); - - return Boolean(onlineUser); - } - - getOnlineForDepartment(departmentId) { - const agents = this.findByDepartmentId(departmentId).fetch(); - - if (agents.length === 0) { - return; - } - - const onlineUsers = Users.findOnlineUserFromList(_.pluck(agents, 'username')); - - const onlineUsernames = _.pluck(onlineUsers.fetch(), 'username'); - - const query = { - departmentId, - username: { - $in: onlineUsernames, - }, - }; - - return this.find(query); - } - - getBotsForDepartment(departmentId) { - const agents = this.findByDepartmentId(departmentId).fetch(); - - if (agents.length === 0) { - return; - } - - const botUsers = Users.findBotAgents(_.pluck(agents, 'username')); - const botUsernames = _.pluck(botUsers.fetch(), 'username'); - - const query = { - departmentId, - username: { - $in: botUsernames, - }, - }; - - return this.find(query); - } - - async getNextBotForDepartment(departmentId, ignoreAgentId) { - const agents = this.findByDepartmentId(departmentId).fetch(); - - if (agents.length === 0) { - return; - } - - const botUsers = Users.findBotAgents(_.pluck(agents, 'username')); - const botUsernames = _.pluck(botUsers.fetch(), 'username'); - - const query = { - departmentId, - username: { - $in: botUsernames, - }, - ...(ignoreAgentId && { agentId: { $ne: ignoreAgentId } }), - }; - - const sort = { - count: 1, - order: 1, - username: 1, - }; - const update = { - $inc: { - count: 1, - }, - }; - - const bot = await this.model.rawCollection().findOneAndUpdate(query, update, { sort, returnNewDocument: 'after' }); - if (bot && bot.value) { - return { - agentId: bot.value.agentId, - username: bot.value.username, - }; - } - return null; - } - - findUsersInQueue(usersList) { - const query = {}; - - if (!_.isEmpty(usersList)) { - query.username = { - $in: usersList, - }; - } - - const options = { - sort: { - departmentId: 1, - count: 1, - order: 1, - username: 1, - }, - }; - - return this.find(query, options); - } - - replaceUsernameOfAgentByUserId(userId, username) { - const query = { agentId: userId }; - - const update = { - $set: { - username, - }, - }; - - return this.update(query, update, { multi: true }); - } - - setDepartmentEnabledByDepartmentId(departmentId, departmentEnabled) { - return this.update({ departmentId }, { $set: { departmentEnabled } }, { multi: true }); - } -} - -export default new LivechatDepartmentAgents(); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts index 63f41447a6d..2c0db1516f8 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts +++ b/apps/meteor/ee/app/livechat-enterprise/server/business-hour/Multiple.ts @@ -231,6 +231,7 @@ export class MultipleBusinessHoursBehavior extends AbstractBusinessHourBehavior if ((await LivechatDepartmentAgents.findByAgentId(agentId).count()) === 0) { agentIdsWithoutDepartment.push(agentId); } + // TODO: We're doing a full fledged aggregation with lookups and getting the whole array just for getting the length? :( if (!(await LivechatDepartmentAgents.findAgentsByAgentIdAndBusinessHourId(agentId, department.businessHourId)).length) { // eslint-disable-line no-await-in-loop agentIdsToRemoveCurrentBusinessHour.push(agentId); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js index 4a6a91fd37f..b7ff5dbff8c 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/LivechatEnterprise.js @@ -287,7 +287,7 @@ export const LivechatEnterprise = { const departmentDB = await LivechatDepartmentRaw.createOrUpdateDepartment(_id, departmentData); if (departmentDB && departmentAgents) { - updateDepartmentAgents(departmentDB._id, departmentAgents, departmentDB.enabled); + await updateDepartmentAgents(departmentDB._id, departmentAgents, departmentDB.enabled); } return departmentDB; diff --git a/apps/meteor/ee/app/models/server/raw/LivechatDepartmentAgents.ts b/apps/meteor/ee/app/models/server/raw/LivechatDepartmentAgents.ts index 11bf2a07fbb..5529dcb9fe7 100644 --- a/apps/meteor/ee/app/models/server/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/ee/app/models/server/raw/LivechatDepartmentAgents.ts @@ -1,8 +1,12 @@ +import type { ILivechatDepartmentAgents } from '@rocket.chat/core-typings'; +import { registerModel } from '@rocket.chat/models'; + +import { trashCollection } from '../../../../../server/database/trash'; +import { db } from '../../../../../server/database/utils'; import { LivechatDepartmentAgentsRaw } from '../../../../../server/models/raw/LivechatDepartmentAgents'; -import { overwriteClassOnLicense } from '../../../license/server'; -overwriteClassOnLicense('livechat-enterprise', LivechatDepartmentAgentsRaw, { - findAgentsByAgentIdAndBusinessHourId(agentId: string, businessHourId: string): Promise> { +class LivechatDepartmentAgents extends LivechatDepartmentAgentsRaw { + findAgentsByAgentIdAndBusinessHourId(agentId: string, businessHourId: string): Promise { const match = { $match: { agentId }, }; @@ -22,7 +26,8 @@ overwriteClassOnLicense('livechat-enterprise', LivechatDepartmentAgentsRaw, { }; const withBusinessHourId = { $match: { 'departments.businessHourId': businessHourId } }; const project = { $project: { departments: 0 } }; - const model = this as unknown as LivechatDepartmentAgentsRaw; - return model.col.aggregate([match, lookup, unwind, withBusinessHourId, project]).toArray(); - }, -}); + return this.col.aggregate([match, lookup, unwind, withBusinessHourId, project]).toArray(); + } +} + +registerModel('ILivechatDepartmentAgents', new LivechatDepartmentAgents(db, trashCollection)); diff --git a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts index 306f0096969..73f1143e665 100644 --- a/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts +++ b/apps/meteor/server/models/raw/LivechatDepartmentAgents.ts @@ -139,7 +139,7 @@ export class LivechatDepartmentAgentsRaw extends BaseRaw { return []; } diff --git a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts index c335afd82ff..b59b8464d57 100644 --- a/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts +++ b/packages/model-typings/src/models/ILivechatDepartmentAgentsModel.ts @@ -57,7 +57,7 @@ export interface ILivechatDepartmentAgentsModel extends IBaseModel | FindCursor

; findByDepartmentIds(departmentIds: string[], options?: Record): FindCursor; - findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): []; + findAgentsByAgentIdAndBusinessHourId(_agentId: string, _businessHourId: string): Promise; setDepartmentEnabledByDepartmentId(departmentId: string, departmentEnabled: boolean): Promise; removeByDepartmentId(departmentId: string): Promise; findByDepartmentId(departmentId: string): FindCursor;