From 9dce0fed7f540064898716998d9c2042113150ca Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Sun, 28 Aug 2022 17:54:36 -0600 Subject: [PATCH] [FIX][ENTERPRISE] Omnichannel real time data on micro services (#26703) Co-authored-by: Diego Sampaio --- apps/meteor/app/livechat/server/lib/Helper.js | 4 +-- .../app/livechat/server/lib/Livechat.js | 6 ++-- .../livechat-enterprise/server/lib/Helper.js | 4 +-- .../ee/server/lib/registerServiceModels.ts | 32 ++++++++++++++----- .../server/services/authorization/service.ts | 10 ++++-- apps/meteor/ee/server/services/mongo.ts | 3 +- apps/meteor/ee/server/services/package.json | 2 +- .../ee/server/services/stream-hub/service.ts | 8 +++-- .../modules/listeners/listeners.module.ts | 3 ++ apps/meteor/server/sdk/lib/Events.ts | 4 +++ ee/apps/ddp-streamer/src/streams.ts | 10 ++++-- 11 files changed, 61 insertions(+), 25 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/Helper.js b/apps/meteor/app/livechat/server/lib/Helper.js index 810fa421a5c01..df8d2bd4fa17e 100644 --- a/apps/meteor/app/livechat/server/lib/Helper.js +++ b/apps/meteor/app/livechat/server/lib/Helper.js @@ -21,11 +21,11 @@ import { callbacks } from '../../../../lib/callbacks'; import { Logger } from '../../../logger'; import { settings } from '../../../settings/server'; import { Apps, AppEvents } from '../../../apps/server'; -import notifications from '../../../notifications/server/lib/Notifications'; import { sendNotification } from '../../../lib/server'; import { sendMessage } from '../../../lib/server/functions/sendMessage'; import { queueInquiry, saveQueueInquiry } from './QueueManager'; import { validateEmail as validatorFunc } from '../../../../lib/emailValidator'; +import { api } from '../../../../server/sdk/api'; const logger = new Logger('LivechatHelper'); @@ -268,7 +268,7 @@ export const normalizeAgent = (agentId) => { export const dispatchAgentDelegated = (rid, agentId) => { const agent = normalizeAgent(agentId); - notifications.streamLivechatRoom.emit(rid, { + api.broadcast('omnichannel.room', rid, { type: 'agentData', data: agent, }); diff --git a/apps/meteor/app/livechat/server/lib/Livechat.js b/apps/meteor/app/livechat/server/lib/Livechat.js index 597af49b80264..6bb862ba4500c 100644 --- a/apps/meteor/app/livechat/server/lib/Livechat.js +++ b/apps/meteor/app/livechat/server/lib/Livechat.js @@ -39,10 +39,10 @@ import { FileUpload } from '../../../file-upload/server'; import { normalizeTransferredByData, parseAgentCustomFields, updateDepartmentAgents, validateEmail } from './Helper'; import { Apps, AppEvents } from '../../../apps/server'; import { businessHourManager } from '../business-hour'; -import notifications from '../../../notifications/server/lib/Notifications'; import { addUserRoles } from '../../../../server/lib/roles/addUserRoles'; import { removeUserFromRoles } from '../../../../server/lib/roles/removeUserFromRoles'; import { VideoConf } from '../../../../server/sdk'; +import { api } from '../../../../server/sdk/api'; const logger = new Logger('Livechat'); @@ -1358,7 +1358,7 @@ export const Livechat = { } LivechatRooms.findOpenByAgent(userId).forEach((room) => { - notifications.streamLivechatRoom.emit(room._id, { + api.broadcast('omnichannel.room', room._id, { type: 'agentStatus', status, }); @@ -1374,7 +1374,7 @@ export const Livechat = { }, notifyRoomVisitorChange(roomId, visitor) { - notifications.streamLivechatRoom.emit(roomId, { + api.broadcast('omnichannel.room', roomId, { type: 'visitorData', visitor, }); diff --git a/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.js b/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.js index b0cde9eb53f4f..943a5484f279a 100644 --- a/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.js +++ b/apps/meteor/ee/app/livechat-enterprise/server/lib/Helper.js @@ -7,9 +7,9 @@ import { Users, LivechatInquiry, LivechatRooms, Messages, LivechatCustomField } import { settings } from '../../../../../app/settings/server'; import { RoutingManager } from '../../../../../app/livechat/server/lib/RoutingManager'; import { dispatchAgentDelegated } from '../../../../../app/livechat/server/lib/Helper'; -import notifications from '../../../../../app/notifications/server/lib/Notifications'; import { logger, helperLogger } from './logger'; import { OmnichannelQueueInactivityMonitor } from './QueueInactivityMonitor'; +import { api } from '../../../../../server/sdk/api'; export const getMaxNumberSimultaneousChat = async ({ agentId, departmentId }) => { if (departmentId) { @@ -76,7 +76,7 @@ export const dispatchInquiryPosition = async (inquiry, queueInfo) => { const { position, department } = inquiry; const data = await normalizeQueueInfo({ position, queueInfo, department }); const propagateInquiryPosition = Meteor.bindEnvironment((inquiry) => { - notifications.streamLivechatRoom.emit(inquiry.rid, { + api.broadcast('omnichannel.room', inquiry.rid, { type: 'queueData', data, }); diff --git a/apps/meteor/ee/server/lib/registerServiceModels.ts b/apps/meteor/ee/server/lib/registerServiceModels.ts index 3ac218897b30e..3d75c96b92960 100644 --- a/apps/meteor/ee/server/lib/registerServiceModels.ts +++ b/apps/meteor/ee/server/lib/registerServiceModels.ts @@ -1,4 +1,11 @@ -import type { Db } from 'mongodb'; +import type { Collection, Db } from 'mongodb'; +import type { + ILivechatDepartmentAgents, + ILivechatInquiryRecord, + ISetting, + ISubscription, + RocketChatRecordDeleted, +} from '@rocket.chat/core-typings'; import { registerModel } from '@rocket.chat/models'; import { RolesRaw } from '../../../server/models/raw/Roles'; @@ -20,12 +27,15 @@ import { IntegrationsRaw } from '../../../server/models/raw/Integrations'; import { EmailInboxRaw } from '../../../server/models/raw/EmailInbox'; import { PbxEventsRaw } from '../../../server/models/raw/PbxEvents'; -// TODO add trash param to model instances -export const registerServiceModels = (db: Db): void => { +// TODO add trash param to appropiate model instances +export function registerServiceModels(db: Db, trash?: Collection): void { registerModel('IRolesModel', () => new RolesRaw(db)); registerModel('IRoomsModel', () => new RoomsRaw(db)); - registerModel('ISettingsModel', () => new SettingsRaw(db)); - registerModel('ISubscriptionsModel', () => new SubscriptionsRaw(db)); + registerModel('ISettingsModel', () => new SettingsRaw(db, trash as unknown as Collection>)); + registerModel( + 'ISubscriptionsModel', + () => new SubscriptionsRaw(db, trash as unknown as Collection>), + ); registerModel('ITeamModel', () => new TeamRaw(db)); registerModel('ITeamMemberModel', () => new TeamMemberRaw(db)); registerModel('IUsersModel', () => new UsersRaw(db)); @@ -33,8 +43,14 @@ export const registerServiceModels = (db: Db): void => { // @ts-ignore-error registerModel('IMessagesModel', () => new MessagesRaw(db)); - registerModel('ILivechatInquiryModel', () => new LivechatInquiryRaw(db)); - registerModel('ILivechatDepartmentAgentsModel', () => new LivechatDepartmentAgentsRaw(db)); + registerModel( + 'ILivechatInquiryModel', + () => new LivechatInquiryRaw(db, trash as unknown as Collection>), + ); + registerModel( + 'ILivechatDepartmentAgentsModel', + () => new LivechatDepartmentAgentsRaw(db, trash as unknown as Collection>), + ); registerModel('IUsersSessionsModel', () => new UsersSessionsRaw(db)); registerModel('IPermissionsModel', () => new PermissionsRaw(db)); registerModel('ILoginServiceConfigurationModel', () => new LoginServiceConfigurationRaw(db)); @@ -43,4 +59,4 @@ export const registerServiceModels = (db: Db): void => { registerModel('IIntegrationsModel', () => new IntegrationsRaw(db)); registerModel('IEmailInboxModel', () => new EmailInboxRaw(db)); registerModel('IPbxEventsModel', () => new PbxEventsRaw(db)); -}; +} diff --git a/apps/meteor/ee/server/services/authorization/service.ts b/apps/meteor/ee/server/services/authorization/service.ts index 08cc60d61a811..d2a3a877a5565 100644 --- a/apps/meteor/ee/server/services/authorization/service.ts +++ b/apps/meteor/ee/server/services/authorization/service.ts @@ -1,12 +1,16 @@ +import type { Document } from 'mongodb'; + import '../../startup/broker'; import { api } from '../../../../server/sdk/api'; import { Authorization } from '../../../../server/services/authorization/service'; -import { getConnection } from '../mongo'; +import { Collections, getCollection, getConnection } from '../mongo'; import { registerServiceModels } from '../../lib/registerServiceModels'; -getConnection().then((db) => { - registerServiceModels(db); +getConnection().then(async (db) => { + const trash = await getCollection(Collections.Trash); + + registerServiceModels(db, trash); api.registerService(new Authorization(db)); }); diff --git a/apps/meteor/ee/server/services/mongo.ts b/apps/meteor/ee/server/services/mongo.ts index c5e1446694d70..e5cb6c427cfa2 100644 --- a/apps/meteor/ee/server/services/mongo.ts +++ b/apps/meteor/ee/server/services/mongo.ts @@ -9,7 +9,7 @@ export enum Collections { Subscriptions = 'rocketchat_subscription', UserSession = 'usersSessions', User = 'users', - Trash = 'rocketchat_trash', + Trash = 'rocketchat__trash', Messages = 'rocketchat_message', Rooms = 'rocketchat_room', Settings = 'rocketchat_settings', @@ -40,6 +40,7 @@ export const getConnection = ((): ((options?: MongoClientOptions) => Promise db = c.db(name); }); } + // if getConnection was called multiple times before it was connected, wait for the connection return (await client).db(name); }; diff --git a/apps/meteor/ee/server/services/package.json b/apps/meteor/ee/server/services/package.json index 3e7d5c53041cd..b007756a9e17e 100644 --- a/apps/meteor/ee/server/services/package.json +++ b/apps/meteor/ee/server/services/package.json @@ -68,6 +68,6 @@ "typescript": "~4.5.5" }, "volta": { - "extends": "../../package.json" + "extends": "../../../package.json" } } diff --git a/apps/meteor/ee/server/services/stream-hub/service.ts b/apps/meteor/ee/server/services/stream-hub/service.ts index 7a395c8c4945a..1faba50353176 100755 --- a/apps/meteor/ee/server/services/stream-hub/service.ts +++ b/apps/meteor/ee/server/services/stream-hub/service.ts @@ -1,11 +1,15 @@ +import type { Document } from 'mongodb'; + import '../../startup/broker'; import { api } from '../../../../server/sdk/api'; -import { getConnection } from '../mongo'; +import { Collections, getCollection, getConnection } from '../mongo'; import { registerServiceModels } from '../../lib/registerServiceModels'; getConnection().then(async (db) => { - registerServiceModels(db); + const trash = await getCollection(Collections.Trash); + + registerServiceModels(db, trash); // need to import StreamHub service after models are registered const { StreamHub } = await import('./StreamHub'); diff --git a/apps/meteor/server/modules/listeners/listeners.module.ts b/apps/meteor/server/modules/listeners/listeners.module.ts index 794ff8c4d92ea..6bdda09d52b86 100644 --- a/apps/meteor/server/modules/listeners/listeners.module.ts +++ b/apps/meteor/server/modules/listeners/listeners.module.ts @@ -345,5 +345,8 @@ export class ListenersModule { service.onEvent('connector.statuschanged', (enabled): void => { notifications.notifyLoggedInThisInstance('voip.statuschanged', enabled); }); + service.onEvent('omnichannel.room', (roomId, data): void => { + notifications.streamLivechatRoom.emitWithoutBroadcast(roomId, data); + }); } } diff --git a/apps/meteor/server/sdk/lib/Events.ts b/apps/meteor/server/sdk/lib/Events.ts index 686ad20931a1e..4130c73f3c78c 100644 --- a/apps/meteor/server/sdk/lib/Events.ts +++ b/apps/meteor/server/sdk/lib/Events.ts @@ -124,6 +124,10 @@ export type EventSignatures = { diff?: undefined | Record; id: string; }): void; + 'omnichannel.room'( + roomId: string, + data: { type: 'agentStatus'; status: string } | { type: 'queueData' | 'agentData'; data: { [k: string]: unknown } }, + ): void; // Send all events from here 'voip.events'(userId: string, data: VoipEventDataSignature): void; diff --git a/ee/apps/ddp-streamer/src/streams.ts b/ee/apps/ddp-streamer/src/streams.ts index c1113f08d7b14..e567bd5bf8656 100644 --- a/ee/apps/ddp-streamer/src/streams.ts +++ b/ee/apps/ddp-streamer/src/streams.ts @@ -1,12 +1,16 @@ +import type { Document } from 'mongodb'; + import { NotificationsModule } from '../../../../apps/meteor/server/modules/notifications/notifications.module'; import { Stream } from './Streamer'; -import { getConnection } from '../../../../apps/meteor/ee/server/services/mongo'; +import { Collections, getCollection, getConnection } from '../../../../apps/meteor/ee/server/services/mongo'; import { registerServiceModels } from '../../../../apps/meteor/ee/server/lib/registerServiceModels'; export const notifications = new NotificationsModule(Stream); -getConnection().then((db) => { - registerServiceModels(db); +getConnection().then(async (db) => { + const trash = await getCollection(Collections.Trash); + + registerServiceModels(db, trash); notifications.configure(); });