diff --git a/.changeset/calm-falcons-gather.md b/.changeset/calm-falcons-gather.md new file mode 100644 index 0000000000000..563fe2ad3a948 --- /dev/null +++ b/.changeset/calm-falcons-gather.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/apps-engine": minor +"@rocket.chat/meteor": minor +--- + +Adds the `getUserRoomIds` method to the `UserRead` accessor in the Apps-Engine, graduating it from the experimental bridge to the stable user bridge. diff --git a/apps/meteor/app/apps/server/bridges/experimental.ts b/apps/meteor/app/apps/server/bridges/experimental.ts index 203097afeea7a..d505a54d5665d 100644 --- a/apps/meteor/app/apps/server/bridges/experimental.ts +++ b/apps/meteor/app/apps/server/bridges/experimental.ts @@ -1,31 +1,8 @@ import type { IAppServerOrchestrator } from '@rocket.chat/apps'; import { ExperimentalBridge } from '@rocket.chat/apps-engine/server/bridges'; -import { Subscriptions } from '@rocket.chat/models'; - -import { metrics } from '../../../metrics/server/lib/metrics'; export class AppExperimentalBridge extends ExperimentalBridge { - constructor(private readonly orch: IAppServerOrchestrator) { + constructor(protected readonly orch: IAppServerOrchestrator) { super(); } - - protected async getUserRoomIds(userId: string, appId: string): Promise { - const stopTimer = metrics.appBridgeMethods.startTimer({ - bridge: 'experimental', - method: 'getUserRoomIds', - app_id: appId, - }); - - try { - 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(); - - const result = subscriptions.map((subscription) => subscription.rid); - - return result; - } finally { - stopTimer(); - } - } } diff --git a/apps/meteor/app/apps/server/bridges/users.ts b/apps/meteor/app/apps/server/bridges/users.ts index 13d2436c768c9..3c27eab902521 100644 --- a/apps/meteor/app/apps/server/bridges/users.ts +++ b/apps/meteor/app/apps/server/bridges/users.ts @@ -168,4 +168,12 @@ export class AppUserBridge extends UserBridge { protected async getUserUnreadMessageCount(uid: string): Promise { return Subscriptions.getBadgeCount(uid); } + + protected async getUserRoomIds(userId: string, appId: string): Promise { + 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); + } } diff --git a/packages/apps-engine/src/definition/accessors/IExperimentalRead.ts b/packages/apps-engine/src/definition/accessors/IExperimentalRead.ts index 5c202911d1424..bb1ed9f2a273d 100644 --- a/packages/apps-engine/src/definition/accessors/IExperimentalRead.ts +++ b/packages/apps-engine/src/definition/accessors/IExperimentalRead.ts @@ -5,12 +5,5 @@ * 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; -} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface IExperimentalRead {} diff --git a/packages/apps-engine/src/definition/accessors/IUserRead.ts b/packages/apps-engine/src/definition/accessors/IUserRead.ts index cd4ad3f91b2ef..1106a2d046dc4 100644 --- a/packages/apps-engine/src/definition/accessors/IUserRead.ts +++ b/packages/apps-engine/src/definition/accessors/IUserRead.ts @@ -19,4 +19,11 @@ export interface IUserRead { * @param uid user's id */ getUserUnreadMessageCount(uid: string): Promise; + + /** + * Fetches the IDs of the rooms that the user is a member of. + * + * @param userId the user whose memberships should be returned + */ + getUserRoomIds(userId: string): Promise; } diff --git a/packages/apps-engine/src/server/accessors/ExperimentalRead.ts b/packages/apps-engine/src/server/accessors/ExperimentalRead.ts index e94254d40ed9b..20922502163ec 100644 --- a/packages/apps-engine/src/server/accessors/ExperimentalRead.ts +++ b/packages/apps-engine/src/server/accessors/ExperimentalRead.ts @@ -3,11 +3,7 @@ import type { ExperimentalBridge } from '../bridges'; export class ExperimentalRead implements IExperimentalRead { constructor( - private experimentalBridge: ExperimentalBridge, - private appId: string, + protected readonly experimentalBridge: ExperimentalBridge, + protected readonly appId: string, ) {} - - public async getUserRoomIds(userId: string): Promise { - return this.experimentalBridge.doGetUserRoomIds(userId, this.appId); - } } diff --git a/packages/apps-engine/src/server/accessors/UserRead.ts b/packages/apps-engine/src/server/accessors/UserRead.ts index 275750ef8064c..a147a51b3bfaa 100644 --- a/packages/apps-engine/src/server/accessors/UserRead.ts +++ b/packages/apps-engine/src/server/accessors/UserRead.ts @@ -23,4 +23,8 @@ export class UserRead implements IUserRead { public getUserUnreadMessageCount(uid: string): Promise { return this.userBridge.doGetUserUnreadMessageCount(uid, this.appId); } + + public getUserRoomIds(userId: string): Promise { + return this.userBridge.doGetUserRoomIds(userId, this.appId); + } } diff --git a/packages/apps-engine/src/server/bridges/ExperimentalBridge.ts b/packages/apps-engine/src/server/bridges/ExperimentalBridge.ts index d808df5790409..806b0d66fef37 100644 --- a/packages/apps-engine/src/server/bridges/ExperimentalBridge.ts +++ b/packages/apps-engine/src/server/bridges/ExperimentalBridge.ts @@ -1,7 +1,4 @@ import { BaseBridge } from './BaseBridge'; -import { PermissionDeniedError } from '../errors/PermissionDeniedError'; -import { AppPermissionManager } from '../managers/AppPermissionManager'; -import { AppPermissions } from '../permissions/AppPermissions'; /** * @description @@ -10,31 +7,4 @@ import { AppPermissions } from '../permissions/AppPermissions'; * 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 { - if (this.hasPermission('getUserRoomIds', appId)) { - return this.getUserRoomIds(userId, appId); - } - } - - protected abstract getUserRoomIds(userId: string, appId: string): Promise; - - 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; - } -} +export abstract class ExperimentalBridge extends BaseBridge {} diff --git a/packages/apps-engine/src/server/bridges/UserBridge.ts b/packages/apps-engine/src/server/bridges/UserBridge.ts index 7112a5a056eca..72cf52eb17c1d 100644 --- a/packages/apps-engine/src/server/bridges/UserBridge.ts +++ b/packages/apps-engine/src/server/bridges/UserBridge.ts @@ -45,6 +45,12 @@ export abstract class UserBridge extends BaseBridge { } } + public async doGetUserRoomIds(userId: string, appId: string): Promise { + if (this.hasReadPermission(appId)) { + return this.getUserRoomIds(userId, appId); + } + } + public async doDeleteUsersCreatedByApp(appId: string, type: UserType.BOT | UserType.APP): Promise { if (this.hasWritePermission(appId)) { return this.deleteUsersCreatedByApp(appId, type); @@ -67,6 +73,8 @@ export abstract class UserBridge extends BaseBridge { protected abstract getUserUnreadMessageCount(uid: string, appId: string): Promise; + protected abstract getUserRoomIds(userId: string, appId: string): Promise; + /** * Creates a user. * @param data the essential data for creating a user diff --git a/packages/apps-engine/src/server/permissions/AppPermissions.ts b/packages/apps-engine/src/server/permissions/AppPermissions.ts index 92e6c518e3ed8..483235286aa29 100644 --- a/packages/apps-engine/src/server/permissions/AppPermissions.ts +++ b/packages/apps-engine/src/server/permissions/AppPermissions.ts @@ -123,7 +123,7 @@ export const AppPermissions = { provide: { name: 'outbound-communication.provide' }, }, 'experimental': { - getUserRoomIds: { name: 'experimental.getUserRoomIds' }, + default: { name: 'experimental.default' }, }, }; diff --git a/packages/apps-engine/tests/server/accessors/UserRead.spec.ts b/packages/apps-engine/tests/server/accessors/UserRead.spec.ts index 47e652871f822..16c933b6cc984 100644 --- a/packages/apps-engine/tests/server/accessors/UserRead.spec.ts +++ b/packages/apps-engine/tests/server/accessors/UserRead.spec.ts @@ -12,11 +12,14 @@ export class UserReadAccessorTestFixture { private mockAppId: 'test-appId'; + private roomIds: Array; + @SetupFixture public setupFixture() { this.user = TestData.getUser(); + this.roomIds = ['room-1', 'room-2']; - const theUser = this.user; + const { user: theUser, roomIds } = this; this.mockUserBridge = { doGetById(id, appId): Promise { return Promise.resolve(theUser); @@ -27,6 +30,9 @@ export class UserReadAccessorTestFixture { doGetAppUser(appId?: string): Promise { return Promise.resolve(theUser); }, + doGetUserRoomIds(userId: string): Promise> { + return Promise.resolve(roomIds); + }, } as UserBridge; } @@ -45,5 +51,6 @@ export class UserReadAccessorTestFixture { Expect(await ur.getAppUser(this.mockAppId)).toBeDefined(); Expect(await ur.getAppUser(this.mockAppId)).toEqual(this.user); Expect(await ur.getAppUser()).toEqual(this.user); + Expect(await ur.getUserRoomIds(this.user.id)).toEqual(this.roomIds); } } diff --git a/packages/apps-engine/tests/test-data/bridges/experimentalBridge.ts b/packages/apps-engine/tests/test-data/bridges/experimentalBridge.ts index 350411e20890d..c30da179c5c98 100644 --- a/packages/apps-engine/tests/test-data/bridges/experimentalBridge.ts +++ b/packages/apps-engine/tests/test-data/bridges/experimentalBridge.ts @@ -1,7 +1,3 @@ import { ExperimentalBridge } from '../../../src/server/bridges'; -export class TestExperimentalBridge extends ExperimentalBridge { - protected getUserRoomIds(userId: string, appId: string): Promise { - throw new Error('Method not implemented.'); - } -} +export class TestExperimentalBridge extends ExperimentalBridge {} diff --git a/packages/apps-engine/tests/test-data/bridges/userBridge.ts b/packages/apps-engine/tests/test-data/bridges/userBridge.ts index 1bba51003b139..0153487c392f8 100644 --- a/packages/apps-engine/tests/test-data/bridges/userBridge.ts +++ b/packages/apps-engine/tests/test-data/bridges/userBridge.ts @@ -38,6 +38,10 @@ export class TestsUserBridge extends UserBridge { throw new Error('Method not implemented.'); } + protected getUserRoomIds(userId: string, appId: string): Promise { + throw new Error('Method not implemented.'); + } + protected deactivate(userId: IUser['id'], confirmRelinquish: boolean, appId: string): Promise { throw new Error('Method not implemented.'); } diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 20a091337f8eb..ac253ada65cc2 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -634,6 +634,7 @@ "Apps_Permissions_message_read": "Access messages", "Apps_Permissions_message_write": "Send and modify messages", "Apps_Permissions_networking": "Access to this server network", + "Apps_Permissions_experimental_default": "Use experimental APIs", "Apps_Permissions_persistence": "Store internal data in the database", "Apps_Permissions_room_read": "Access room information", "Apps_Permissions_room_write": "Create and modify rooms",