From 97a659d8b15b8f1d4c3bbd3a7e3a4095f5320a06 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 28 Dec 2023 20:03:17 -0300 Subject: [PATCH 1/7] Start Listener handler --- deno-runtime/handlers/listener/handler.ts | 101 ++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 deno-runtime/handlers/listener/handler.ts diff --git a/deno-runtime/handlers/listener/handler.ts b/deno-runtime/handlers/listener/handler.ts new file mode 100644 index 000000000..21ad917a1 --- /dev/null +++ b/deno-runtime/handlers/listener/handler.ts @@ -0,0 +1,101 @@ +import { Defined, JsonRpcError } from 'jsonrpc-lite'; +import type { App } from '@rocket.chat/apps-engine/definition/App.ts'; + +import { AppObjectRegistry } from '../../AppObjectRegistry.ts'; +import { MessageExtender } from "../../lib/accessors/extenders/MessageExtender.ts"; +import { IMessage } from "@rocket.chat/apps-engine/definition/messages/IMessage.ts"; +import { IRoom } from "@rocket.chat/apps-engine/definition/rooms/IRoom.ts"; +import { RoomExtender } from "../../lib/accessors/extenders/RoomExtender.ts"; +import { MessageBuilder } from "../../lib/accessors/builders/MessageBuilder.ts"; +import { RoomBuilder } from "../../lib/accessors/builders/RoomBuilder.ts"; +import { AppAccessorsInstance } from "../../lib/accessors/mod.ts"; + +export default async function handleListener(method: string, params: unknown): Promise { + const [, evtInterface] = method.split(':'); + + const app = AppObjectRegistry.get('app'); + + const eventExecutor = app?.[evtInterface as keyof App]; + + if (typeof eventExecutor !== 'function') { + return new JsonRpcError('Invalid event interface called on app', -32000); + } + + if (!Array.isArray(params) || params.length < 1 || params.length > 2) { + return new JsonRpcError('Invalid params', -32602); + } + + try { + const args = parseArgs(evtInterface, params); + return await (eventExecutor as Function).apply(app, args); + } catch (e) { + if (e instanceof JsonRpcError) { + return e; + } + + return JsonRpcError.internalError(e.message); + } +} + +function parseArgs(evtInterface: string, params: unknown[]): unknown[] { + /** + * param1 is the context for the event handler execution + * param2 is an optional extra content that some hanlers require + */ + const [param1, param2] = params as [unknown, unknown]; + + if (!param1) { + throw new JsonRpcError('Invalid params', -32000); + } + + const args: unknown[] = [param1, AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp()]; + + // "check" events will only go this far - (context, reader, http) + if (evtInterface.startsWith('check')) { + // "checkPostMessageDeleted" has an extra param - (context, reader, http, extraContext) + if (param2) { + args.push(param2); + } + + return args; + } + + // From this point on, all events will require (reader, http, persistence) injected + args.push(AppAccessorsInstance.getPersistence()); + + // "extend" events have an additional "Extender" param - (context, extender, reader, http, persistence) + if (evtInterface.endsWith('Extend')) { + if (evtInterface.includes('Message')) { + args.splice(1, 0, new MessageExtender(param1 as IMessage)); + } else if (evtInterface.includes('Room')) { + args.splice(1, 0, new RoomExtender(param1 as IRoom)); + } + + return args; + } + + // "Modify" events have an additional "Builder" param - (context, builder, reader, http, persistence) + if (evtInterface.endsWith('Modify')) { + if (evtInterface.includes('Message')) { + args.splice(1, 0, new MessageBuilder(param1 as IMessage)); + } else if (evtInterface.includes('Room')) { + args.splice(1, 0, new RoomBuilder(param1 as IRoom)); + } + + return args; + } + + // From this point on, all events will require (reader, http, persistence, modifier) injected + args.push(AppAccessorsInstance.getModifier()); + + // This guy gets an extra one + if (evtInterface === 'executePostMessageDeleted') { + if (!param2) { + throw new JsonRpcError('Invalid params', -32000); + } + + args.push(param2); + } + + return args; +} From 62596c7587ff6458b1d8239c35e2964f7b2ba77e Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Thu, 28 Dec 2023 20:03:34 -0300 Subject: [PATCH 2/7] Partial refactor of AppListenerManager for the new runtime --- src/server/managers/AppListenerManager.ts | 354 +++++----------------- 1 file changed, 72 insertions(+), 282 deletions(-) diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index b2f9fc39b..0fe0cf34c 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -448,28 +448,20 @@ export class AppListenerManager { // Messages private async executePreMessageSentPrevent(data: IMessage): Promise { let prevented = false; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreMessageSentPrevent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGESENTPREVENT)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTPREVENT, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTPREVENT, data)) as boolean; + + if (!continueOn) { + continue; } - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGESENTPREVENT)) { - prevented = (await app.call( - AppMethod.EXECUTEPREMESSAGESENTPREVENT, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as boolean; + prevented = (await app.call(AppMethod.EXECUTEPREMESSAGESENTPREVENT, data)) as boolean; - if (prevented) { - return prevented; - } + if (prevented) { + return prevented; } } @@ -477,26 +469,15 @@ export class AppListenerManager { } private async executePreMessageSentExtend(data: IMessage): Promise { - const msg = data; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); + let msg = data; for (const appId of this.listeners.get(AppInterface.IPreMessageSentExtend)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGESENTEXTEND)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTEXTEND, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTEXTEND, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGESENTEXTEND)) { - await app.call( - AppMethod.EXECUTEPREMESSAGESENTEXTEND, - cfMsg, - new MessageExtender(msg), // This mutates the passed in object - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - ); + if (continueOn) { + msg = await app.call(AppMethod.EXECUTEPREMESSAGESENTEXTEND, msg); } } @@ -505,36 +486,23 @@ export class AppListenerManager { private async executePreMessageSentModify(data: IMessage): Promise { let msg = data; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreMessageSentModify)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGESENTMODIFY)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTMODIFY, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTMODIFY, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGESENTMODIFY)) { - msg = (await app.call( - AppMethod.EXECUTEPREMESSAGESENTMODIFY, - cfMsg, - new MessageBuilder(msg), - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as IMessage; + if (continueOn) { + msg = (await app.call(AppMethod.EXECUTEPREMESSAGESENTMODIFY, msg)) as IMessage; } } - return data; + return msg; } private async executePostMessageSent(data: IMessage): Promise { - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); - // First check if the app implements Bot DM handlers and check if the dm contains more than one user - if (cfMsg.room.type === RoomType.DIRECT_MESSAGE && cfMsg.room.userIds.length > 1) { + if (data.room.type === RoomType.DIRECT_MESSAGE && data.room.userIds.length > 1) { for (const appId of this.listeners.get(AppInterface.IPostMessageSentToBot)) { const app = this.manager.getOneById(appId); if (app.hasMethod(AppMethod.EXECUTEPOSTMESSAGESENTTOBOT)) { @@ -546,22 +514,15 @@ export class AppListenerManager { // if the sender is the bot just ignore it - if (bot.id === cfMsg.sender.id) { + if (bot.id === data.sender.id) { continue; } // if the user doesnt belong to the room ignore it - if (!cfMsg.room.userIds.includes(bot.id)) { + if (!data.room.userIds.includes(bot.id)) { continue; } - await app.call( - AppMethod.EXECUTEPOSTMESSAGESENTTOBOT, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTEPOSTMESSAGESENTTOBOT, data); } } } @@ -569,44 +530,24 @@ export class AppListenerManager { for (const appId of this.listeners.get(AppInterface.IPostMessageSent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPOSTMESSAGESENT)) { - continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGESENT, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGESENT, data, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPOSTMESSAGESENT)) { - await app.call( - AppMethod.EXECUTEPOSTMESSAGESENT, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + if (continueOn) { + await app.call(AppMethod.EXECUTEPOSTMESSAGESENT, data); } } } private async executePreMessageDeletePrevent(data: IMessage): Promise { let prevented = false; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreMessageDeletePrevent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGEDELETEPREVENT)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGEDELETEPREVENT, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGEDELETEPREVENT, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGEDELETEPREVENT)) { - prevented = (await app.call( - AppMethod.EXECUTEPREMESSAGEDELETEPREVENT, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as boolean; + if (continueOn) { + prevented = (await app.call(AppMethod.EXECUTEPREMESSAGEDELETEPREVENT, data)) as boolean; if (prevented) { return prevented; @@ -624,53 +565,30 @@ export class AppListenerManager { for (const appId of this.listeners.get(AppInterface.IPostMessageDeleted)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPOSTMESSAGEDELETED)) { - continueOn = (await app.call( - AppMethod.CHECKPOSTMESSAGEDELETED, - // `context` has more information about the event, but - // we had to keep this `message` here for compatibility - message, - this.am.getReader(appId), - this.am.getHttp(appId), - context, - )) as boolean; - } + const continueOn = (await app.call( + AppMethod.CHECKPOSTMESSAGEDELETED, + // `context` has more information about the event, but + // we had to keep this `message` here for compatibility + message, + context, + )) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPOSTMESSAGEDELETED)) { - await app.call( - AppMethod.EXECUTEPOSTMESSAGEDELETED, - message, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - context, - ); + if (continueOn) { + await app.call(AppMethod.EXECUTEPOSTMESSAGEDELETED, message, context); } } } private async executePreMessageUpdatedPrevent(data: IMessage): Promise { let prevented = false; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreMessageUpdatedPrevent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGEUPDATEDPREVENT)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDPREVENT, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDPREVENT, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGEUPDATEDPREVENT)) { - prevented = (await app.call( - AppMethod.EXECUTEPREMESSAGEUPDATEDPREVENT, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as boolean; + if (continueOn) { + prevented = (await app.call(AppMethod.EXECUTEPREMESSAGEUPDATEDPREVENT, data)) as boolean; if (prevented) { return prevented; @@ -682,26 +600,15 @@ export class AppListenerManager { } private async executePreMessageUpdatedExtend(data: IMessage): Promise { - const msg = data; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); + let msg = data; for (const appId of this.listeners.get(AppInterface.IPreMessageUpdatedExtend)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGEUPDATEDEXTEND)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDEXTEND, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDEXTEND, msg)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGEUPDATEDEXTEND)) { - await app.call( - AppMethod.EXECUTEPREMESSAGEUPDATEDEXTEND, - cfMsg, - new MessageExtender(msg), // This mutates the passed in object - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - ); + if (continueOn) { + msg = await app.call(AppMethod.EXECUTEPREMESSAGEUPDATEDEXTEND, msg); } } @@ -710,76 +617,43 @@ export class AppListenerManager { private async executePreMessageUpdatedModify(data: IMessage): Promise { let msg = data; - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreMessageUpdatedModify)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREMESSAGEUPDATEDMODIFY)) { - continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDMODIFY, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGEUPDATEDMODIFY, msg)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREMESSAGEUPDATEDMODIFY)) { - msg = (await app.call( - AppMethod.EXECUTEPREMESSAGEUPDATEDMODIFY, - cfMsg, - new MessageBuilder(msg), - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as IMessage; + if (continueOn) { + msg = (await app.call(AppMethod.EXECUTEPREMESSAGEUPDATEDMODIFY, msg)) as IMessage; } } - return data; + return msg; } private async executePostMessageUpdated(data: IMessage): Promise { - const cfMsg = new Message(Utilities.deepCloneAndFreeze(data), this.manager); - for (const appId of this.listeners.get(AppInterface.IPostMessageUpdated)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPOSTMESSAGEUPDATED)) { - continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGEUPDATED, cfMsg, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGEUPDATED, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPOSTMESSAGEUPDATED)) { - await app.call( - AppMethod.EXECUTEPOSTMESSAGEUPDATED, - cfMsg, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + if (continueOn) { + await app.call(AppMethod.EXECUTEPOSTMESSAGEUPDATED, data); } } } // Rooms private async executePreRoomCreatePrevent(data: IRoom): Promise { - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); let prevented = false; for (const appId of this.listeners.get(AppInterface.IPreRoomCreatePrevent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREROOMCREATEPREVENT)) { - continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEPREVENT, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEPREVENT, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREROOMCREATEPREVENT)) { - prevented = (await app.call( - AppMethod.EXECUTEPREROOMCREATEPREVENT, - cfRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as boolean; + if (continueOn) { + prevented = (await app.call(AppMethod.EXECUTEPREROOMCREATEPREVENT, data)) as boolean; if (prevented) { return prevented; @@ -791,53 +665,31 @@ export class AppListenerManager { } private async executePreRoomCreateExtend(data: IRoom): Promise { - const room = data; - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); + let room = data; for (const appId of this.listeners.get(AppInterface.IPreRoomCreateExtend)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREROOMCREATEEXTEND)) { - continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEEXTEND, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEEXTEND, room)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREROOMCREATEEXTEND)) { - await app.call( - AppMethod.EXECUTEPREROOMCREATEEXTEND, - cfRoom, - new RoomExtender(room), // This mutates the passed in object - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - ); + if (continueOn) { + room = await app.call(AppMethod.EXECUTEPREROOMCREATEEXTEND, room); } } - return data; + return room; } private async executePreRoomCreateModify(data: IRoom): Promise { let room = data; - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); for (const appId of this.listeners.get(AppInterface.IPreRoomCreateModify)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREROOMCREATEMODIFY)) { - continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEMODIFY, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREROOMCREATEMODIFY, room)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREROOMCREATEMODIFY)) { - room = (await app.call( - AppMethod.EXECUTEPREROOMCREATEMODIFY, - cfRoom, - new RoomBuilder(room), - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as IRoom; + if (continueOn) { + room = (await app.call(AppMethod.EXECUTEPREROOMCREATEMODIFY, room)) as IRoom; } } @@ -845,49 +697,27 @@ export class AppListenerManager { } private async executePostRoomCreate(data: IRoom): Promise { - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); - for (const appId of this.listeners.get(AppInterface.IPostRoomCreate)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPOSTROOMCREATE)) { - continueOn = (await app.call(AppMethod.CHECKPOSTROOMCREATE, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPOSTROOMCREATE, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPOSTROOMCREATE)) { - await app.call( - AppMethod.EXECUTEPOSTROOMCREATE, - cfRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + if (continueOn) { + await app.call(AppMethod.EXECUTEPOSTROOMCREATE, data); } } } private async executePreRoomDeletePrevent(data: IRoom): Promise { - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); let prevented = false; for (const appId of this.listeners.get(AppInterface.IPreRoomDeletePrevent)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPREROOMDELETEPREVENT)) { - continueOn = (await app.call(AppMethod.CHECKPREROOMDELETEPREVENT, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPREROOMDELETEPREVENT, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPREROOMDELETEPREVENT)) { - prevented = (await app.call( - AppMethod.EXECUTEPREROOMDELETEPREVENT, - cfRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - )) as boolean; + if (continueOn) { + prevented = (await app.call(AppMethod.EXECUTEPREROOMDELETEPREVENT, data)) as boolean; if (prevented) { return prevented; @@ -899,82 +729,42 @@ export class AppListenerManager { } private async executePostRoomDeleted(data: IRoom): Promise { - const cfRoom = new Room(Utilities.deepCloneAndFreeze(data), this.manager); - for (const appId of this.listeners.get(AppInterface.IPostRoomDeleted)) { const app = this.manager.getOneById(appId); - let continueOn = true; - if (app.hasMethod(AppMethod.CHECKPOSTROOMDELETED)) { - continueOn = (await app.call(AppMethod.CHECKPOSTROOMDELETED, cfRoom, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; - } + const continueOn = (await app.call(AppMethod.CHECKPOSTROOMDELETED, data)) as boolean; - if (continueOn && app.hasMethod(AppMethod.EXECUTEPOSTROOMDELETED)) { - await app.call(AppMethod.EXECUTEPOSTROOMDELETED, cfRoom, this.am.getReader(appId), this.am.getHttp(appId), this.am.getPersistence(appId)); + if (continueOn) { + await app.call(AppMethod.EXECUTEPOSTROOMDELETED, data); } } } private async executePreRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { - const data = Utilities.deepClone(externalData); - - data.room = new Room(Utilities.deepFreeze(data.room), this.manager); - Utilities.deepFreeze(data.joiningUser); - - if (data.inviter) { - Utilities.deepFreeze(data.inviter); - } - for (const appId of this.listeners.get(AppInterface.IPreRoomUserJoined)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_PRE_ROOM_USER_JOINED)) { - await app.call(AppMethod.EXECUTE_PRE_ROOM_USER_JOINED, data, this.am.getReader(appId), this.am.getHttp(appId), this.am.getPersistence(appId)); - } + await app.call(AppMethod.EXECUTE_PRE_ROOM_USER_JOINED, externalData); } } private async executePostRoomUserJoined(externalData: IRoomUserJoinedContext): Promise { - const data = Utilities.deepClone(externalData); - - data.room = new Room(Utilities.deepFreeze(data.room), this.manager); - Utilities.deepFreeze(data.joiningUser); - - if (data.inviter) { - Utilities.deepFreeze(data.inviter); - } - for (const appId of this.listeners.get(AppInterface.IPostRoomUserJoined)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_ROOM_USER_JOINED)) { - await app.call( - AppMethod.EXECUTE_POST_ROOM_USER_JOINED, - data, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_ROOM_USER_JOINED, externalData); } } private async executePreRoomUserLeave(externalData: IRoomUserLeaveContext): Promise { - const data = Utilities.deepClone(externalData); - - data.room = new Room(Utilities.deepFreeze(data.room), this.manager); - Utilities.deepFreeze(data.leavingUser); - for (const appId of this.listeners.get(AppInterface.IPreRoomUserLeave)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_PRE_ROOM_USER_LEAVE)) { - await app.call(AppMethod.EXECUTE_PRE_ROOM_USER_LEAVE, data, this.am.getReader(appId), this.am.getHttp(appId), this.am.getPersistence(appId)); - } + await app.call(AppMethod.EXECUTE_PRE_ROOM_USER_LEAVE, externalData); } } + // TODO CONTINUE FROM HERE private async executePostRoomUserLeave(externalData: IRoomUserLeaveContext): Promise { const data = Utilities.deepClone(externalData); From d5109e02604a894a25220f58050359c4c80040ad Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 29 Dec 2023 12:02:29 -0300 Subject: [PATCH 3/7] Finish refactor on AppListenerManager --- deno-runtime/handlers/listener/handler.ts | 2 +- src/server/managers/AppListenerManager.ts | 388 +++------------------- 2 files changed, 48 insertions(+), 342 deletions(-) diff --git a/deno-runtime/handlers/listener/handler.ts b/deno-runtime/handlers/listener/handler.ts index 21ad917a1..6c3d3dc32 100644 --- a/deno-runtime/handlers/listener/handler.ts +++ b/deno-runtime/handlers/listener/handler.ts @@ -27,7 +27,7 @@ export default async function handleListener(method: string, params: unknown): P try { const args = parseArgs(evtInterface, params); - return await (eventExecutor as Function).apply(app, args); + return await (eventExecutor as (...args: unknown[]) => Promise).apply(app, args); } catch (e) { if (e instanceof JsonRpcError) { return e; diff --git a/src/server/managers/AppListenerManager.ts b/src/server/managers/AppListenerManager.ts index 0fe0cf34c..7d70bad06 100644 --- a/src/server/managers/AppListenerManager.ts +++ b/src/server/managers/AppListenerManager.ts @@ -32,13 +32,10 @@ import { } from '../../definition/uikit/UIKitInteractionContext'; import type { IFileUploadContext } from '../../definition/uploads/IFileUploadContext'; import type { IUser, IUserContext, IUserStatusContext, IUserUpdateContext } from '../../definition/users'; -import { MessageBuilder, MessageExtender, RoomBuilder, RoomExtender } from '../accessors'; import type { AppManager } from '../AppManager'; -import { Message } from '../messages/Message'; -import { Utilities } from '../misc/Utilities'; import type { ProxiedApp } from '../ProxiedApp'; -import { Room } from '../rooms/Room'; import type { AppAccessorManager } from './AppAccessorManager'; +import { Utilities } from '../misc/Utilities'; interface IListenerExecutor { [AppInterface.IPreMessageSentPrevent]: { @@ -490,7 +487,7 @@ export class AppListenerManager { for (const appId of this.listeners.get(AppInterface.IPreMessageSentModify)) { const app = this.manager.getOneById(appId); - const continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTMODIFY, data)) as boolean; + const continueOn = (await app.call(AppMethod.CHECKPREMESSAGESENTMODIFY, msg)) as boolean; if (continueOn) { msg = (await app.call(AppMethod.EXECUTEPREMESSAGESENTMODIFY, msg)) as IMessage; @@ -505,32 +502,31 @@ export class AppListenerManager { if (data.room.type === RoomType.DIRECT_MESSAGE && data.room.userIds.length > 1) { for (const appId of this.listeners.get(AppInterface.IPostMessageSentToBot)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTEPOSTMESSAGESENTTOBOT)) { - const reader = this.am.getReader(appId); - const bot = await reader.getUserReader().getAppUser(); - if (!bot) { - continue; - } - - // if the sender is the bot just ignore it - - if (bot.id === data.sender.id) { - continue; - } - // if the user doesnt belong to the room ignore it - if (!data.room.userIds.includes(bot.id)) { - continue; - } - - await app.call(AppMethod.EXECUTEPOSTMESSAGESENTTOBOT, data); + + const reader = this.am.getReader(appId); + const bot = await reader.getUserReader().getAppUser(); + if (!bot) { + continue; + } + + // if the sender is the bot just ignore it + + if (bot.id === data.sender.id) { + continue; + } + // if the user doesnt belong to the room ignore it + if (!data.room.userIds.includes(bot.id)) { + continue; } + + await app.call(AppMethod.EXECUTEPOSTMESSAGESENTTOBOT, data); } } for (const appId of this.listeners.get(AppInterface.IPostMessageSent)) { const app = this.manager.getOneById(appId); - const continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGESENT, data, this.am.getReader(appId), this.am.getHttp(appId))) as boolean; + const continueOn = (await app.call(AppMethod.CHECKPOSTMESSAGESENT, data)) as boolean; if (continueOn) { await app.call(AppMethod.EXECUTEPOSTMESSAGESENT, data); @@ -764,63 +760,28 @@ export class AppListenerManager { } } - // TODO CONTINUE FROM HERE private async executePostRoomUserLeave(externalData: IRoomUserLeaveContext): Promise { - const data = Utilities.deepClone(externalData); - - data.room = new Room(Utilities.deepFreeze(data.room), this.manager); - Utilities.deepFreeze(data.leavingUser); - for (const appId of this.listeners.get(AppInterface.IPostRoomUserLeave)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_ROOM_USER_LEAVE)) { - await app.call( - AppMethod.EXECUTE_POST_ROOM_USER_LEAVE, - data, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_ROOM_USER_LEAVE, externalData); } } // External Components private async executePostExternalComponentOpened(data: IExternalComponent): Promise { - const cfExternalComponent = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostExternalComponentOpened)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTEPOSTEXTERNALCOMPONENTOPENED)) { - await app.call( - AppMethod.EXECUTEPOSTEXTERNALCOMPONENTOPENED, - cfExternalComponent, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - ); - } + await app.call(AppMethod.EXECUTEPOSTEXTERNALCOMPONENTOPENED, data); } } private async executePostExternalComponentClosed(data: IExternalComponent): Promise { - const cfExternalComponent = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostExternalComponentClosed)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTEPOSTEXTERNALCOMPONENTCLOSED)) { - await app.call( - AppMethod.EXECUTEPOSTEXTERNALCOMPONENTCLOSED, - cfExternalComponent, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - ); - } + await app.call(AppMethod.EXECUTEPOSTEXTERNALCOMPONENTCLOSED, data); } } @@ -998,190 +959,75 @@ export class AppListenerManager { // Livechat private async executePostLivechatRoomStarted(data: ILivechatRoom): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatRoomStarted)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_STARTED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_ROOM_STARTED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_STARTED, data); } } private async executeLivechatRoomClosedHandler(data: ILivechatRoom): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.ILivechatRoomClosedHandler)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_LIVECHAT_ROOM_CLOSED_HANDLER)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_LIVECHAT_ROOM_CLOSED_HANDLER, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_LIVECHAT_ROOM_CLOSED_HANDLER, data); } } private async executePostLivechatRoomClosed(data: ILivechatRoom): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatRoomClosed)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_CLOSED, data); } } private async executePostLivechatAgentAssigned(data: ILivechatEventContext): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatAgentAssigned)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_AGENT_ASSIGNED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_AGENT_ASSIGNED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_AGENT_ASSIGNED, data); } } private async executePostLivechatAgentUnassigned(data: ILivechatEventContext): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatAgentUnassigned)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_AGENT_UNASSIGNED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_AGENT_UNASSIGNED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_AGENT_UNASSIGNED, data); } } private async executePostLivechatRoomTransferred(data: ILivechatTransferEventContext): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatRoomTransferred)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_TRANSFERRED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_ROOM_TRANSFERRED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_TRANSFERRED, data); } } private async executePostLivechatGuestSaved(data: IVisitor): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatGuestSaved)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_GUEST_SAVED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_GUEST_SAVED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_GUEST_SAVED, data); } } private async executePostLivechatRoomSaved(data: ILivechatRoom): Promise { - const cfLivechatRoom = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostLivechatRoomSaved)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_SAVED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_LIVECHAT_ROOM_SAVED, - cfLivechatRoom, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_LIVECHAT_ROOM_SAVED, data); } } // FileUpload private async executePreFileUpload(data: IFileUploadContext): Promise { - const context = Object.freeze(data); - for (const appId of this.listeners.get(AppInterface.IPreFileUpload)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_PRE_FILE_UPLOAD)) { - await app.call( - AppMethod.EXECUTE_PRE_FILE_UPLOAD, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_PRE_FILE_UPLOAD, data); } } @@ -1191,240 +1037,100 @@ export class AppListenerManager { for (const appId of this.listeners.get(AppInterface.IPreEmailSent)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_PRE_EMAIL_SENT)) { - descriptor = await app.call( - AppMethod.EXECUTE_PRE_EMAIL_SENT, - { - context: data.context, - email: descriptor, - }, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + descriptor = await app.call(AppMethod.EXECUTE_PRE_EMAIL_SENT, { + context: data.context, + email: descriptor, + }); } return descriptor; } private async executePostMessageReacted(data: IMessageReactionContext): Promise { - const context = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostMessageReacted)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_MESSAGE_REACTED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_MESSAGE_REACTED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_MESSAGE_REACTED, data); } } private async executePostMessageFollowed(data: IMessageFollowContext): Promise { - const context = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostMessageFollowed)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_MESSAGE_FOLLOWED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_MESSAGE_FOLLOWED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_MESSAGE_FOLLOWED, data); } } private async executePostMessagePinned(data: IMessagePinContext): Promise { - const context = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostMessagePinned)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_MESSAGE_PINNED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_MESSAGE_PINNED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_MESSAGE_PINNED, data); } } private async executePostMessageStarred(data: IMessageStarContext): Promise { - const context = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostMessageStarred)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_MESSAGE_STARRED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_MESSAGE_STARRED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_MESSAGE_STARRED, data); } } private async executePostMessageReported(data: IMessageReportContext): Promise { - const context = Utilities.deepCloneAndFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostMessageReported)) { const app = this.manager.getOneById(appId); - if (!app.hasMethod(AppMethod.EXECUTE_POST_MESSAGE_REPORTED)) { - continue; - } - - await app.call( - AppMethod.EXECUTE_POST_MESSAGE_REPORTED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); + await app.call(AppMethod.EXECUTE_POST_MESSAGE_REPORTED, data); } } private async executePostUserCreated(data: IUserContext): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserCreated)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_CREATED)) { - await app.call( - AppMethod.EXECUTE_POST_USER_CREATED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_CREATED, data); } } private async executePostUserUpdated(data: IUserUpdateContext): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserUpdated)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_UPDATED)) { - await app.call( - AppMethod.EXECUTE_POST_USER_UPDATED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_UPDATED, data); } } private async executePostUserDeleted(data: IUserContext): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserDeleted)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_DELETED)) { - await app.call( - AppMethod.EXECUTE_POST_USER_DELETED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_DELETED, data); } } private async executePostUserLoggedIn(data: IUser): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserLoggedIn)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_LOGGED_IN)) { - await app.call( - AppMethod.EXECUTE_POST_USER_LOGGED_IN, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_LOGGED_IN, data); } } private async executePostUserLoggedOut(data: IUser): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserLoggedOut)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_LOGGED_OUT)) { - await app.call( - AppMethod.EXECUTE_POST_USER_LOGGED_OUT, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_LOGGED_OUT, data); } } private async executePostUserStatusChanged(data: IUserStatusContext): Promise { - const context = Utilities.deepFreeze(data); - for (const appId of this.listeners.get(AppInterface.IPostUserStatusChanged)) { const app = this.manager.getOneById(appId); - if (app.hasMethod(AppMethod.EXECUTE_POST_USER_STATUS_CHANGED)) { - await app.call( - AppMethod.EXECUTE_POST_USER_STATUS_CHANGED, - context, - this.am.getReader(appId), - this.am.getHttp(appId), - this.am.getPersistence(appId), - this.am.getModifier(appId), - ); - } + await app.call(AppMethod.EXECUTE_POST_USER_STATUS_CHANGED, data); } } } From 39ed7576ec12928d43a9e0ea940365d0b736371a Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Fri, 29 Dec 2023 15:42:37 -0300 Subject: [PATCH 4/7] Refactor some error handling and the AppsEngineException for listeners --- deno-runtime/handlers/listener/handler.ts | 22 +++++++++++++------ deno-runtime/lib/messenger.ts | 12 +++++----- .../exceptions/AppsEngineException.ts | 2 ++ src/server/ProxiedApp.ts | 9 +++++++- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/deno-runtime/handlers/listener/handler.ts b/deno-runtime/handlers/listener/handler.ts index 6c3d3dc32..7fb79ee74 100644 --- a/deno-runtime/handlers/listener/handler.ts +++ b/deno-runtime/handlers/listener/handler.ts @@ -1,14 +1,18 @@ import { Defined, JsonRpcError } from 'jsonrpc-lite'; import type { App } from '@rocket.chat/apps-engine/definition/App.ts'; +import type { IMessage } from "@rocket.chat/apps-engine/definition/messages/IMessage.ts"; +import type { IRoom } from "@rocket.chat/apps-engine/definition/rooms/IRoom.ts"; +import type { AppsEngineException as _AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException.ts'; import { AppObjectRegistry } from '../../AppObjectRegistry.ts'; import { MessageExtender } from "../../lib/accessors/extenders/MessageExtender.ts"; -import { IMessage } from "@rocket.chat/apps-engine/definition/messages/IMessage.ts"; -import { IRoom } from "@rocket.chat/apps-engine/definition/rooms/IRoom.ts"; import { RoomExtender } from "../../lib/accessors/extenders/RoomExtender.ts"; import { MessageBuilder } from "../../lib/accessors/builders/MessageBuilder.ts"; import { RoomBuilder } from "../../lib/accessors/builders/RoomBuilder.ts"; import { AppAccessorsInstance } from "../../lib/accessors/mod.ts"; +import { require } from '../../lib/require.ts'; + +const { AppsEngineException } = require('@rocket.chat/apps-engine/definition/exceptions/AppsEgnineException') as { AppsEngineException: typeof _AppsEngineException }; export default async function handleListener(method: string, params: unknown): Promise { const [, evtInterface] = method.split(':'); @@ -18,11 +22,11 @@ export default async function handleListener(method: string, params: unknown): P const eventExecutor = app?.[evtInterface as keyof App]; if (typeof eventExecutor !== 'function') { - return new JsonRpcError('Invalid event interface called on app', -32000); + return JsonRpcError.methodNotFound({ message: 'Invalid event interface called on app' }); } if (!Array.isArray(params) || params.length < 1 || params.length > 2) { - return new JsonRpcError('Invalid params', -32602); + return JsonRpcError.invalidParams(null); } try { @@ -33,7 +37,11 @@ export default async function handleListener(method: string, params: unknown): P return e; } - return JsonRpcError.internalError(e.message); + if (e instanceof AppsEngineException) { + return new JsonRpcError(e.message, AppsEngineException.JSONRPC_ERROR_CODE, { name: e.name }); + } + + return JsonRpcError.internalError({ message: e.message }); } } @@ -45,7 +53,7 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { const [param1, param2] = params as [unknown, unknown]; if (!param1) { - throw new JsonRpcError('Invalid params', -32000); + throw JsonRpcError.invalidParams(null); } const args: unknown[] = [param1, AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp()]; @@ -91,7 +99,7 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { // This guy gets an extra one if (evtInterface === 'executePostMessageDeleted') { if (!param2) { - throw new JsonRpcError('Invalid params', -32000); + throw JsonRpcError.invalidParams(null); } args.push(param2); diff --git a/deno-runtime/lib/messenger.ts b/deno-runtime/lib/messenger.ts index 88409abf9..92941461a 100644 --- a/deno-runtime/lib/messenger.ts +++ b/deno-runtime/lib/messenger.ts @@ -1,7 +1,7 @@ import * as jsonrpc from 'jsonrpc-lite'; -import { AppObjectRegistry } from "../AppObjectRegistry.ts"; -import type { Logger } from './logger.ts' +import { AppObjectRegistry } from '../AppObjectRegistry.ts'; +import type { Logger } from './logger.ts'; export type RequestDescriptor = Pick; @@ -29,8 +29,8 @@ export function isErrorResponse(message: jsonrpc.JsonRpc): message is jsonrpc.Er const encoder = new TextEncoder(); export const RPCResponseObserver = new EventTarget(); -export const Transport = new class Transporter { - private selectedTransport: Transporter["stdoutTransport"] | Transporter["noopTransport"]; +export const Transport = new (class Transporter { + private selectedTransport: Transporter['stdoutTransport'] | Transporter['noopTransport']; constructor() { this.selectedTransport = this.stdoutTransport.bind(this); @@ -41,7 +41,7 @@ export const Transport = new class Transporter { await Deno.stdout.write(encoded); } - private async noopTransport(_message: jsonrpc.JsonRpc): Promise { } + private async noopTransport(_message: jsonrpc.JsonRpc): Promise {} public selectTransport(transport: 'stdout' | 'noop'): void { switch (transport) { @@ -57,7 +57,7 @@ export const Transport = new class Transporter { public send(message: jsonrpc.JsonRpc): Promise { return this.selectedTransport(message); } -} +})(); export function parseMessage(message: string) { const parsed = jsonrpc.parse(message); diff --git a/src/definition/exceptions/AppsEngineException.ts b/src/definition/exceptions/AppsEngineException.ts index 510b5314d..a3e802aa6 100644 --- a/src/definition/exceptions/AppsEngineException.ts +++ b/src/definition/exceptions/AppsEngineException.ts @@ -14,6 +14,8 @@ export class AppsEngineException extends Error { public name = 'AppsEngineException'; + public static JSONRPC_ERROR_CODE = -32070; + public message: string; constructor(message?: string) { diff --git a/src/server/ProxiedApp.ts b/src/server/ProxiedApp.ts index c53976b21..80f21abf0 100644 --- a/src/server/ProxiedApp.ts +++ b/src/server/ProxiedApp.ts @@ -1,5 +1,6 @@ import type { IAppAccessors, ILogger } from '../definition/accessors'; import { AppStatus } from '../definition/AppStatus'; +import { AppsEngineException } from '../definition/exceptions'; import type { IApp } from '../definition/IApp'; import type { IAppAuthorInfo, IAppInfo } from '../definition/metadata'; import { AppMethod } from '../definition/metadata'; @@ -55,7 +56,13 @@ export class ProxiedApp implements IApp { } public async call(method: `${AppMethod}`, ...args: Array): Promise { - return this.appRuntime.sendRequest({ method: `app:${method}`, params: args }); + try { + return await this.appRuntime.sendRequest({ method: `app:${method}`, params: args }); + } catch (e) { + if (e.code === AppsEngineException.JSONRPC_ERROR_CODE) { + throw new AppsEngineException(e.message); + } + } } public getStatus(): AppStatus { From d24402083f19cbaedde176fda6a43dce0e1ac5cb Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 3 Jan 2024 16:55:24 -0300 Subject: [PATCH 5/7] Refactor room object creation --- deno-runtime/handlers/listener/handler.ts | 32 ++++++++++++------- deno-runtime/handlers/slashcommand-handler.ts | 31 +++++++----------- .../tests/slashcommand-handler.test.ts | 12 ++++--- deno-runtime/lib/roomFactory.ts | 26 +++++++++++++++ 4 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 deno-runtime/lib/roomFactory.ts diff --git a/deno-runtime/handlers/listener/handler.ts b/deno-runtime/handlers/listener/handler.ts index 7fb79ee74..e85d84334 100644 --- a/deno-runtime/handlers/listener/handler.ts +++ b/deno-runtime/handlers/listener/handler.ts @@ -11,6 +11,7 @@ import { MessageBuilder } from "../../lib/accessors/builders/MessageBuilder.ts"; import { RoomBuilder } from "../../lib/accessors/builders/RoomBuilder.ts"; import { AppAccessorsInstance } from "../../lib/accessors/mod.ts"; import { require } from '../../lib/require.ts'; +import createRoom from '../../lib/roomFactory.ts'; const { AppsEngineException } = require('@rocket.chat/apps-engine/definition/exceptions/AppsEgnineException') as { AppsEngineException: typeof _AppsEngineException }; @@ -30,7 +31,7 @@ export default async function handleListener(method: string, params: unknown): P } try { - const args = parseArgs(evtInterface, params); + const args = parseArgs({ AppAccessorsInstance }, evtInterface, params); return await (eventExecutor as (...args: unknown[]) => Promise).apply(app, args); } catch (e) { if (e instanceof JsonRpcError) { @@ -45,7 +46,8 @@ export default async function handleListener(method: string, params: unknown): P } } -function parseArgs(evtInterface: string, params: unknown[]): unknown[] { +export function parseArgs(deps: { AppAccessorsInstance: AppAccessors }, evtMethod: string, params: unknown[]): unknown[] { + const { AppAccessorsInstance } = deps; /** * param1 is the context for the event handler execution * param2 is an optional extra content that some hanlers require @@ -56,10 +58,18 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { throw JsonRpcError.invalidParams(null); } - const args: unknown[] = [param1, AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp()]; + let context = param1; + + if (evtMethod.endsWith('RoomUserJoined') || evtMethod.endsWith('RoomUserLeave')) { + (context as Record).room = createRoom((context as Record).room as IRoom, AppAccessorsInstance.getSenderFn()); + } else if (evtMethod.includes('PreRoom')) { + context = createRoom(context as IRoom, AppAccessorsInstance.getSenderFn()); + } + + const args: unknown[] = [context, AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp()]; // "check" events will only go this far - (context, reader, http) - if (evtInterface.startsWith('check')) { + if (evtMethod.startsWith('check')) { // "checkPostMessageDeleted" has an extra param - (context, reader, http, extraContext) if (param2) { args.push(param2); @@ -72,10 +82,10 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { args.push(AppAccessorsInstance.getPersistence()); // "extend" events have an additional "Extender" param - (context, extender, reader, http, persistence) - if (evtInterface.endsWith('Extend')) { - if (evtInterface.includes('Message')) { + if (evtMethod.endsWith('Extend')) { + if (evtMethod.includes('Message')) { args.splice(1, 0, new MessageExtender(param1 as IMessage)); - } else if (evtInterface.includes('Room')) { + } else if (evtMethod.includes('Room')) { args.splice(1, 0, new RoomExtender(param1 as IRoom)); } @@ -83,10 +93,10 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { } // "Modify" events have an additional "Builder" param - (context, builder, reader, http, persistence) - if (evtInterface.endsWith('Modify')) { - if (evtInterface.includes('Message')) { + if (evtMethod.endsWith('Modify')) { + if (evtMethod.includes('Message')) { args.splice(1, 0, new MessageBuilder(param1 as IMessage)); - } else if (evtInterface.includes('Room')) { + } else if (evtMethod.includes('Room')) { args.splice(1, 0, new RoomBuilder(param1 as IRoom)); } @@ -97,7 +107,7 @@ function parseArgs(evtInterface: string, params: unknown[]): unknown[] { args.push(AppAccessorsInstance.getModifier()); // This guy gets an extra one - if (evtInterface === 'executePostMessageDeleted') { + if (evtMethod === 'executePostMessageDeleted') { if (!param2) { throw JsonRpcError.invalidParams(null); } diff --git a/deno-runtime/handlers/slashcommand-handler.ts b/deno-runtime/handlers/slashcommand-handler.ts index 0609d7fbb..5422e1e58 100644 --- a/deno-runtime/handlers/slashcommand-handler.ts +++ b/deno-runtime/handlers/slashcommand-handler.ts @@ -1,28 +1,19 @@ -import { ISlashCommand } from '@rocket.chat/apps-engine/definition/slashcommands/ISlashCommand.ts'; +import { Defined, JsonRpcError } from 'jsonrpc-lite'; + +import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom.ts'; +import type { ISlashCommand } from '@rocket.chat/apps-engine/definition/slashcommands/ISlashCommand.ts'; import { SlashCommandContext as _SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands/SlashCommandContext.ts'; import { Room as _Room } from '@rocket.chat/apps-engine/server/rooms/Room.ts'; import { AppObjectRegistry } from '../AppObjectRegistry.ts'; -import { require } from '../lib/require.ts'; import { AppAccessors, AppAccessorsInstance } from '../lib/accessors/mod.ts'; -import { Defined, JsonRpcError } from "jsonrpc-lite"; +import { require } from '../lib/require.ts'; +import createRoom from '../lib/roomFactory.ts'; // For some reason Deno couldn't understand the typecast to the original interfaces and said it wasn't a constructor type -const { SlashCommandContext } = require('@rocket.chat/apps-engine/definition/slashcommands/SlashCommandContext.js') as { SlashCommandContext: typeof _SlashCommandContext }; -const { Room } = require('@rocket.chat/apps-engine/server/rooms/Room.js') as { Room: typeof _Room } ; - -const getMockAppManager = (senderFn: AppAccessors['senderFn']) => ({ - getBridges: () => ({ - getInternalBridge: () => ({ - doGetUsernamesOfRoomById: (roomId: string) => { - senderFn({ - method: 'bridges:getInternalBridge:doGetUsernamesOfRoomById', - params: [roomId], - }); - }, - }), - }), -}); +const { SlashCommandContext } = require('@rocket.chat/apps-engine/definition/slashcommands/SlashCommandContext.js') as { + SlashCommandContext: typeof _SlashCommandContext; +}; export default async function slashCommandHandler(call: string, params: unknown): Promise { const [, commandName, method] = call.split(':'); @@ -71,7 +62,7 @@ export function handleExecutor(deps: { AppAccessorsInstance: AppAccessors }, com const context = new SlashCommandContext( sender as _SlashCommandContext['sender'], - new Room(room, getMockAppManager(deps.AppAccessorsInstance.getSenderFn())), + createRoom(room as IRoom, deps.AppAccessorsInstance.getSenderFn()), args as _SlashCommandContext['params'], threadId as _SlashCommandContext['threadId'], triggerId as _SlashCommandContext['triggerId'], @@ -104,7 +95,7 @@ export function handlePreviewItem(deps: { AppAccessorsInstance: AppAccessors }, const context = new SlashCommandContext( sender as _SlashCommandContext['sender'], - new Room(room, getMockAppManager(deps.AppAccessorsInstance.getSenderFn())), + createRoom(room as IRoom, deps.AppAccessorsInstance.getSenderFn()), args as _SlashCommandContext['params'], threadId as _SlashCommandContext['threadId'], triggerId as _SlashCommandContext['triggerId'], diff --git a/deno-runtime/handlers/tests/slashcommand-handler.test.ts b/deno-runtime/handlers/tests/slashcommand-handler.test.ts index 40fb5a397..f466ad32d 100644 --- a/deno-runtime/handlers/tests/slashcommand-handler.test.ts +++ b/deno-runtime/handlers/tests/slashcommand-handler.test.ts @@ -1,7 +1,7 @@ // deno-lint-ignore-file no-explicit-any -import { assertInstanceOf, assertEquals } from 'https://deno.land/std@0.203.0/assert/mod.ts'; +import { assertEquals, assertInstanceOf } from 'https://deno.land/std@0.203.0/assert/mod.ts'; import { beforeEach, describe, it } from 'https://deno.land/std@0.203.0/testing/bdd.ts'; -import { spy } from "https://deno.land/std@0.203.0/testing/mock.ts"; +import { spy } from 'https://deno.land/std@0.203.0/testing/mock.ts'; import { Room as _Room } from '@rocket.chat/apps-engine/server/rooms/Room.ts'; import { AppObjectRegistry } from '../../AppObjectRegistry.ts'; @@ -9,7 +9,9 @@ import { AppAccessors } from '../../lib/accessors/mod.ts'; import { handleExecutor, handlePreviewItem } from '../slashcommand-handler.ts'; import { require } from '../../lib/require.ts'; -const { Room } = require('@rocket.chat/apps-engine/server/rooms/Room.js') as { Room: typeof _Room } ; +const { Room } = require('@rocket.chat/apps-engine/server/rooms/Room.js') as { + Room: typeof _Room; +}; describe('handlers > slashcommand', () => { const mockAppAccessors = { @@ -39,7 +41,7 @@ describe('handlers > slashcommand', () => { // deno-lint-ignore no-unused-vars async previewer(context: any, read: any, modify: any, http: any, persis: any): Promise {}, // deno-lint-ignore no-unused-vars - async executePreviewItem(previewItem: any,context: any, read: any, modify: any, http: any, persis: any): Promise {}, + async executePreviewItem(previewItem: any, context: any, read: any, modify: any, http: any, persis: any): Promise {}, }; const mockCommandPreviewWithNoExecutor = { @@ -50,7 +52,7 @@ describe('handlers > slashcommand', () => { // deno-lint-ignore no-unused-vars async previewer(context: any, read: any, modify: any, http: any, persis: any): Promise {}, // deno-lint-ignore no-unused-vars - async executePreviewItem(previewItem: any,context: any, read: any, modify: any, http: any, persis: any): Promise {}, + async executePreviewItem(previewItem: any, context: any, read: any, modify: any, http: any, persis: any): Promise {}, }; beforeEach(() => { diff --git a/deno-runtime/lib/roomFactory.ts b/deno-runtime/lib/roomFactory.ts new file mode 100644 index 000000000..7bc97bbb1 --- /dev/null +++ b/deno-runtime/lib/roomFactory.ts @@ -0,0 +1,26 @@ +import type { IRoom } from "@rocket.chat/apps-engine/definition/rooms/IRoom.ts"; +import type { Room as _Room } from "@rocket.chat/apps-engine/server/rooms/Room.ts"; + +import { require } from '../lib/require.ts'; +import { AppAccessors } from "./accessors/mod.ts"; + +export const { Room } = require('@rocket.chat/apps-engine/server/rooms/Room.js') as { Room: typeof _Room } ; + +const getMockAppManager = (senderFn: AppAccessors['senderFn']) => ({ + getBridges: () => ({ + getInternalBridge: () => ({ + doGetUsernamesOfRoomById: (roomId: string) => { + senderFn({ + method: 'bridges:getInternalBridge:doGetUsernamesOfRoomById', + params: [roomId], + }); + }, + }), + }), +}); + +export default function createRoom(room: IRoom, senderFn: AppAccessors['senderFn']) { + const mockAppManager = getMockAppManager(senderFn); + + return new Room(room, mockAppManager); +} From f47c201ca36faf404b8a4720b941d316fe548a7d Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 3 Jan 2024 16:56:30 -0300 Subject: [PATCH 6/7] Test listener handler and formatting --- deno-runtime/handlers/listener/handler.ts | 22 ++- .../handlers/tests/listener-handler.test.ts | 181 ++++++++++++++++++ 2 files changed, 194 insertions(+), 9 deletions(-) create mode 100644 deno-runtime/handlers/tests/listener-handler.test.ts diff --git a/deno-runtime/handlers/listener/handler.ts b/deno-runtime/handlers/listener/handler.ts index e85d84334..392f9ab48 100644 --- a/deno-runtime/handlers/listener/handler.ts +++ b/deno-runtime/handlers/listener/handler.ts @@ -1,19 +1,21 @@ import { Defined, JsonRpcError } from 'jsonrpc-lite'; import type { App } from '@rocket.chat/apps-engine/definition/App.ts'; -import type { IMessage } from "@rocket.chat/apps-engine/definition/messages/IMessage.ts"; -import type { IRoom } from "@rocket.chat/apps-engine/definition/rooms/IRoom.ts"; +import type { IMessage } from '@rocket.chat/apps-engine/definition/messages/IMessage.ts'; +import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms/IRoom.ts'; import type { AppsEngineException as _AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException.ts'; import { AppObjectRegistry } from '../../AppObjectRegistry.ts'; -import { MessageExtender } from "../../lib/accessors/extenders/MessageExtender.ts"; -import { RoomExtender } from "../../lib/accessors/extenders/RoomExtender.ts"; -import { MessageBuilder } from "../../lib/accessors/builders/MessageBuilder.ts"; -import { RoomBuilder } from "../../lib/accessors/builders/RoomBuilder.ts"; -import { AppAccessorsInstance } from "../../lib/accessors/mod.ts"; +import { MessageExtender } from '../../lib/accessors/extenders/MessageExtender.ts'; +import { RoomExtender } from '../../lib/accessors/extenders/RoomExtender.ts'; +import { MessageBuilder } from '../../lib/accessors/builders/MessageBuilder.ts'; +import { RoomBuilder } from '../../lib/accessors/builders/RoomBuilder.ts'; +import { AppAccessors, AppAccessorsInstance } from '../../lib/accessors/mod.ts'; import { require } from '../../lib/require.ts'; import createRoom from '../../lib/roomFactory.ts'; -const { AppsEngineException } = require('@rocket.chat/apps-engine/definition/exceptions/AppsEgnineException') as { AppsEngineException: typeof _AppsEngineException }; +const { AppsEngineException } = require('@rocket.chat/apps-engine/definition/exceptions/AppsEngineException.js') as { + AppsEngineException: typeof _AppsEngineException; +}; export default async function handleListener(method: string, params: unknown): Promise { const [, evtInterface] = method.split(':'); @@ -23,7 +25,9 @@ export default async function handleListener(method: string, params: unknown): P const eventExecutor = app?.[evtInterface as keyof App]; if (typeof eventExecutor !== 'function') { - return JsonRpcError.methodNotFound({ message: 'Invalid event interface called on app' }); + return JsonRpcError.methodNotFound({ + message: 'Invalid event interface called on app', + }); } if (!Array.isArray(params) || params.length < 1 || params.length > 2) { diff --git a/deno-runtime/handlers/tests/listener-handler.test.ts b/deno-runtime/handlers/tests/listener-handler.test.ts new file mode 100644 index 000000000..9401d62e8 --- /dev/null +++ b/deno-runtime/handlers/tests/listener-handler.test.ts @@ -0,0 +1,181 @@ +// deno-lint-ignore-file no-explicit-any +import { assertEquals, assertInstanceOf, assertObjectMatch } from 'https://deno.land/std@0.203.0/assert/mod.ts'; +import { describe, it } from 'https://deno.land/std@0.203.0/testing/bdd.ts'; + +import { parseArgs } from '../listener/handler.ts'; +import { AppAccessors } from '../../lib/accessors/mod.ts'; +import { Room } from '../../lib/roomFactory.ts'; +import { MessageExtender } from '../../lib/accessors/extenders/MessageExtender.ts'; +import { RoomExtender } from '../../lib/accessors/extenders/RoomExtender.ts'; +import { MessageBuilder } from '../../lib/accessors/builders/MessageBuilder.ts'; +import { RoomBuilder } from '../../lib/accessors/builders/RoomBuilder.ts'; + +describe('handlers > listeners', () => { + const mockAppAccessors = { + getReader: () => ({ __type: 'reader' }), + getHttp: () => ({ __type: 'http' }), + getModifier: () => ({ __type: 'modifier' }), + getPersistence: () => ({ __type: 'persistence' }), + getSenderFn: () => (id: string) => Promise.resolve([{ __type: 'bridgeCall' }, { id }]), + } as unknown as AppAccessors; + + it('correctly parses the arguments for a request to trigger the "checkPreMessageSentPrevent" method', () => { + const evtMethod = 'checkPreMessageSentPrevent'; + // For the 'checkPreMessageSentPrevent' method, the context will be a message in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 3); + assertEquals(params[0], { __type: 'context' }); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + }); + + it('correctly parses the arguments for a request to trigger the "checkPostMessageDeleted" method', () => { + const evtMethod = 'checkPostMessageDeleted'; + // For the 'checkPostMessageDeleted' method, the context will be a message in a real scenario, + // and the extraContext will provide further information such the user who deleted the message + const evtArgs = [{ __type: 'context' }, { __type: 'extraContext' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 4); + assertEquals(params[0], { __type: 'context' }); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + assertEquals(params[3], { __type: 'extraContext' }); + }); + + it('correctly parses the arguments for a request to trigger the "checkPreRoomCreateExtend" method', () => { + const evtMethod = 'checkPreRoomCreateExtend'; + // For the 'checkPreRoomCreateExtend' method, the context will be a room in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 3); + + assertInstanceOf(params[0], Room); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePreMessageSentExtend" method', () => { + const evtMethod = 'executePreMessageSentExtend'; + // For the 'executePreMessageSentExtend' method, the context will be a message in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + // Instantiating the MessageExtender might modify the original object, so we need to assert it matches instead of equals + assertObjectMatch(params[0] as Record, { + __type: 'context', + }); + assertInstanceOf(params[1], MessageExtender); + assertEquals(params[2], { __type: 'reader' }); + assertEquals(params[3], { __type: 'http' }); + assertEquals(params[4], { __type: 'persistence' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePreRoomCreateExtend" method', () => { + const evtMethod = 'executePreRoomCreateExtend'; + // For the 'executePreRoomCreateExtend' method, the context will be a room in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + // Instantiating the RoomExtender might modify the original object, so we need to assert it matches instead of equals + assertObjectMatch(params[0] as Record, { + __type: 'context', + }); + assertInstanceOf(params[1], RoomExtender); + assertEquals(params[2], { __type: 'reader' }); + assertEquals(params[3], { __type: 'http' }); + assertEquals(params[4], { __type: 'persistence' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePreMessageSentModify" method', () => { + const evtMethod = 'executePreMessageSentModify'; + // For the 'executePreMessageSentModify' method, the context will be a message in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + // Instantiating the MessageBuilder might modify the original object, so we need to assert it matches instead of equals + assertObjectMatch(params[0] as Record, { + __type: 'context', + }); + assertInstanceOf(params[1], MessageBuilder); + assertEquals(params[2], { __type: 'reader' }); + assertEquals(params[3], { __type: 'http' }); + assertEquals(params[4], { __type: 'persistence' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePreRoomCreateModify" method', () => { + const evtMethod = 'executePreRoomCreateModify'; + // For the 'executePreRoomCreateModify' method, the context will be a room in a real scenario + const evtArgs = [{ __type: 'context' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + // Instantiating the RoomBuilder might modify the original object, so we need to assert it matches instead of equals + assertObjectMatch(params[0] as Record, { + __type: 'context', + }); + assertInstanceOf(params[1], RoomBuilder); + assertEquals(params[2], { __type: 'reader' }); + assertEquals(params[3], { __type: 'http' }); + assertEquals(params[4], { __type: 'persistence' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePostRoomUserJoined" method', () => { + const evtMethod = 'executePostRoomUserJoined'; + // For the 'executePostRoomUserJoined' method, the context will be a room in a real scenario + const evtArgs = [{ __type: 'context', room: { __type: 'room' } }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + assertInstanceOf((params[0] as any).room, Room); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + assertEquals(params[3], { __type: 'persistence' }); + assertEquals(params[4], { __type: 'modifier' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePostRoomUserLeave" method', () => { + const evtMethod = 'executePostRoomUserLeave'; + // For the 'executePostRoomUserLeave' method, the context will be a room in a real scenario + const evtArgs = [{ __type: 'context', room: { __type: 'room' } }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 5); + assertInstanceOf((params[0] as any).room, Room); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + assertEquals(params[3], { __type: 'persistence' }); + assertEquals(params[4], { __type: 'modifier' }); + }); + + it('correctly parses the arguments for a request to trigger the "executePostMessageDeleted" method', () => { + const evtMethod = 'executePostMessageDeleted'; + // For the 'executePostMessageDeleted' method, the context will be a message in a real scenario + const evtArgs = [{ __type: 'context' }, { __type: 'extraContext' }]; + + const params = parseArgs({ AppAccessorsInstance: mockAppAccessors }, evtMethod, evtArgs); + + assertEquals(params.length, 6); + assertEquals(params[0], { __type: 'context' }); + assertEquals(params[1], { __type: 'reader' }); + assertEquals(params[2], { __type: 'http' }); + assertEquals(params[3], { __type: 'persistence' }); + assertEquals(params[4], { __type: 'modifier' }); + assertEquals(params[5], { __type: 'extraContext' }); + }); +}); From 51af9924f9b595a12027ece2ce1393c222428b38 Mon Sep 17 00:00:00 2001 From: Douglas Gubert Date: Wed, 3 Jan 2024 20:12:11 -0300 Subject: [PATCH 7/7] Actually call the handler function :D --- deno-runtime/handlers/app/handler.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/deno-runtime/handlers/app/handler.ts b/deno-runtime/handlers/app/handler.ts index d94616f08..2ba6202ce 100644 --- a/deno-runtime/handlers/app/handler.ts +++ b/deno-runtime/handlers/app/handler.ts @@ -10,10 +10,15 @@ import handleOnDisable from './handleOnDisable.ts'; import handleOnUninstall from './handleOnUninstall.ts'; import handleOnPreSettingUpdate from './handleOnPreSettingUpdate.ts'; import handleOnSettingUpdated from './handleOnSettingUpdated.ts'; +import handleListener from "../listener/handler.ts"; export default async function handleApp(method: string, params: unknown): Promise { const [, appMethod] = method.split(':'); + if (appMethod.startsWith('check') || appMethod.startsWith('execute')) { + return handleListener(method, params); + } + try { switch (appMethod) { case 'construct':