Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
refactor: bind executions to app proxy with correct context
  • Loading branch information
d-gubert committed Feb 18, 2026
commit 4af43de6717b987b975472bd8b574f7d1909b748
12 changes: 6 additions & 6 deletions packages/apps-engine/deno-runtime/handlers/api-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { IApiEndpoint } from '@rocket.chat/apps-engine/definition/api/IApiE
import { Defined, JsonRpcError } from 'jsonrpc-lite';

import { AppObjectRegistry } from '../AppObjectRegistry.ts';
import { Logger } from '../lib/logger.ts';
import { AppAccessorsInstance } from '../lib/accessors/mod.ts';
import { RequestContext } from '../lib/requestContext.ts';
import { wrapComposedApp } from '../lib/wrapAppForRequest.ts';

export default async function apiHandler(request: RequestContext): Promise<JsonRpcError | Defined> {
const { method: call, params } = request;
Expand All @@ -13,7 +13,7 @@ export default async function apiHandler(request: RequestContext): Promise<JsonR
const path = parts.join(':');

const endpoint = AppObjectRegistry.get<IApiEndpoint>(`api:${path}`);
const logger = AppObjectRegistry.get<Logger>('logger');
const { logger } = request.context;

if (!endpoint) {
return new JsonRpcError(`Endpoint ${path} not found`, -32000);
Expand All @@ -27,11 +27,11 @@ export default async function apiHandler(request: RequestContext): Promise<JsonR

const [requestData, endpointInfo] = params as Array<unknown>;

logger?.debug(`${path}'s ${call} is being executed...`, requestData);
logger.debug(`${path}'s ${call} is being executed...`, requestData);

try {
// deno-lint-ignore ban-types
const result = await (method as Function).apply(endpoint, [
const result = await (method as Function).apply(wrapComposedApp(endpoint, request), [
requestData,
endpointInfo,
AppAccessorsInstance.getReader(),
Expand All @@ -40,11 +40,11 @@ export default async function apiHandler(request: RequestContext): Promise<JsonR
AppAccessorsInstance.getPersistence(),
]);

logger?.debug(`${path}'s ${call} was successfully executed.`);
logger.debug(`${path}'s ${call} was successfully executed.`);

return result;
} catch (e) {
logger?.debug(`${path}'s ${call} was unsuccessful.`);
logger.debug(`${path}'s ${call} was unsuccessful.`);
return new JsonRpcError(e.message || 'Internal server error', -32000);
Comment thread
d-gubert marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,8 @@ export default async function handleConstructApp(request: RequestContext): Promi
// Applying the correct type here is quite difficult because of the dynamic nature of the code
// deno-lint-ignore no-explicit-any
const appClass = Object.values(exports)[0] as any;
const logger = AppObjectRegistry.get('logger');

const app = new appClass(appPackage.info, logger, AppAccessorsInstance.getDefaultAppAccessors());
const app = new appClass(appPackage.info, request.context.logger, AppAccessorsInstance.getDefaultAppAccessors());

if (typeof app.getName !== 'function') {
throw new Error('App must contain a getName function');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleInitialize(_request: RequestContext): Promise<boolean> {
export default async function handleInitialize(request: RequestContext): Promise<boolean> {
const app = AppObjectRegistry.get<App>('app');

if (typeof app?.initialize !== 'function') {
Expand All @@ -13,7 +14,11 @@ export default async function handleInitialize(_request: RequestContext): Promis
});
}

await app.initialize(AppAccessorsInstance.getConfigurationExtend(), AppAccessorsInstance.getEnvironmentRead());
await app.initialize.call(
wrapAppForRequest(app, request),
AppAccessorsInstance.getConfigurationExtend(),
AppAccessorsInstance.getEnvironmentRead()
);

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleOnDisable(_request: RequestContext): Promise<boolean> {
export default async function handleOnDisable(request: RequestContext): Promise<boolean> {
const app = AppObjectRegistry.get<App>('app');

if (typeof app?.onDisable !== 'function') {
Expand All @@ -13,7 +14,7 @@ export default async function handleOnDisable(_request: RequestContext): Promise
});
}

await app.onDisable(AppAccessorsInstance.getConfigurationModify());
await app.onDisable.call(wrapAppForRequest(app, request), AppAccessorsInstance.getConfigurationModify());

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default function handleOnEnable(_request: RequestContext): Promise<boolean> {
export default function handleOnEnable(request: RequestContext): Promise<boolean> {
const app = AppObjectRegistry.get<App>('app');

if (typeof app?.onEnable !== 'function') {
Expand All @@ -13,5 +14,9 @@ export default function handleOnEnable(_request: RequestContext): Promise<boolea
});
}

return app.onEnable(AppAccessorsInstance.getEnvironmentRead(), AppAccessorsInstance.getConfigurationModify());
return app.onEnable.call(
wrapAppForRequest(app, request),
AppAccessorsInstance.getEnvironmentRead(),
AppAccessorsInstance.getConfigurationModify()
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleOnInstall(request: RequestContext): Promise<boolean> {
const { params } = request;
Expand All @@ -20,7 +21,8 @@ export default async function handleOnInstall(request: RequestContext): Promise<

const [context] = params as [Record<string, unknown>];

await app.onInstall(
await app.onInstall.call(
wrapAppForRequest(app, request),
context,
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default function handleOnPreSettingUpdate(request: RequestContext): Promise<object> {
const { params } = request;
Expand All @@ -20,5 +21,11 @@ export default function handleOnPreSettingUpdate(request: RequestContext): Promi

const [setting] = params as [Record<string, unknown>];

return app.onPreSettingUpdate(setting, AppAccessorsInstance.getConfigurationModify(), AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp());
return app.onPreSettingUpdate.call(
wrapAppForRequest(app, request),
setting,
AppAccessorsInstance.getConfigurationModify(),
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleOnSettingUpdated(request: RequestContext): Promise<boolean> {
const { params } = request;
Expand All @@ -20,7 +21,13 @@ export default async function handleOnSettingUpdated(request: RequestContext): P

const [setting] = params as [Record<string, unknown>];

await app.onSettingUpdated(setting, AppAccessorsInstance.getConfigurationModify(), AppAccessorsInstance.getReader(), AppAccessorsInstance.getHttp());
await app.onSettingUpdated.call(
wrapAppForRequest(app, request),
setting,
AppAccessorsInstance.getConfigurationModify(),
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
);

return true;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleOnUninstall(request: RequestContext): Promise<boolean> {
const { params } = request;
Expand All @@ -20,7 +21,8 @@ export default async function handleOnUninstall(request: RequestContext): Promis

const [context] = params as [Record<string, unknown>];

await app.onUninstall(
await app.onUninstall.call(
wrapAppForRequest(app, request),
context,
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export default async function handleOnUpdate(request: RequestContext): Promise<boolean> {
const { params } = request;
Expand All @@ -20,7 +21,8 @@ export default async function handleOnUpdate(request: RequestContext): Promise<b

const [context] = params as [Record<string, unknown>];

await app.onUpdate(
await app.onUpdate.call(
wrapAppForRequest(app, request),
context,
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { AppStatus as _AppStatus } from '@rocket.chat/apps-engine/definitio
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { require } from '../../lib/require.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

const { AppStatus } = require('@rocket.chat/apps-engine/definition/AppStatus.js') as {
AppStatus: typeof _AppStatus;
Expand All @@ -26,7 +27,7 @@ export default async function handleSetStatus(request: RequestContext): Promise<
});
}

await app['setStatus'](status);
await app['setStatus'].call(wrapAppForRequest(app, request), status);

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { assertAppAvailable, assertHandlerFunction, isRecord } from '../lib/assertions.ts';
import { AppAccessorsInstance } from '../../lib/accessors/mod.ts';
import { RequestContext } from '../../lib/requestContext.ts';
import { wrapAppForRequest } from '../../lib/wrapAppForRequest.ts';

export const uploadEvents = ['executePreFileUpload'] as const;

Expand Down Expand Up @@ -53,7 +54,7 @@ export default async function handleUploadEvents(request: RequestContext): Promi
}

return await handlerFunction.call(
app,
wrapAppForRequest(app, request),
context,
AppAccessorsInstance.getReader(),
AppAccessorsInstance.getHttp(),
Expand Down
64 changes: 26 additions & 38 deletions packages/apps-engine/deno-runtime/handlers/app/handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { App } from '@rocket.chat/apps-engine/definition/App.ts';
import { Defined, JsonRpcError } from 'jsonrpc-lite';

import handleConstructApp from './construct.ts';
Expand All @@ -13,14 +12,14 @@ import handleOnPreSettingUpdate from './handleOnPreSettingUpdate.ts';
import handleOnSettingUpdated from './handleOnSettingUpdated.ts';
import handleOnUpdate from './handleOnUpdate.ts';
import handleUploadEvents, { uploadEvents } from './handleUploadEvents.ts';
import { isOneOf } from '../lib/assertions.ts';
import handleListener from '../listener/handler.ts';
import handleUIKitInteraction, { uikitInteractions } from '../uikit/handler.ts';
import { isOneOf } from '../lib/assertions.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { RequestContext } from '../../lib/requestContext.ts';

export default async function handleApp(request: RequestContext): Promise<Defined | JsonRpcError> {
const { method } = request;
const { logger } = request.context;
const [, appMethod] = method.split(':');

try {
Expand All @@ -29,21 +28,18 @@ export default async function handleApp(request: RequestContext): Promise<Define
return await handleGetStatus();
}

// `app` will be undefined if the method here is "app:construct"
const app = AppObjectRegistry.get<App>('app');

app?.getLogger().debug({ msg: `A method is being called...`, appMethod });
logger.debug({ msg: `A method is being called...`, appMethod });

const formatResult = (result: Defined | JsonRpcError): Defined | JsonRpcError => {
if (result instanceof JsonRpcError) {
app?.getLogger().debug({
logger.debug({
msg: `'${appMethod}' was unsuccessful.`,
appMethod,
err: result,
errorMessage: result.message,
});
} else {
app?.getLogger().debug({
logger.debug({
msg: `'${appMethod}' was successfully called! The result is:`,
appMethod,
result,
Expand All @@ -53,62 +49,54 @@ export default async function handleApp(request: RequestContext): Promise<Define
return result;
};

if (app && isOneOf(appMethod, uploadEvents)) {
return handleUploadEvents(request).then(formatResult);
}

if (app && isOneOf(appMethod, uikitInteractions)) {
return handleUIKitInteraction(request).then(formatResult);
}
let result: Promise<Defined | JsonRpcError> | undefined = undefined;

if (app && (appMethod.startsWith('check') || appMethod.startsWith('execute'))) {
return handleListener(request).then(formatResult);
if (isOneOf(appMethod, uploadEvents)) {
result = handleUploadEvents(request);
} else if (isOneOf(appMethod, uikitInteractions)) {
result = handleUIKitInteraction(request);
} else if (appMethod.startsWith('check') || appMethod.startsWith('execute')) {
result = handleListener(request);
}
Comment thread
d-gubert marked this conversation as resolved.

let result: Defined | JsonRpcError;

switch (appMethod) {
case 'construct':
result = await handleConstructApp(request);
result = handleConstructApp(request);
break;
case 'initialize':
result = await handleInitialize(request);
result = handleInitialize(request);
break;
case 'setStatus':
result = await handleSetStatus(request);
result = handleSetStatus(request);
break;
case 'onEnable':
result = await handleOnEnable(request);
result = handleOnEnable(request);
break;
case 'onDisable':
result = await handleOnDisable(request);
result = handleOnDisable(request);
break;
case 'onInstall':
result = await handleOnInstall(request);
result = handleOnInstall(request);
break;
case 'onUninstall':
result = await handleOnUninstall(request);
result = handleOnUninstall(request);
break;
case 'onPreSettingUpdate':
result = await handleOnPreSettingUpdate(request);
result = handleOnPreSettingUpdate(request);
break;
case 'onSettingUpdated':
result = await handleOnSettingUpdated(request);
result = handleOnSettingUpdated(request);
break;
case 'onUpdate':
result = await handleOnUpdate(request);
result = handleOnUpdate(request);
break;
default:
throw new JsonRpcError('Method not found', -32601);
}

app?.getLogger().debug({
msg: `'${appMethod}' was successfully called! The result is:`,
appMethod,
result,
});
if (typeof result === 'undefined') {
throw new JsonRpcError(`Unknown method "${appMethod}"`, -32601);
}

return result;
return await result.then(formatResult);
Comment thread
d-gubert marked this conversation as resolved.
} catch (e: unknown) {
if (!(e instanceof Error)) {
return new JsonRpcError('Unknown error', -32000, e);
Expand Down
Loading