Skip to content

Commit

Permalink
Adapted config accessors
Browse files Browse the repository at this point in the history
  • Loading branch information
d-gubert committed Nov 10, 2023
1 parent c10c691 commit 534a514
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 36 deletions.
8 changes: 4 additions & 4 deletions deno-runtime/lib/accessors/_test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { beforeEach, describe, it } from 'https://deno.land/[email protected]/testing/bdd.ts';
import { assertEquals } from "https://deno.land/[email protected]/assert/assert_equals.ts";

import { AppAccessors, getProxify } from "./mod.ts";
import { AppAccessors } from "./mod.ts";
import { AppObjectRegistry } from "../../AppObjectRegistry.ts";

describe('AppAccessors', () => {
let appAccessors: AppAccessors;
const proxify = getProxify((r) => Promise.resolve({
const senderFn = (r: object) => Promise.resolve({
id: Math.random().toString(36).substring(2),
jsonrpc: '2.0',
result: r,
serialize() {
return JSON.stringify(this);
}
}));
});

beforeEach(() => {
appAccessors = new AppAccessors(proxify);
appAccessors = new AppAccessors(senderFn);
AppObjectRegistry.clear();
});

Expand Down
64 changes: 41 additions & 23 deletions deno-runtime/lib/accessors/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,9 @@ import type { IApi } from '@rocket.chat/apps-engine/definition/api/IApi.ts';
import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders/IVideoConfProvider.ts';

import * as Messenger from '../messenger.ts';
import { AppObjectRegistry } from "../../AppObjectRegistry.ts";

export const getProxify = (call: typeof Messenger.sendRequest) =>
function proxify<T>(namespace: string): T {
return new Proxy(
{ __kind: namespace }, // debugging purposes
{
get:
(_target: unknown, prop: string) =>
(...params: unknown[]) =>
call({
method: `accessor:${namespace}:${prop}`,
params,
}),
},
) as T;
};
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';

const httpMethods = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'] as const;

export class AppAccessors {
private defaultAppAccessors?: IAppAccessors;
Expand All @@ -43,7 +29,23 @@ export class AppAccessors {
private persistence?: IPersistence;
private http?: IHttp;

constructor(private readonly proxify: <T>(n: string) => T) {}
private proxify: <T>(namespace: string) => T;

constructor(senderFn: typeof Messenger.sendRequest) {
this.proxify = <T>(namespace: string): T =>
new Proxy(
{ __kind: namespace },
{
get:
(_target: unknown, prop: string) =>
(...params: unknown[]) =>
senderFn({
method: `accessor:${namespace}:${prop}`,
params,
}),
},
) as T;
}

public getEnvironmentRead(): IEnvironmentRead {
if (!this.environmentRead) {
Expand Down Expand Up @@ -72,7 +74,21 @@ export class AppAccessors {
if (!this.configModifier) {
this.configModifier = {
scheduler: this.proxify('getConfigurationModify:scheduler'),
slashCommands: this.proxify('getConfigurationModify:slashCommands'),
slashCommands: {
_proxy: this.proxify('getConfigurationModify:slashCommands'),
modifySlashCommand(slashcommand: ISlashCommand) {
// Store the slashcommand instance to use when the Apps-Engine calls the slashcommand
AppObjectRegistry.set(`slashcommand:${slashcommand.command}`, slashcommand);

return this._proxy.modifySlashCommand(slashcommand);
},
disableSlashCommand(command: string) {
return this._proxy.disableSlashCommand(command);
},
enableSlashCommand(command: string) {
return this._proxy.enableSlashCommand(command);
},
},
serverSettings: this.proxify('getConfigurationModify:serverSettings'),
};
}
Expand All @@ -92,6 +108,8 @@ export class AppAccessors {
provideApi(api: IApi) {
api.endpoints.forEach((endpoint) => {
AppObjectRegistry.set(`api:${endpoint.path}`, endpoint);

endpoint._availableMethods = httpMethods.filter((method) => typeof endpoint[method] === 'function');
});

return this._proxy.provideApi(api);
Expand Down Expand Up @@ -124,8 +142,8 @@ export class AppAccessors {
AppObjectRegistry.set(`slashcommand:${slashcommand.command}`, slashcommand);

return this._proxy.provideSlashCommand(slashcommand);
}
}
},
},
};
}

Expand All @@ -139,7 +157,7 @@ export class AppAccessors {
environmentWriter: this.getEnvironmentWrite(),
reader: this.getReader(),
http: this.getHttp(),
providedApiEndpoints: this.proxify('providedApiEndpoints'),
providedApiEndpoints: this.proxify('api:listApis'),
};
}

Expand Down Expand Up @@ -207,4 +225,4 @@ export class AppAccessors {
}
}

export const AppAccessorsInstance = new AppAccessors(getProxify(Messenger.sendRequest.bind(Messenger)));
export const AppAccessorsInstance = new AppAccessors(Messenger.sendRequest);
8 changes: 8 additions & 0 deletions src/definition/api/IApiEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ export interface IApiEndpoint {
*/
authRequired?: boolean;

/**
* The methods that are available for this endpoint.
* This property is provided by the Runtime and should not be set manually.
*
* Its values are used on the Apps-Engine to validate the request method.
*/
_availableMethods?: string[];

/**
* Called whenever the publically accessible url for this App is called,
* if you handle the methods differently then split it out so your code doesn't get too big.
Expand Down
6 changes: 2 additions & 4 deletions src/server/managers/AppApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import type { ProxiedApp } from '../ProxiedApp';
import type { AppLogStorage } from '../storage';
import type { AppAccessorManager } from './AppAccessorManager';

const methods: Array<string> = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];

export class AppApi {
public readonly computedPath: string;

Expand Down Expand Up @@ -36,7 +34,7 @@ export class AppApi {

this.computedPath = `${this.basePath}/${endpoint.path}`;

this.implementedMethods = methods.filter((m) => typeof (endpoint as any)[m] === 'function');
this.implementedMethods = endpoint._availableMethods;
}

public async runExecutor(request: IApiRequest, logStorage: AppLogStorage, accessors: AppAccessorManager): Promise<IApiResponse> {
Expand All @@ -45,7 +43,7 @@ export class AppApi {
const { method } = request;

// Ensure the api has the property before going on
if (typeof this.endpoint[method] !== 'function') {
if (!this.endpoint[method]) {
return;
}

Expand Down
15 changes: 10 additions & 5 deletions src/server/runtime/AppsEngineDenoRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@ export class DenoRuntimeSubprocessController extends EventEmitter {
const managerOrigin = accessorMethods.shift();
const tailMethodName = accessorMethods.pop();

if (managerOrigin === 'api' && tailMethodName === 'listApis') {
const result = this.api.listApis(this.appId);

return jsonrpc.success(id, result);
}

/**
* At this point, the accessorMethods array will contain the path to the accessor from the origin (AppAccessorManager)
* The accessor is the one that contains the actual method the app wants to call
Expand Down Expand Up @@ -196,15 +202,14 @@ export class DenoRuntimeSubprocessController extends EventEmitter {
) => {
const origin = accessorManager[managerOrigin](this.appId);

// These will need special treatment
if (managerOrigin === 'getConfigurationExtend' || managerOrigin === 'getConfigurationModify') {
return origin[accessorMethods[0] as keyof typeof origin];
}

if (managerOrigin === 'getHttp' || managerOrigin === 'getPersistence') {
return origin;
}

if (managerOrigin === 'getConfigurationExtend' || managerOrigin === 'getConfigurationModify') {
return origin[accessorMethods[0] as keyof typeof origin];
}

let accessor = origin;

// Call all intermediary objects to "resolve" the accessor
Expand Down

0 comments on commit 534a514

Please sign in to comment.