From 73dfc92cf853e45e66c2fc8b0b499e47f074a051 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Thu, 13 Mar 2025 09:53:04 -0300 Subject: [PATCH 01/15] feat(apps-engine): adds executeLivechatRoomCreatePrevent event --- .../app/apps/server/bridges/listeners.js | 1 + .../app/livechat/server/lib/LivechatTyped.ts | 20 +++++++++++++++++ .../livechat/IPreLivechatRoomCreatePrevent.ts | 19 ++++++++++++++++ .../src/definition/livechat/index.ts | 2 ++ .../src/definition/metadata/AppInterface.ts | 1 + .../src/definition/metadata/AppMethod.ts | 1 + .../src/server/managers/AppListenerManager.ts | 22 +++++++++++++++++++ 7 files changed, 66 insertions(+) create mode 100644 packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts diff --git a/apps/meteor/app/apps/server/bridges/listeners.js b/apps/meteor/app/apps/server/bridges/listeners.js index d18d5bdceb656..ebf57f7ccceb3 100644 --- a/apps/meteor/app/apps/server/bridges/listeners.js +++ b/apps/meteor/app/apps/server/bridges/listeners.js @@ -42,6 +42,7 @@ export class AppListenerBridge { * @deprecated please prefer the AppInterface.IPostLivechatRoomClosed event */ case AppInterface.ILivechatRoomClosedHandler: + case AppInterface.IPreLivechatRoomCreatePrevent: case AppInterface.IPostLivechatRoomStarted: case AppInterface.IPostLivechatRoomClosed: case AppInterface.IPostLivechatAgentAssigned: diff --git a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts index 0a874710117c6..45d08b7431adf 100644 --- a/apps/meteor/app/livechat/server/lib/LivechatTyped.ts +++ b/apps/meteor/app/livechat/server/lib/LivechatTyped.ts @@ -1,4 +1,5 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; +import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, VideoConf, api } from '@rocket.chat/core-services'; import type { IOmnichannelRoom, @@ -172,6 +173,25 @@ class LivechatClass { } } + // allows prevent room creation + const usernames: string[] = [visitor.username, defaultAgent?.username].filter((u): u is string => Boolean(u)); + const tmpRoom: { _USERNAMES?: (string | undefined)[] } & typeof roomInfo = { + ...roomInfo, + _USERNAMES: usernames, + }; + + const prevent = await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, tmpRoom).catch((error) => { + if (error.name === AppsEngineException.name) { + throw new Meteor.Error('error-app-prevented', error.message); + } + + throw error; + }); + + if (prevent) { + throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); + } + // delegate room creation to QueueManager Livechat.logger.debug(`Calling QueueManager to request a room for visitor ${visitor._id}`); diff --git a/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts new file mode 100644 index 0000000000000..1c68fde8f1544 --- /dev/null +++ b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts @@ -0,0 +1,19 @@ +import { IRead, IHttp, IPersistence } from "../accessors"; +import { AppMethod } from "../metadata"; +import { ILivechatRoom } from "./ILivechatRoom"; + +/** + * Handler called before a livechat room is created. + */ +export interface IPreLivechatRoomCreatePrevent { + /** + * Method called *before* a livechat room is created. + * + * @param livechatRoom The livechat room which is about to be created + * @param read An accessor to the environment + * @param http An accessor to the outside world + * @param persis An accessor to the App's persistence + * @param modify An accessor to the modifier + */ + [AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT](room: ILivechatRoom, read: IRead, http: IHttp, persis: IPersistence): Promise; +} diff --git a/packages/apps-engine/src/definition/livechat/index.ts b/packages/apps-engine/src/definition/livechat/index.ts index 7cc5a3d1c0046..c5045751d5bfd 100644 --- a/packages/apps-engine/src/definition/livechat/index.ts +++ b/packages/apps-engine/src/definition/livechat/index.ts @@ -13,6 +13,7 @@ import { IPostLivechatRoomClosed } from './IPostLivechatRoomClosed'; import { IPostLivechatRoomSaved } from './IPostLivechatRoomSaved'; import { IPostLivechatRoomStarted } from './IPostLivechatRoomStarted'; import { IPostLivechatRoomTransferred } from './IPostLivechatRoomTransferred'; +import { IPreLivechatRoomCreatePrevent } from './IPreLivechatRoomCreatePrevent'; import { IVisitor } from './IVisitor'; import { IVisitorEmail } from './IVisitorEmail'; import { IVisitorPhone } from './IVisitorPhone'; @@ -22,6 +23,7 @@ export { ILivechatMessage, ILivechatRoom, IPostLivechatAgentAssigned, + IPreLivechatRoomCreatePrevent, ILivechatContact, IPostLivechatAgentUnassigned, IPostLivechatGuestSaved, diff --git a/packages/apps-engine/src/definition/metadata/AppInterface.ts b/packages/apps-engine/src/definition/metadata/AppInterface.ts index 23afb7efb0799..6c599bf56c153 100644 --- a/packages/apps-engine/src/definition/metadata/AppInterface.ts +++ b/packages/apps-engine/src/definition/metadata/AppInterface.ts @@ -43,6 +43,7 @@ export enum AppInterface { * @deprecated please use the AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED method */ ILivechatRoomClosedHandler = 'ILivechatRoomClosedHandler', + IPreLivechatRoomCreatePrevent = "IPreLivechatRoomCreatePrevent", IPostLivechatAgentAssigned = 'IPostLivechatAgentAssigned', IPostLivechatAgentUnassigned = 'IPostLivechatAgentUnassigned', IPostLivechatRoomTransferred = 'IPostLivechatRoomTransferred', diff --git a/packages/apps-engine/src/definition/metadata/AppMethod.ts b/packages/apps-engine/src/definition/metadata/AppMethod.ts index 9cf7fb22aa4fa..50c4cde74f0d4 100644 --- a/packages/apps-engine/src/definition/metadata/AppMethod.ts +++ b/packages/apps-engine/src/definition/metadata/AppMethod.ts @@ -83,6 +83,7 @@ export enum AppMethod { * @deprecated please use the AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED method */ EXECUTE_LIVECHAT_ROOM_CLOSED_HANDLER = 'executeLivechatRoomClosedHandler', + EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT = 'executeLivechatRoomCreatePrevent', EXECUTE_POST_LIVECHAT_ROOM_CLOSED = 'executePostLivechatRoomClosed', EXECUTE_POST_LIVECHAT_AGENT_ASSIGNED = 'executePostLivechatAgentAssigned', EXECUTE_POST_LIVECHAT_AGENT_UNASSIGNED = 'executePostLivechatAgentUnassigned', diff --git a/packages/apps-engine/src/server/managers/AppListenerManager.ts b/packages/apps-engine/src/server/managers/AppListenerManager.ts index 2d6cd25e62ad6..86b6230796f28 100644 --- a/packages/apps-engine/src/server/managers/AppListenerManager.ts +++ b/packages/apps-engine/src/server/managers/AppListenerManager.ts @@ -166,6 +166,10 @@ interface IListenerExecutor { args: [ILivechatRoom]; result: void; }; + [AppInterface.IPreLivechatRoomCreatePrevent]: { + args: [ILivechatRoom]; + result: boolean; + }; [AppInterface.IPostLivechatRoomClosed]: { args: [ILivechatRoom]; result: void; @@ -411,6 +415,8 @@ export class AppListenerManager { */ case AppInterface.ILivechatRoomClosedHandler: return this.executeLivechatRoomClosedHandler(data as ILivechatRoom); + case AppInterface.IPreLivechatRoomCreatePrevent: + return this.executePreLivechatRoomCreatePrevent(data as ILivechatRoom); case AppInterface.IPostLivechatRoomClosed: return this.executePostLivechatRoomClosed(data as ILivechatRoom); case AppInterface.IPostLivechatRoomSaved: @@ -1058,6 +1064,22 @@ export class AppListenerManager { } // Livechat + private async executePreLivechatRoomCreatePrevent(data: ILivechatRoom): Promise { + let prevented = false; + + for (const appId of this.listeners.get(AppInterface.IPreLivechatRoomCreatePrevent)) { + const app = this.manager.getOneById(appId); + + prevented = (await app.call(AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT, data)) as boolean; + + if (prevented) { + return prevented; + } + } + + return prevented; + } + private async executePostLivechatRoomStarted(data: ILivechatRoom): Promise { for (const appId of this.listeners.get(AppInterface.IPostLivechatRoomStarted)) { const app = this.manager.getOneById(appId); From 22055a1794a7ad178263856ecf84558f527dd99f Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Fri, 14 Mar 2025 18:47:02 -0300 Subject: [PATCH 02/15] fix: add LivechatRoom info to executePreLivechatRoomCreatePrevent --- apps/meteor/app/livechat/server/lib/rooms.ts | 48 ++++++++++++++----- apps/meteor/tests/e2e/fixtures/insert-apps.ts | 3 +- .../tests/end-to-end/api/livechat/00-rooms.ts | 6 +++ packages/apps/src/bridges/IListenerBridge.ts | 5 +- 4 files changed, 49 insertions(+), 13 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/rooms.ts b/apps/meteor/app/livechat/server/lib/rooms.ts index 62c95a3c51269..1e098b377e8da 100644 --- a/apps/meteor/app/livechat/server/lib/rooms.ts +++ b/apps/meteor/app/livechat/server/lib/rooms.ts @@ -1,6 +1,14 @@ import { AppEvents, Apps } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import type { ILivechatVisitor, IMessage, IOmnichannelRoomInfo, SelectedAgent, IOmnichannelRoomExtraData } from '@rocket.chat/core-typings'; +import { RoomType } from '@rocket.chat/apps-engine/definition/rooms/RoomType'; +import type { + ILivechatVisitor, + IMessage, + IOmnichannelRoomInfo, + SelectedAgent, + IOmnichannelRoomExtraData, + IOmnichannelRoom, +} from '@rocket.chat/core-typings'; import { LivechatRooms, LivechatContacts, Messages, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models'; import { QueueManager } from './QueueManager'; @@ -93,23 +101,41 @@ export async function createRoom({ } } - // allows prevent room creation - const usernames: string[] = [visitor.username, defaultAgent?.username].filter((u): u is string => Boolean(u)); - const tmpRoom: { _USERNAMES?: (string | undefined)[] } & typeof roomInfo = { - ...roomInfo, - _USERNAMES: usernames, + const now = new Date(); + const roomProps: Omit = { + v: { + _id: visitor._id, + username: visitor.username, + status: visitor.status, + name: visitor.name, + token: visitor.token, + activity: visitor.activity, + lastMessageTs: visitor.lastChat?.ts, + phone: visitor.phone?.[0]?.phoneNumber ?? undefined, + }, + departmentId: visitor.department, + source: roomInfo.source, + t: RoomType.LIVE_CHAT, + usersCount: 0, + msgs: 0, + u: { + _id: visitor._id, + username: visitor.username, + name: visitor.name, + }, + ts: now, + livechatData: visitor.livechatData, + customFields: extraData?.customFields, }; - const prevent = await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, tmpRoom).catch((error) => { + try { + await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, roomProps); + } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); } throw error; - }); - - if (prevent) { - throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); } // delegate room creation to QueueManager diff --git a/apps/meteor/tests/e2e/fixtures/insert-apps.ts b/apps/meteor/tests/e2e/fixtures/insert-apps.ts index 80214074049b6..c626eb74322a3 100644 --- a/apps/meteor/tests/e2e/fixtures/insert-apps.ts +++ b/apps/meteor/tests/e2e/fixtures/insert-apps.ts @@ -3,7 +3,8 @@ import { request } from '@playwright/test'; import { Users } from './userStates'; import { BASE_API_URL, BASE_URL } from '../config/constants'; -const APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/master/dist/appsrocketchattester_0.1.0.zip?raw=true'; +const APP_URL = + 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/test-new-livechat-event/dist/appsrocketchattester_0.2.0.zip?raw=true'; export default async function insertApp(): Promise { const api = await request.newContext(); diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 1f4f331fa44df..dc21a9f7c59da 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -111,6 +111,12 @@ describe('LIVECHAT - rooms', () => { expect(body.room.v).to.have.property('token', visitor.token); expect(body.room.source.type).to.be.equal('api'); }); + it('should prevent create a room for visitor', async () => { + const visitor = await createVisitor(undefined, 'visitor prevent from app'); + const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); + + expect(body).to.have.property('success', false); + }); it('should return an existing open room when visitor has one available', async () => { const visitor = await createVisitor(); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); diff --git a/packages/apps/src/bridges/IListenerBridge.ts b/packages/apps/src/bridges/IListenerBridge.ts index 313ecf90f5b09..264d86153dfe4 100644 --- a/packages/apps/src/bridges/IListenerBridge.ts +++ b/packages/apps/src/bridges/IListenerBridge.ts @@ -42,7 +42,10 @@ declare module '@rocket.chat/apps-engine/server/bridges' { ): Promise; livechatEvent(int: 'IPostLivechatGuestSaved', data: ILivechatVisitor['_id']): Promise; livechatEvent(int: 'IPostLivechatRoomSaved', data: IRoom['_id']): Promise; - livechatEvent(int: 'ILivechatRoomClosedHandler' | 'IPostLivechatRoomStarted' | 'IPostLivechatRoomClosed', data: IRoom): Promise; + livechatEvent( + int: 'ILivechatRoomClosedHandler' | 'IPostLivechatRoomStarted' | 'IPostLivechatRoomClosed' | 'IPreLivechatRoomCreatePrevent', + data: IRoom, + ): Promise; livechatEvent(int: AppEvents | AppEvents[keyof AppEvents], data: any): Promise; } } From a8c0f0b57f8153cd552625e40de6d53ee719ab1c Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 17 Mar 2025 15:17:14 -0300 Subject: [PATCH 03/15] fix: simplifies hook logic --- .../src/server/managers/AppListenerManager.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/apps-engine/src/server/managers/AppListenerManager.ts b/packages/apps-engine/src/server/managers/AppListenerManager.ts index 86b6230796f28..e14a104933368 100644 --- a/packages/apps-engine/src/server/managers/AppListenerManager.ts +++ b/packages/apps-engine/src/server/managers/AppListenerManager.ts @@ -1065,19 +1065,13 @@ export class AppListenerManager { // Livechat private async executePreLivechatRoomCreatePrevent(data: ILivechatRoom): Promise { - let prevented = false; - for (const appId of this.listeners.get(AppInterface.IPreLivechatRoomCreatePrevent)) { const app = this.manager.getOneById(appId); - - prevented = (await app.call(AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT, data)) as boolean; - - if (prevented) { - return prevented; + if (await app.call(AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT, data)) { + return true; } } - - return prevented; + return false; } private async executePostLivechatRoomStarted(data: ILivechatRoom): Promise { From 992cf1329ff03bef1ac1988c76823191ca139e0a Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Mon, 17 Mar 2025 15:18:51 -0300 Subject: [PATCH 04/15] fix: keeps default error message when returning a boolean from apps --- apps/meteor/app/livechat/server/lib/rooms.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/meteor/app/livechat/server/lib/rooms.ts b/apps/meteor/app/livechat/server/lib/rooms.ts index 1e098b377e8da..8187f8b55ecc8 100644 --- a/apps/meteor/app/livechat/server/lib/rooms.ts +++ b/apps/meteor/app/livechat/server/lib/rooms.ts @@ -129,7 +129,10 @@ export async function createRoom({ }; try { - await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, roomProps); + const prevent = await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, roomProps); + if (prevent) { + throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); + } } catch (error: any) { if (error.name === AppsEngineException.name) { throw new Meteor.Error('error-app-prevented', error.message); From 757f45f80f109442a6b4240c9b009c9a75d90cef Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Tue, 18 Mar 2025 16:30:08 -0300 Subject: [PATCH 05/15] refactor: removes boolean return from apps engine hook --- .../app/livechat/server/lib/QueueManager.ts | 11 +++++ apps/meteor/app/livechat/server/lib/rooms.ts | 43 ------------------- apps/meteor/tests/e2e/fixtures/insert-apps.ts | 2 +- .../tests/end-to-end/api/livechat/00-rooms.ts | 2 +- .../livechat/IPreLivechatRoomCreatePrevent.ts | 7 ++- .../src/server/managers/AppListenerManager.ts | 10 ++--- 6 files changed, 23 insertions(+), 52 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index 8b29527b9ff0e..9f19c5145ffb0 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -32,6 +32,7 @@ import { notifyOnLivechatInquiryChangedById, notifyOnLivechatInquiryChanged } fr import { settings } from '../../../settings/server'; import { i18n } from '../../../utils/lib/i18n'; import { getOmniChatSortQuery } from '../../lib/inquiries'; +import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException'; const logger = new Logger('QueueManager'); @@ -329,6 +330,16 @@ export class QueueManager { ...(Boolean(customFields) && { customFields }), }); + try { + await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, insertionRoom); + } catch (error: any) { + if (error.name === AppsEngineException.name) { + throw new Meteor.Error('error-app-prevented', error.message); + } + + throw error; + } + // Transactional start of the conversation. This should prevent rooms from being created without inquiries and viceversa. // All the actions that happened inside createLivechatRoom are now outside this transaction const { room, inquiry } = await this.startConversation(rid, insertionRoom, guest, roomInfo, defaultAgent, message, extraData); diff --git a/apps/meteor/app/livechat/server/lib/rooms.ts b/apps/meteor/app/livechat/server/lib/rooms.ts index 8187f8b55ecc8..f2b5b24addb35 100644 --- a/apps/meteor/app/livechat/server/lib/rooms.ts +++ b/apps/meteor/app/livechat/server/lib/rooms.ts @@ -1,13 +1,10 @@ import { AppEvents, Apps } from '@rocket.chat/apps'; -import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; -import { RoomType } from '@rocket.chat/apps-engine/definition/rooms/RoomType'; import type { ILivechatVisitor, IMessage, IOmnichannelRoomInfo, SelectedAgent, IOmnichannelRoomExtraData, - IOmnichannelRoom, } from '@rocket.chat/core-typings'; import { LivechatRooms, LivechatContacts, Messages, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models'; @@ -101,46 +98,6 @@ export async function createRoom({ } } - const now = new Date(); - const roomProps: Omit = { - v: { - _id: visitor._id, - username: visitor.username, - status: visitor.status, - name: visitor.name, - token: visitor.token, - activity: visitor.activity, - lastMessageTs: visitor.lastChat?.ts, - phone: visitor.phone?.[0]?.phoneNumber ?? undefined, - }, - departmentId: visitor.department, - source: roomInfo.source, - t: RoomType.LIVE_CHAT, - usersCount: 0, - msgs: 0, - u: { - _id: visitor._id, - username: visitor.username, - name: visitor.name, - }, - ts: now, - livechatData: visitor.livechatData, - customFields: extraData?.customFields, - }; - - try { - const prevent = await Apps.self?.triggerEvent(AppEvents.IPreLivechatRoomCreatePrevent, roomProps); - if (prevent) { - throw new Meteor.Error('error-app-prevented', 'A Rocket.Chat App prevented the room creation.'); - } - } catch (error: any) { - if (error.name === AppsEngineException.name) { - throw new Meteor.Error('error-app-prevented', error.message); - } - - throw error; - } - // delegate room creation to QueueManager livechatLogger.debug(`Calling QueueManager to request a room for visitor ${visitor._id}`); diff --git a/apps/meteor/tests/e2e/fixtures/insert-apps.ts b/apps/meteor/tests/e2e/fixtures/insert-apps.ts index c626eb74322a3..204045dc579c8 100644 --- a/apps/meteor/tests/e2e/fixtures/insert-apps.ts +++ b/apps/meteor/tests/e2e/fixtures/insert-apps.ts @@ -4,7 +4,7 @@ import { Users } from './userStates'; import { BASE_API_URL, BASE_URL } from '../config/constants'; const APP_URL = - 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/test-new-livechat-event/dist/appsrocketchattester_0.2.0.zip?raw=true'; + 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/test-new-livechat-event/dist/appsrocketchattester_0.3.0.zip?raw=true'; export default async function insertApp(): Promise { const api = await request.newContext(); diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index dc21a9f7c59da..dca1af9697936 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -70,7 +70,7 @@ const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Crede return subscription; }; -describe('LIVECHAT - rooms', () => { +describe.only('LIVECHAT - rooms', () => { let visitor: ILivechatVisitor; let room: IOmnichannelRoom; diff --git a/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts index 1c68fde8f1544..c123a2a7806d5 100644 --- a/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts +++ b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts @@ -15,5 +15,10 @@ export interface IPreLivechatRoomCreatePrevent { * @param persis An accessor to the App's persistence * @param modify An accessor to the modifier */ - [AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT](room: ILivechatRoom, read: IRead, http: IHttp, persis: IPersistence): Promise; + [AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT]( + room: ILivechatRoom, + read: IRead, + http: IHttp, + persis: IPersistence + ): Promise; } diff --git a/packages/apps-engine/src/server/managers/AppListenerManager.ts b/packages/apps-engine/src/server/managers/AppListenerManager.ts index e14a104933368..a340abd060664 100644 --- a/packages/apps-engine/src/server/managers/AppListenerManager.ts +++ b/packages/apps-engine/src/server/managers/AppListenerManager.ts @@ -168,7 +168,7 @@ interface IListenerExecutor { }; [AppInterface.IPreLivechatRoomCreatePrevent]: { args: [ILivechatRoom]; - result: boolean; + result: void; }; [AppInterface.IPostLivechatRoomClosed]: { args: [ILivechatRoom]; @@ -1064,14 +1064,12 @@ export class AppListenerManager { } // Livechat - private async executePreLivechatRoomCreatePrevent(data: ILivechatRoom): Promise { + private async executePreLivechatRoomCreatePrevent(data: ILivechatRoom): Promise { for (const appId of this.listeners.get(AppInterface.IPreLivechatRoomCreatePrevent)) { const app = this.manager.getOneById(appId); - if (await app.call(AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT, data)) { - return true; - } + + await app.call(AppMethod.EXECUTE_PRE_LIVECHAT_ROOM_CREATE_PREVENT, data); } - return false; } private async executePostLivechatRoomStarted(data: ILivechatRoom): Promise { From d1d9463801ade3971b41a3c11964cb6d986046e0 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Tue, 18 Mar 2025 16:37:43 -0300 Subject: [PATCH 06/15] chore: added just for CI debug --- apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index dca1af9697936..dc21a9f7c59da 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -70,7 +70,7 @@ const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Crede return subscription; }; -describe.only('LIVECHAT - rooms', () => { +describe('LIVECHAT - rooms', () => { let visitor: ILivechatVisitor; let room: IOmnichannelRoom; From 570947f0424ab66617afd9b0f9d9f58a6e0c5c3c Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Tue, 18 Mar 2025 17:41:57 -0300 Subject: [PATCH 07/15] fix: lint --- apps/meteor/app/livechat/server/lib/QueueManager.ts | 2 +- apps/meteor/app/livechat/server/lib/rooms.ts | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/apps/meteor/app/livechat/server/lib/QueueManager.ts b/apps/meteor/app/livechat/server/lib/QueueManager.ts index 9f19c5145ffb0..ac1ec08d00e34 100644 --- a/apps/meteor/app/livechat/server/lib/QueueManager.ts +++ b/apps/meteor/app/livechat/server/lib/QueueManager.ts @@ -1,4 +1,5 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; +import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException'; import { Message, Omnichannel } from '@rocket.chat/core-services'; import type { ILivechatDepartment, @@ -32,7 +33,6 @@ import { notifyOnLivechatInquiryChangedById, notifyOnLivechatInquiryChanged } fr import { settings } from '../../../settings/server'; import { i18n } from '../../../utils/lib/i18n'; import { getOmniChatSortQuery } from '../../lib/inquiries'; -import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException'; const logger = new Logger('QueueManager'); diff --git a/apps/meteor/app/livechat/server/lib/rooms.ts b/apps/meteor/app/livechat/server/lib/rooms.ts index f2b5b24addb35..4db8f526e7c4c 100644 --- a/apps/meteor/app/livechat/server/lib/rooms.ts +++ b/apps/meteor/app/livechat/server/lib/rooms.ts @@ -1,11 +1,5 @@ import { AppEvents, Apps } from '@rocket.chat/apps'; -import type { - ILivechatVisitor, - IMessage, - IOmnichannelRoomInfo, - SelectedAgent, - IOmnichannelRoomExtraData, -} from '@rocket.chat/core-typings'; +import type { ILivechatVisitor, IMessage, IOmnichannelRoomInfo, SelectedAgent, IOmnichannelRoomExtraData } from '@rocket.chat/core-typings'; import { LivechatRooms, LivechatContacts, Messages, LivechatCustomField, LivechatInquiry, Rooms, Subscriptions } from '@rocket.chat/models'; import { QueueManager } from './QueueManager'; From 312b03b973e7de56dcd17f098132dadb1c2de8a2 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Wed, 19 Mar 2025 09:33:34 -0300 Subject: [PATCH 08/15] fix: install test app before runs testapi --- .../tests/end-to-end/api/livechat/00-rooms.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index dc21a9f7c59da..9f80742b4bcf1 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -56,6 +56,8 @@ import { import { adminUsername, password } from '../../../data/user'; import { createUser, deleteUser, login } from '../../../data/users.helper'; import { IS_EE } from '../../../e2e/config/constants'; +import insertApp from '../../../e2e/fixtures/insert-apps'; +import injectInitialData from '../../../e2e/fixtures/inject-initial-data'; const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Credentials): Promise => { const response = await request @@ -77,6 +79,8 @@ describe('LIVECHAT - rooms', () => { before((done) => getCredentials(done)); before(async () => { + await injectInitialData(); + await insertApp(); await updateSetting('Livechat_enabled', true); await updateEESetting('Livechat_Require_Contact_Verification', 'never'); await updateSetting('Omnichannel_enable_department_removal', true); @@ -101,6 +105,13 @@ describe('LIVECHAT - rooms', () => { const visitor = await createVisitor(); await request.get(api('livechat/room')).query({ token: visitor.token, rid: 'invalid-rid' }).expect(400); }); + it('should prevent create a room for visitor if an app throws an error', async () => { + // this test relies on the app installed by the insertApp fixture + const visitor = await createVisitor(undefined, 'visitor prevent from app'); + const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); + + expect(body).to.have.property('success', false); + }); it('should create a room for visitor', async () => { const visitor = await createVisitor(); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); @@ -111,12 +122,6 @@ describe('LIVECHAT - rooms', () => { expect(body.room.v).to.have.property('token', visitor.token); expect(body.room.source.type).to.be.equal('api'); }); - it('should prevent create a room for visitor', async () => { - const visitor = await createVisitor(undefined, 'visitor prevent from app'); - const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); - - expect(body).to.have.property('success', false); - }); it('should return an existing open room when visitor has one available', async () => { const visitor = await createVisitor(); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token }); From b04a625b1bcc414ddeb265133d480955b39baea0 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Wed, 19 Mar 2025 10:12:00 -0300 Subject: [PATCH 09/15] fix: lint --- apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 9f80742b4bcf1..7016ebe48a12d 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -56,8 +56,8 @@ import { import { adminUsername, password } from '../../../data/user'; import { createUser, deleteUser, login } from '../../../data/users.helper'; import { IS_EE } from '../../../e2e/config/constants'; -import insertApp from '../../../e2e/fixtures/insert-apps'; import injectInitialData from '../../../e2e/fixtures/inject-initial-data'; +import insertApp from '../../../e2e/fixtures/insert-apps'; const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Credentials): Promise => { const response = await request From ea1100e5dff8cbca6c90a7d92357348abe7f7740 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 19 Mar 2025 11:24:10 -0300 Subject: [PATCH 10/15] test: install app for API tests --- apps/meteor/tests/e2e/config/constants.ts | 2 ++ apps/meteor/tests/e2e/fixtures/insert-apps.ts | 7 ++----- .../tests/end-to-end/api/livechat/00-rooms.ts | 17 ++++++++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/meteor/tests/e2e/config/constants.ts b/apps/meteor/tests/e2e/config/constants.ts index 4c2a174e98aa2..1ed6e361f5459 100644 --- a/apps/meteor/tests/e2e/config/constants.ts +++ b/apps/meteor/tests/e2e/config/constants.ts @@ -20,3 +20,5 @@ export const DEFAULT_USER_CREDENTIALS = { password: 'password', bcrypt: '$2b$10$LNYaqDreDE7tt9EVEeaS9uw.C3hic9hcqFfIocMBPTMxJaDCC6QWW', } as const; + +export const TEST_APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/master/dist/appsrocketchattester_0.3.0.zip?raw=true'; diff --git a/apps/meteor/tests/e2e/fixtures/insert-apps.ts b/apps/meteor/tests/e2e/fixtures/insert-apps.ts index 204045dc579c8..f1f9501f9edf0 100644 --- a/apps/meteor/tests/e2e/fixtures/insert-apps.ts +++ b/apps/meteor/tests/e2e/fixtures/insert-apps.ts @@ -1,10 +1,7 @@ import { request } from '@playwright/test'; import { Users } from './userStates'; -import { BASE_API_URL, BASE_URL } from '../config/constants'; - -const APP_URL = - 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/test-new-livechat-event/dist/appsrocketchattester_0.3.0.zip?raw=true'; +import { BASE_API_URL, BASE_URL, TEST_APP_URL } from '../config/constants'; export default async function insertApp(): Promise { const api = await request.newContext(); @@ -14,6 +11,6 @@ export default async function insertApp(): Promise { 'X-User-Id': Users.admin.data.username, }; - await api.post(`${BASE_URL}/api/apps`, { data: { url: APP_URL }, headers }); + await api.post(`${BASE_URL}/api/apps`, { data: { url: TEST_APP_URL }, headers }); await api.post(`${BASE_API_URL}/settings/VideoConf_Default_Provider`, { data: { value: 'test' }, headers }); } diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 7016ebe48a12d..37307e96c67a8 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -20,6 +20,7 @@ import type { Response } from 'supertest'; import type { SuccessResult } from '../../../../app/api/server/definition'; import { getCredentials, api, request, credentials, methodCall } from '../../../data/api-data'; +import { apps } from '../../../data/apps/apps-data'; import { createCustomField } from '../../../data/livechat/custom-fields'; import { createDepartmentWithAnOfflineAgent, createDepartmentWithAnOnlineAgent, deleteDepartment } from '../../../data/livechat/department'; import { createSLA, getRandomPriority } from '../../../data/livechat/priorities'; @@ -55,9 +56,7 @@ import { } from '../../../data/permissions.helper'; import { adminUsername, password } from '../../../data/user'; import { createUser, deleteUser, login } from '../../../data/users.helper'; -import { IS_EE } from '../../../e2e/config/constants'; -import injectInitialData from '../../../e2e/fixtures/inject-initial-data'; -import insertApp from '../../../e2e/fixtures/insert-apps'; +import { IS_EE, TEST_APP_URL } from '../../../e2e/config/constants'; const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Credentials): Promise => { const response = await request @@ -75,12 +74,11 @@ const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Crede describe('LIVECHAT - rooms', () => { let visitor: ILivechatVisitor; let room: IOmnichannelRoom; + let appId: string; before((done) => getCredentials(done)); before(async () => { - await injectInitialData(); - await insertApp(); await updateSetting('Livechat_enabled', true); await updateEESetting('Livechat_Require_Contact_Verification', 'never'); await updateSetting('Omnichannel_enable_department_removal', true); @@ -89,9 +87,18 @@ describe('LIVECHAT - rooms', () => { visitor = await createVisitor(); room = await createLivechatRoom(visitor.token); + + const response = await request.post(apps('/')).set(credentials).send({ url: TEST_APP_URL }).expect(200); + + appId = response.body.app.id; }); after(async () => { await updateSetting('Omnichannel_enable_department_removal', false); + + await request + .delete(apps(`/${appId}`)) + .set(credentials) + .expect(200); }); describe('livechat/room', () => { From cac913bfaa83a6d7866b764e5697cc4891997e39 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 19 Mar 2025 12:29:07 -0300 Subject: [PATCH 11/15] Update packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts --- .../src/definition/livechat/IPreLivechatRoomCreatePrevent.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts index c123a2a7806d5..35efe7229faf7 100644 --- a/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts +++ b/packages/apps-engine/src/definition/livechat/IPreLivechatRoomCreatePrevent.ts @@ -4,6 +4,8 @@ import { ILivechatRoom } from "./ILivechatRoom"; /** * Handler called before a livechat room is created. + * + * To prevent the room from being created, the app should throw an `AppsEngineException` */ export interface IPreLivechatRoomCreatePrevent { /** From 58d51bc94857401b1485699cb6929d47e2dc6848 Mon Sep 17 00:00:00 2001 From: Alfredo Del Fabro Neto Date: Wed, 19 Mar 2025 13:19:22 -0300 Subject: [PATCH 12/15] chore: adds changeset --- .changeset/sour-ghosts-count.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/sour-ghosts-count.md diff --git a/.changeset/sour-ghosts-count.md b/.changeset/sour-ghosts-count.md new file mode 100644 index 0000000000000..b91282b9263e0 --- /dev/null +++ b/.changeset/sour-ghosts-count.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/apps-engine': minor +'@rocket.chat/apps': minor +'@rocket.chat/meteor': minor +--- + +Adds the executeLivechatRoomCreatePrevent hook to the Rocket.Chat Apps-Engine to prevent the creation of live chat rooms. From 3ebbddbaff28a3d5ece1f1aaa3e92122c77f5dd1 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 19 Mar 2025 15:30:19 -0300 Subject: [PATCH 13/15] test: check if app is enabled --- .../ee/server/apps/communication/rest.ts | 2 +- .../tests/end-to-end/api/livechat/00-rooms.ts | 29 ++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/apps/meteor/ee/server/apps/communication/rest.ts b/apps/meteor/ee/server/apps/communication/rest.ts index c8b7fc9d0872f..10409f7d24ddc 100644 --- a/apps/meteor/ee/server/apps/communication/rest.ts +++ b/apps/meteor/ee/server/apps/communication/rest.ts @@ -817,7 +817,7 @@ export class AppsRestApi { } return API.v1.success({ - app: formatAppInstanceForRest(app), + app: await formatAppInstanceForRest(app), }); }, async post() { diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 37307e96c67a8..0afd13655d6a5 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -78,20 +78,41 @@ describe('LIVECHAT - rooms', () => { before((done) => getCredentials(done)); + before('install tester app', async () => { + // install the app + const response = await request.post(apps('/')).set(credentials).send({ url: TEST_APP_URL }).expect(200); + + appId = response.body.app.id; + + // check if app is enabled with 5 max retries + const checkAppEnabled = async (retries = 5): Promise => { + if (retries === 0) { + throw new Error('App is not enabled'); + } + + const response = await request.get(apps(`/${appId}`)).set(credentials); + + if (response.status !== 200 || response.body.app.status !== 'manually_enabled') { + await sleep(100); + return checkAppEnabled(retries - 1); + } + }; + + await checkAppEnabled(); + }); + before(async () => { await updateSetting('Livechat_enabled', true); await updateEESetting('Livechat_Require_Contact_Verification', 'never'); await updateSetting('Omnichannel_enable_department_removal', true); await createAgent(); await makeAgentAvailable(); + visitor = await createVisitor(); room = await createLivechatRoom(visitor.token); - - const response = await request.post(apps('/')).set(credentials).send({ url: TEST_APP_URL }).expect(200); - - appId = response.body.app.id; }); + after(async () => { await updateSetting('Omnichannel_enable_department_removal', false); From c3b01972746bcb123f95ce22cf9d927604bd4950 Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 19 Mar 2025 15:55:01 -0300 Subject: [PATCH 14/15] test: unify APP_URL constant --- apps/meteor/tests/data/apps/apps-data.ts | 2 +- apps/meteor/tests/e2e/config/constants.ts | 2 -- apps/meteor/tests/e2e/fixtures/insert-apps.ts | 5 +++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/apps/meteor/tests/data/apps/apps-data.ts b/apps/meteor/tests/data/apps/apps-data.ts index 30bc3e92b1441..ba6a69bba103f 100644 --- a/apps/meteor/tests/data/apps/apps-data.ts +++ b/apps/meteor/tests/data/apps/apps-data.ts @@ -1,6 +1,6 @@ import type { Path } from '@rocket.chat/rest-typings'; -export const APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/raw/master/dist/appsrocketchattester_0.1.1.zip?raw=true'; +export const APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/raw/master/dist/appsrocketchattester_0.3.0.zip?raw=true'; export const APP_NAME = 'Apps.RocketChat.Tester'; type PathWithoutPrefix = TPath extends `/apps${infer U}` ? U : never; diff --git a/apps/meteor/tests/e2e/config/constants.ts b/apps/meteor/tests/e2e/config/constants.ts index 1ed6e361f5459..4c2a174e98aa2 100644 --- a/apps/meteor/tests/e2e/config/constants.ts +++ b/apps/meteor/tests/e2e/config/constants.ts @@ -20,5 +20,3 @@ export const DEFAULT_USER_CREDENTIALS = { password: 'password', bcrypt: '$2b$10$LNYaqDreDE7tt9EVEeaS9uw.C3hic9hcqFfIocMBPTMxJaDCC6QWW', } as const; - -export const TEST_APP_URL = 'https://github.com/RocketChat/Apps.RocketChat.Tester/blob/master/dist/appsrocketchattester_0.3.0.zip?raw=true'; diff --git a/apps/meteor/tests/e2e/fixtures/insert-apps.ts b/apps/meteor/tests/e2e/fixtures/insert-apps.ts index f1f9501f9edf0..eaa370e67f1d7 100644 --- a/apps/meteor/tests/e2e/fixtures/insert-apps.ts +++ b/apps/meteor/tests/e2e/fixtures/insert-apps.ts @@ -1,7 +1,8 @@ import { request } from '@playwright/test'; import { Users } from './userStates'; -import { BASE_API_URL, BASE_URL, TEST_APP_URL } from '../config/constants'; +import { APP_URL } from '../../data/apps/apps-data'; +import { BASE_API_URL, BASE_URL } from '../config/constants'; export default async function insertApp(): Promise { const api = await request.newContext(); @@ -11,6 +12,6 @@ export default async function insertApp(): Promise { 'X-User-Id': Users.admin.data.username, }; - await api.post(`${BASE_URL}/api/apps`, { data: { url: TEST_APP_URL }, headers }); + await api.post(`${BASE_URL}/api/apps`, { data: { url: APP_URL }, headers }); await api.post(`${BASE_API_URL}/settings/VideoConf_Default_Provider`, { data: { value: 'test' }, headers }); } From e734fa3c513789655307006d4ab9bb1a801299ab Mon Sep 17 00:00:00 2001 From: Diego Sampaio Date: Wed, 19 Mar 2025 15:55:09 -0300 Subject: [PATCH 15/15] test: run test only on EE --- .../tests/end-to-end/api/livechat/00-rooms.ts | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts index 0afd13655d6a5..fc1413cd870c4 100644 --- a/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts +++ b/apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts @@ -20,7 +20,7 @@ import type { Response } from 'supertest'; import type { SuccessResult } from '../../../../app/api/server/definition'; import { getCredentials, api, request, credentials, methodCall } from '../../../data/api-data'; -import { apps } from '../../../data/apps/apps-data'; +import { apps, APP_URL } from '../../../data/apps/apps-data'; import { createCustomField } from '../../../data/livechat/custom-fields'; import { createDepartmentWithAnOfflineAgent, createDepartmentWithAnOnlineAgent, deleteDepartment } from '../../../data/livechat/department'; import { createSLA, getRandomPriority } from '../../../data/livechat/priorities'; @@ -56,7 +56,7 @@ import { } from '../../../data/permissions.helper'; import { adminUsername, password } from '../../../data/user'; import { createUser, deleteUser, login } from '../../../data/users.helper'; -import { IS_EE, TEST_APP_URL } from '../../../e2e/config/constants'; +import { IS_EE } from '../../../e2e/config/constants'; const getSubscriptionForRoom = async (roomId: string, overrideCredential?: Credentials): Promise => { const response = await request @@ -78,30 +78,26 @@ describe('LIVECHAT - rooms', () => { before((done) => getCredentials(done)); - before('install tester app', async () => { - // install the app - const response = await request.post(apps('/')).set(credentials).send({ url: TEST_APP_URL }).expect(200); - - appId = response.body.app.id; - - // check if app is enabled with 5 max retries - const checkAppEnabled = async (retries = 5): Promise => { - if (retries === 0) { - throw new Error('App is not enabled'); - } - - const response = await request.get(apps(`/${appId}`)).set(credentials); - - if (response.status !== 200 || response.body.app.status !== 'manually_enabled') { - await sleep(100); - return checkAppEnabled(retries - 1); - } - }; + before(async () => { + if (IS_EE) { + // install the app + await request + .post(apps('/')) + .set(credentials) + .send({ url: APP_URL }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res: Response) => { + expect(res.body).to.have.a.property('success', true); + expect(res.body).to.have.a.property('app'); + expect(res.body.app).to.have.a.property('id'); + expect(res.body.app).to.have.a.property('version'); + expect(res.body.app).to.have.a.property('status').and.to.be.equal('auto_enabled'); - await checkAppEnabled(); - }); + appId = res.body.app.id; + }); + } - before(async () => { await updateSetting('Livechat_enabled', true); await updateEESetting('Livechat_Require_Contact_Verification', 'never'); await updateSetting('Omnichannel_enable_department_removal', true); @@ -116,10 +112,12 @@ describe('LIVECHAT - rooms', () => { after(async () => { await updateSetting('Omnichannel_enable_department_removal', false); - await request - .delete(apps(`/${appId}`)) - .set(credentials) - .expect(200); + if (IS_EE) { + await request + .delete(apps(`/${appId}`)) + .set(credentials) + .expect(200); + } }); describe('livechat/room', () => { @@ -133,7 +131,7 @@ describe('LIVECHAT - rooms', () => { const visitor = await createVisitor(); await request.get(api('livechat/room')).query({ token: visitor.token, rid: 'invalid-rid' }).expect(400); }); - it('should prevent create a room for visitor if an app throws an error', async () => { + (IS_EE ? it : it.skip)('should prevent create a room for visitor if an app throws an error', async () => { // this test relies on the app installed by the insertApp fixture const visitor = await createVisitor(undefined, 'visitor prevent from app'); const { body } = await request.get(api('livechat/room')).query({ token: visitor.token });