Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions .changeset/chatty-foxes-attend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/apps-engine': minor
'@rocket.chat/meteor': minor
---

Adds an experimental API to the apps-engine that retrieves the ids of rooms the user is a member of
6 changes: 6 additions & 0 deletions apps/meteor/app/apps/server/bridges/bridges.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppContactBridge } from './contact';
import { AppDetailChangesBridge } from './details';
import { AppEmailBridge } from './email';
import { AppEnvironmentalVariableBridge } from './environmental';
import { AppExperimentalBridge } from './experimental';
import { AppHttpBridge } from './http';
import { AppInternalBridge } from './internal';
import { AppInternalFederationBridge } from './internalFederation';
Expand Down Expand Up @@ -59,6 +60,7 @@ export class RealAppBridges extends AppBridges {
this._emailBridge = new AppEmailBridge(orch);
this._contactBridge = new AppContactBridge(orch);
this._outboundMessageBridge = new OutboundCommunicationBridge(orch);
this._experimentalBridge = new AppExperimentalBridge(orch);
}

getCommandBridge() {
Expand Down Expand Up @@ -168,4 +170,8 @@ export class RealAppBridges extends AppBridges {
getContactBridge() {
return this._contactBridge;
}

getExperimentalBridge() {
return this._experimentalBridge;
}
}
17 changes: 17 additions & 0 deletions apps/meteor/app/apps/server/bridges/experimental.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { IAppServerOrchestrator } from '@rocket.chat/apps';
import { ExperimentalBridge } from '@rocket.chat/apps-engine/server/bridges';
import { Subscriptions } from '@rocket.chat/models';

export class AppExperimentalBridge extends ExperimentalBridge {
constructor(private readonly orch: IAppServerOrchestrator) {
super();
}

protected async getUserRoomIds(userId: string, appId: string): Promise<string[] | undefined> {
this.orch.debugLog(`The App ${appId} is getting the room ids for the user: "${userId}"`);

const subscriptions = await Subscriptions.findByUserId(userId, { projection: { rid: 1 } }).toArray();

return subscriptions.map((subscription) => subscription.rid);
}
}
1 change: 1 addition & 0 deletions packages/apps-engine/deno-runtime/lib/accessors/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ export class AppAccessors {
getThreadReader: () => this.proxify('getReader:getThreadReader'),
getRoleReader: () => this.proxify('getReader:getRoleReader'),
getContactReader: () => this.proxify('getReader:getContactReader'),
getExperimentalReader: () => this.proxify('getReader:getExperimentalReader'),
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* @description
* Experimental bridge for experimental features.
* Methods in this class are not guaranteed to be stable between updates as the
* team evaluates the proper signature, underlying implementation and performance
* impact of candidates for future APIs
*/
export interface IExperimentalRead {
/**
* Fetches the IDs of the rooms that the user is a member of.
*
* @returns an array of room ids or undefined if the app doesn't have the proper permission
* @experimental
*/
getUserRoomIds(userId: string): Promise<string[] | undefined>;
}
3 changes: 3 additions & 0 deletions packages/apps-engine/src/definition/accessors/IRead.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ICloudWorkspaceRead } from './ICloudWorkspaceRead';
import type { IContactRead } from './IContactRead';
import type { IEnvironmentRead } from './IEnvironmentRead';
import type { IExperimentalRead } from './IExperimentalRead';
import type { ILivechatRead } from './ILivechatRead';
import type { IMessageRead } from './IMessageRead';
import type { INotifier } from './INotifier';
Expand Down Expand Up @@ -51,4 +52,6 @@ export interface IRead {

getRoleReader(): IRoleRead;
getContactReader(): IContactRead;

getExperimentalReader(): IExperimentalRead;
}
1 change: 1 addition & 0 deletions packages/apps-engine/src/definition/accessors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export * from './IEnvironmentalVariableRead';
export * from './IEnvironmentRead';
export * from './IEnvironmentWrite';
export * from './IExternalComponentsExtend';
export * from './IExperimentalRead';
export * from './IHttp';
export * from './ILivechatCreator';
export * from './ILivechatMessageBuilder';
Expand Down
13 changes: 13 additions & 0 deletions packages/apps-engine/src/server/accessors/ExperimentalRead.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { IExperimentalRead } from '../../definition/accessors';
import type { ExperimentalBridge } from '../bridges';

export class ExperimentalRead implements IExperimentalRead {
constructor(
private experimentalBridge: ExperimentalBridge,
private appId: string,
) {}

public async getUserRoomIds(userId: string): Promise<string[] | undefined> {
return this.experimentalBridge.doGetUserRoomIds(userId, this.appId);
}
}
7 changes: 6 additions & 1 deletion packages/apps-engine/src/server/accessors/Reader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {
ICloudWorkspaceRead,
IEnvironmentRead,
IExperimentalRead,
ILivechatRead,
IMessageRead,
INotifier,
Expand Down Expand Up @@ -29,10 +30,10 @@ export class Reader implements IRead {
private cloud: ICloudWorkspaceRead,
private videoConf: IVideoConferenceRead,
private contactRead: IContactRead,

private oauthApps: IOAuthAppsReader,
private thread: IThreadRead,
private role: IRoleRead,
private experimental: IExperimentalRead,
) {}

public getEnvironmentReader(): IEnvironmentRead {
Expand Down Expand Up @@ -90,4 +91,8 @@ export class Reader implements IRead {
public getContactReader(): IContactRead {
return this.contactRead;
}

public getExperimentalReader(): IExperimentalRead {
return this.experimental;
}
}
4 changes: 4 additions & 0 deletions packages/apps-engine/src/server/bridges/AppBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { CommandBridge } from './CommandBridge';
import type { ContactBridge } from './ContactBridge';
import type { EmailBridge } from './EmailBridge';
import type { EnvironmentalVariableBridge } from './EnvironmentalVariableBridge';
import type { ExperimentalBridge } from './ExperimentalBridge';
import type { HttpBridge } from './HttpBridge';
import type { IInternalBridge } from './IInternalBridge';
import type { IInternalFederationBridge } from './IInternalFederationBridge';
Expand Down Expand Up @@ -42,6 +43,7 @@ export type Bridge =
| IInternalBridge
| ServerSettingBridge
| EmailBridge
| ExperimentalBridge
| UploadBridge
| UserBridge
| UiInteractionBridge
Expand Down Expand Up @@ -106,4 +108,6 @@ export abstract class AppBridges {
public abstract getRoleBridge(): RoleBridge;

public abstract getOutboundMessageBridge(): OutboundMessageBridge;

public abstract getExperimentalBridge(): ExperimentalBridge;
}
40 changes: 40 additions & 0 deletions packages/apps-engine/src/server/bridges/ExperimentalBridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { BaseBridge } from './BaseBridge';
import { PermissionDeniedError } from '../errors/PermissionDeniedError';
import { AppPermissionManager } from '../managers/AppPermissionManager';
import { AppPermissions } from '../permissions/AppPermissions';

/**
* @description
* Experimental bridge for experimental features.
* Methods in this class are not guaranteed to be stable between updates as the
* team evaluates the proper signature, underlying implementation and performance
* impact of candidates for future APIs
*/
export abstract class ExperimentalBridge extends BaseBridge {
/**
*
* Candidate bridge: User bridge
*/
public async doGetUserRoomIds(userId: string, appId: string): Promise<string[] | undefined> {
if (this.hasPermission('getUserRoomIds', appId)) {
return this.getUserRoomIds(userId, appId);
}
}

protected abstract getUserRoomIds(userId: string, appId: string): Promise<string[] | undefined>;

private hasPermission(feature: keyof typeof AppPermissions.experimental, appId: string): boolean {
if (AppPermissionManager.hasPermission(appId, AppPermissions.experimental[feature])) {
return true;
}

AppPermissionManager.notifyAboutError(
new PermissionDeniedError({
appId,
missingPermissions: [AppPermissions.experimental[feature]],
}),
);

return false;
}
}
2 changes: 2 additions & 0 deletions packages/apps-engine/src/server/bridges/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CommandBridge } from './CommandBridge';
import { ContactBridge } from './ContactBridge';
import { EmailBridge } from './EmailBridge';
import { EnvironmentalVariableBridge } from './EnvironmentalVariableBridge';
import { ExperimentalBridge } from './ExperimentalBridge';
import { HttpBridge, IHttpBridgeRequestInfo } from './HttpBridge';
import { IInternalBridge } from './IInternalBridge';
import { IInternalFederationBridge } from './IInternalFederationBridge';
Expand Down Expand Up @@ -45,6 +46,7 @@ export {
UserBridge,
UploadBridge,
EmailBridge,
ExperimentalBridge,
UiInteractionBridge,
SchedulerBridge,
AppBridges,
Expand Down
21 changes: 19 additions & 2 deletions packages/apps-engine/src/server/managers/AppAccessorManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import {
} from '../accessors';
import { CloudWorkspaceRead } from '../accessors/CloudWorkspaceRead';
import { ContactRead } from '../accessors/ContactRead';
import { ExperimentalRead } from '../accessors/ExperimentalRead';
import { ThreadRead } from '../accessors/ThreadRead';
import { UIExtend } from '../accessors/UIExtend';
import type { AppBridges } from '../bridges/AppBridges';
Expand Down Expand Up @@ -188,12 +189,28 @@ export class AppAccessorManager {
const oauthApps = new OAuthAppsReader(this.bridges.getOAuthAppsBridge(), appId);
const contactReader = new ContactRead(this.bridges, appId);
const thread = new ThreadRead(this.bridges.getThreadBridge(), appId);

const role = new RoleRead(this.bridges.getRoleBridge(), appId);
const experimental = new ExperimentalRead(this.bridges.getExperimentalBridge(), appId);

this.readers.set(
appId,
new Reader(env, msg, persist, room, user, noti, livechat, upload, cloud, videoConf, contactReader, oauthApps, thread, role),
new Reader(
env,
msg,
persist,
room,
user,
noti,
livechat,
upload,
cloud,
videoConf,
contactReader,
oauthApps,
thread,
role,
experimental,
),
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ export const AppPermissions = {
'outboundComms': {
provide: { name: 'outbound-communication.provide' },
},
'experimental': {
getUserRoomIds: { name: 'experimental.getUserRoomIds' },
},
};

/**
Expand Down
6 changes: 6 additions & 0 deletions packages/apps-engine/tests/server/accessors/Reader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Expect, SetupFixture, Test } from 'alsatian';
import type {
ICloudWorkspaceRead,
IEnvironmentRead,
IExperimentalRead,
ILivechatRead,
IMessageRead,
INotifier,
Expand Down Expand Up @@ -47,6 +48,8 @@ export class ReaderAccessorTestFixture {

private contact: IContactRead;

private experimental: IExperimentalRead;

@SetupFixture
public setupFixture() {
this.env = {} as IEnvironmentRead;
Expand All @@ -63,6 +66,7 @@ export class ReaderAccessorTestFixture {
this.thread = {} as IThreadRead;
this.role = {} as IRoleRead;
this.contact = {} as IContactRead;
this.experimental = {} as IExperimentalRead;
}

@Test()
Expand All @@ -84,6 +88,7 @@ export class ReaderAccessorTestFixture {
this.oauthApps,
this.thread,
this.role,
this.experimental,
),
).not.toThrow();

Expand All @@ -102,6 +107,7 @@ export class ReaderAccessorTestFixture {
this.oauthApps,
this.thread,
this.role,
this.experimental,
);

Expect(rd.getEnvironmentReader()).toBeDefined();
Expand Down
9 changes: 9 additions & 0 deletions packages/apps-engine/tests/test-data/bridges/appBridges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { TestsCommandBridge } from './commandBridge';
import { TestContactBridge } from './contactBridge';
import { TestsEmailBridge } from './emailBridge';
import { TestsEnvironmentalVariableBridge } from './environmentalVariableBridge';
import { TestExperimentalBridge } from './experimentalBridge';
import { TestsHttpBridge } from './httpBridge';
import { TestsInternalBridge } from './internalBridge';
import { TestsInternalFederationBridge } from './internalFederationBridge';
Expand All @@ -30,6 +31,7 @@ import type {
AppDetailChangesBridge,
ContactBridge,
EnvironmentalVariableBridge,
ExperimentalBridge,
HttpBridge,
IInternalBridge,
IListenerBridge,
Expand Down Expand Up @@ -106,6 +108,8 @@ export class TestsAppBridges extends AppBridges {

private readonly outboundCommsBridge: TestOutboundCommunicationBridge;

private readonly experimentalBridge: TestExperimentalBridge;

constructor() {
super();
this.appDetails = new TestsAppDetailChangesBridge();
Expand Down Expand Up @@ -134,6 +138,7 @@ export class TestsAppBridges extends AppBridges {
this.emailBridge = new TestsEmailBridge();
this.contactBridge = new TestContactBridge();
this.outboundCommsBridge = new TestOutboundCommunicationBridge();
this.experimentalBridge = new TestExperimentalBridge();
}

public getCommandBridge(): TestsCommandBridge {
Expand Down Expand Up @@ -243,4 +248,8 @@ export class TestsAppBridges extends AppBridges {
public getOutboundMessageBridge(): OutboundMessageBridge {
return this.outboundCommsBridge;
}

public getExperimentalBridge(): ExperimentalBridge {
return this.experimentalBridge;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ExperimentalBridge } from '../../../src/server/bridges';

export class TestExperimentalBridge extends ExperimentalBridge {
protected getUserRoomIds(userId: string, appId: string): Promise<string[] | undefined> {
throw new Error('Method not implemented.');
}
}
Loading