Skip to content

Commit

Permalink
Add ModifyUpdater accessors (#681)
Browse files Browse the repository at this point in the history
  • Loading branch information
d-gubert authored Dec 5, 2023
1 parent 092f19b commit b574afb
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 3 deletions.
14 changes: 11 additions & 3 deletions deno-runtime/lib/accessors/mod.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// @ts-ignore - this is a hack to make the tests work
import type { IAppAccessors } from '@rocket.chat/apps-engine/definition/accessors/IAppAccessors.ts';
import type { IEnvironmentWrite } from '@rocket.chat/apps-engine/definition/accessors/IEnvironmentWrite.ts';
import type { IEnvironmentRead } from '@rocket.chat/apps-engine/definition/accessors/IEnvironmentRead.ts';
Expand All @@ -16,6 +15,7 @@ import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/vid
import * as Messenger from '../messenger.ts';
import { AppObjectRegistry } from '../../AppObjectRegistry.ts';
import { ModifyCreator } from "./modify/ModifyCreator.ts";
import { ModifyUpdater } from "./modify/ModifyUpdater.ts";

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

Expand All @@ -30,6 +30,7 @@ export class AppAccessors {
private persistence?: IPersistence;
private http?: IHttp;
private creator?: ModifyCreator;
private updater?: ModifyUpdater;

private proxify: <T>(namespace: string) => T;

Expand Down Expand Up @@ -195,9 +196,8 @@ export class AppAccessors {
public getModifier() {
if (!this.modifier) {
this.modifier = {
// getCreator: () => this.proxify('getModifier:getCreator'), // can't be proxy
getCreator: this.getCreator.bind(this),
getUpdater: () => this.proxify('getModifier:getUpdater'), // can't be proxy
getUpdater: this.getUpdater.bind(this),
getDeleter: () => this.proxify('getModifier:getDeleter'),
getExtender: () => this.proxify('getModifier:getExtender'), // can't be proxy
getNotifier: () => this.proxify('getModifier:getNotifier'),
Expand Down Expand Up @@ -234,6 +234,14 @@ export class AppAccessors {

return this.creator;
}

private getUpdater() {
if (!this.updater) {
this.updater = new ModifyUpdater(this.senderFn);
}

return this.updater;
}
}

export const AppAccessorsInstance = new AppAccessors(Messenger.sendRequest);
137 changes: 137 additions & 0 deletions deno-runtime/lib/accessors/modify/ModifyUpdater.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import { createRequire } from 'node:module';

import type { IModifyUpdater } from '@rocket.chat/apps-engine/definition/accessors/IModifyUpdater.ts';
import type { ILivechatUpdater } from '@rocket.chat/apps-engine/definition/accessors/ILivechatUpdater.ts';
import type { IUserUpdater } from '@rocket.chat/apps-engine/definition/accessors/IUserUpdater.ts';
import type { IMessageBuilder } from '@rocket.chat/apps-engine/definition/accessors/IMessageBuilder.ts';
import type { IRoomBuilder } from '@rocket.chat/apps-engine/definition/accessors/IRoomBuilder.ts';
import type { IUser } from '@rocket.chat/apps-engine/definition/users/IUser.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 { RoomType } from '@rocket.chat/apps-engine/definition/rooms/RoomType.ts';
import { RocketChatAssociationModel } from '@rocket.chat/apps-engine/definition/metadata/RocketChatAssociations.ts';

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

const require = createRequire(import.meta.url);

const UIHelper = require(import.meta.resolve('@rocket.chat/apps-engine/server/misc/UIHelper.js').replace('file://', '').replace('src/', ''));

export class ModifyUpdater implements IModifyUpdater {
constructor(private readonly senderFn: typeof Messenger.sendRequest) {}

public getLivechatUpdater(): ILivechatUpdater {
return new Proxy(
{ __kind: 'getLivechatUpdater' },
{
get:
(_target: unknown, prop: string) =>
(...params: unknown[]) =>
this.senderFn({
method: `accessor:getModifier:getUpdater:getLivechatUpdater:${prop}`,
params,
}),
},
) as ILivechatUpdater;
}

public getUserUpdater(): IUserUpdater {
return new Proxy(
{ __kind: 'getUserUpdater' },
{
get:
(_target: unknown, prop: string) =>
(...params: unknown[]) =>
this.senderFn({
method: `accessor:getModifier:getUpdater:getUserUpdater:${prop}`,
params,
}),
},
) as IUserUpdater;
}

public async message(messageId: string, _updater: IUser): Promise<IMessageBuilder> {
const response = await this.senderFn({
method: 'bridges:getMessageBridge:doGetById',
params: [messageId, AppObjectRegistry.get('appId')],
});

return new MessageBuilder(response.result as IMessage);
}

public async room(roomId: string, _updater: IUser): Promise<IRoomBuilder> {
const response = await this.senderFn({
method: 'bridges:getRoomBridge:doGetById',
params: [roomId, AppObjectRegistry.get('appId')],
});

return new RoomBuilder(response.result as IRoom);
}

public finish(builder: IMessageBuilder | IRoomBuilder): Promise<void> {
switch (builder.kind) {
case RocketChatAssociationModel.MESSAGE:
return this._finishMessage(builder as IMessageBuilder);
case RocketChatAssociationModel.ROOM:
return this._finishRoom(builder as IRoomBuilder);
default:
throw new Error('Invalid builder passed to the ModifyUpdater.finish function.');
}
}

private async _finishMessage(builder: IMessageBuilder): Promise<void> {
const result = builder.getMessage();

if (!result.id) {
throw new Error("Invalid message, can't update a message without an id.");
}

if (!result.sender?.id) {
throw new Error('Invalid sender assigned to the message.');
}

if (result.blocks?.length) {
result.blocks = UIHelper.assignIds(result.blocks, AppObjectRegistry.get('appId'));
}

await this.senderFn({
method: 'bridges:getMessageBridge:doUpdate',
params: [result, AppObjectRegistry.get('appId')],
});
}

private async _finishRoom(builder: IRoomBuilder): Promise<void> {
const result = builder.getRoom();

if (!result.id) {
throw new Error("Invalid room, can't update a room without an id.");
}

if (!result.type) {
throw new Error('Invalid type assigned to the room.');
}

if (result.type !== RoomType.LIVE_CHAT) {
if (!result.creator || !result.creator.id) {
throw new Error('Invalid creator assigned to the room.');
}

if (!result.slugifiedName || !result.slugifiedName.trim()) {
throw new Error('Invalid slugifiedName assigned to the room.');
}
}

if (!result.displayName || !result.displayName.trim()) {
throw new Error('Invalid displayName assigned to the room.');
}

await this.senderFn({
method: 'bridges:getRoomBridge:doUpdate',
params: [result, builder.getMembersToBeAddedUsernames(), AppObjectRegistry.get('appId')],
});
}
}
128 changes: 128 additions & 0 deletions deno-runtime/lib/accessors/tests/ModifyUpdater.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// deno-lint-ignore-file no-explicit-any
import { afterAll, beforeEach, describe, it } from 'https://deno.land/[email protected]/testing/bdd.ts';
import { assertSpyCall, spy } from 'https://deno.land/[email protected]/testing/mock.ts';
import { assertEquals } from 'https://deno.land/[email protected]/assert/mod.ts';

import { AppObjectRegistry } from '../../../AppObjectRegistry.ts';
import { ModifyUpdater } from '../modify/ModifyUpdater.ts';

describe('ModifyUpdater', () => {
let modifyUpdater: ModifyUpdater;

const senderFn = (r: any) =>
Promise.resolve({
id: Math.random().toString(36).substring(2),
jsonrpc: '2.0',
result: r,
serialize() {
return JSON.stringify(this);
},
});

beforeEach(() => {
AppObjectRegistry.clear();
AppObjectRegistry.set('appId', 'deno-test');
modifyUpdater = new ModifyUpdater(senderFn);
});

afterAll(() => {
AppObjectRegistry.clear();
});

it('correctly formats requests for the update message flow', async () => {
const _spy = spy(modifyUpdater, 'senderFn' as keyof ModifyUpdater);

const messageBuilder = await modifyUpdater.message('123', { id: '456' } as any);

assertSpyCall(_spy, 0, {
args: [
{
method: 'bridges:getMessageBridge:doGetById',
params: ['123', 'deno-test'],
},
],
});

messageBuilder.setUpdateData(
{
id: '123',
room: { id: '123' },
sender: { id: '456' },
text: 'Hello World',
},
{
id: '456',
},
);

await modifyUpdater.finish(messageBuilder);

assertSpyCall(_spy, 1, {
args: [
{
method: 'bridges:getMessageBridge:doUpdate',
params: [messageBuilder.getMessage(), 'deno-test'],
},
],
});

_spy.restore();
});

it('correctly formats requests for the update room flow', async () => {
const _spy = spy(modifyUpdater, 'senderFn' as keyof ModifyUpdater);

const roomBuilder = await modifyUpdater.room('123', { id: '456' } as any);

assertSpyCall(_spy, 0, {
args: [
{
method: 'bridges:getRoomBridge:doGetById',
params: ['123', 'deno-test'],
},
],
});

roomBuilder.setData({
id: '123',
type: 'c',
displayName: 'Test Room',
slugifiedName: 'test-room',
creator: { id: '456' },
});

roomBuilder.setMembersToBeAddedByUsernames(['username1', 'username2']);

// We need to sneak in the id as the `modifyUpdater.room` call won't have legitimate data
roomBuilder.getRoom().id = '123';

await modifyUpdater.finish(roomBuilder);

assertSpyCall(_spy, 1, {
args: [
{
method: 'bridges:getRoomBridge:doUpdate',
params: [roomBuilder.getRoom(), roomBuilder.getMembersToBeAddedUsernames(), 'deno-test'],
},
],
});
});

it('correctly formats requests to UserUpdater methods', async () => {
const result = await modifyUpdater.getUserUpdater().updateStatusText({ id: '123' } as any, 'Hello World') as any;

assertEquals(result.result, {
method: 'accessor:getModifier:getUpdater:getUserUpdater:updateStatusText',
params: [{ id: '123' }, 'Hello World'],
});
});

it('correctly formats requests to LivechatUpdater methods', async () => {
const result = await modifyUpdater.getLivechatUpdater().closeRoom({ id: '123' } as any, 'close it!') as any;

assertEquals(result.result, {
method: 'accessor:getModifier:getUpdater:getLivechatUpdater:closeRoom',
params: [{ id: '123' }, 'close it!'],
});
});
});

0 comments on commit b574afb

Please sign in to comment.