-
Notifications
You must be signed in to change notification settings - Fork 118
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
276 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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')], | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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!'], | ||
}); | ||
}); | ||
}); |