From d69be40738f81de625e0175a35ce2b816b3c2954 Mon Sep 17 00:00:00 2001 From: Kevin Aleman Date: Tue, 22 Oct 2024 03:44:26 -0600 Subject: [PATCH] chore: Replace `cursor.count` with `countDocuments` or `estimatedDocumentCount` (#33693) --- apps/meteor/app/api/server/v1/roles.ts | 6 +-- .../server/methods/deleteRole.ts | 2 +- .../server/PendingAvatarImporter.ts | 3 +- .../server/functions/updateGroupDMsName.ts | 4 +- .../app/lib/server/methods/leaveRoom.ts | 3 +- .../slashcommands-inviteall/server/server.ts | 10 ++--- .../server/configuration/videoConference.ts | 2 +- apps/meteor/server/methods/browseChannels.ts | 2 +- apps/meteor/server/methods/removeRoomOwner.ts | 5 +-- .../server/methods/removeUserFromRoom.ts | 6 +-- .../server/methods/requestDataDownload.ts | 2 +- .../server/models/raw/ExportOperations.ts | 9 +++++ apps/meteor/server/models/raw/Roles.ts | 16 ++++++++ apps/meteor/server/models/raw/Rooms.ts | 7 ++++ .../meteor/server/models/raw/Subscriptions.ts | 11 ++++++ apps/meteor/server/models/raw/Users.js | 20 ++++++++++ apps/meteor/server/startup/initialData.js | 9 ++--- .../unit/server/startup/initialData.tests.ts | 38 +++++++++---------- .../src/models/IExportOperationsModel.ts | 1 + .../model-typings/src/models/IRolesModel.ts | 1 + .../model-typings/src/models/IRoomsModel.ts | 1 + .../src/models/ISubscriptionsModel.ts | 1 + .../model-typings/src/models/IUsersModel.ts | 2 + yarn.lock | 2 - 24 files changed, 111 insertions(+), 52 deletions(-) diff --git a/apps/meteor/app/api/server/v1/roles.ts b/apps/meteor/app/api/server/v1/roles.ts index fc9bd273996d..20f6e38f3567 100644 --- a/apps/meteor/app/api/server/v1/roles.ts +++ b/apps/meteor/app/api/server/v1/roles.ts @@ -165,9 +165,7 @@ API.v1.addRoute( throw new Meteor.Error('error-role-protected', 'Cannot delete a protected role'); } - const existingUsers = await Roles.findUsersInRole(role._id); - - if (existingUsers && (await existingUsers.count()) > 0) { + if ((await Roles.countUsersInRole(role._id)) > 0) { throw new Meteor.Error('error-role-in-use', "Cannot delete role because it's in use"); } @@ -217,7 +215,7 @@ API.v1.addRoute( } if (role._id === 'admin') { - const adminCount = await (await Roles.findUsersInRole('admin')).count(); + const adminCount = await Roles.countUsersInRole('admin'); if (adminCount === 1) { throw new Meteor.Error('error-admin-required', 'You need to have at least one admin'); } diff --git a/apps/meteor/app/authorization/server/methods/deleteRole.ts b/apps/meteor/app/authorization/server/methods/deleteRole.ts index 140852e0f1ec..512468f2d6d7 100644 --- a/apps/meteor/app/authorization/server/methods/deleteRole.ts +++ b/apps/meteor/app/authorization/server/methods/deleteRole.ts @@ -56,7 +56,7 @@ Meteor.methods({ }); } - const users = await (await Roles.findUsersInRole(role._id)).count(); + const users = await Roles.countUsersInRole(role._id); if (users > 0) { throw new Meteor.Error('error-role-in-use', "Cannot delete role because it's in use", { diff --git a/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts b/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts index de37ba200289..f057da4a625d 100644 --- a/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts +++ b/apps/meteor/app/importer-pending-avatars/server/PendingAvatarImporter.ts @@ -10,8 +10,7 @@ export class PendingAvatarImporter extends Importer { this.logger.debug('start preparing import operation'); await super.updateProgress(ProgressStep.PREPARING_STARTED); - const users = Users.findAllUsersWithPendingAvatar(); - const fileCount = await users.count(); + const fileCount = await Users.countAllUsersWithPendingAvatar(); if (fileCount === 0) { await super.updateProgress(ProgressStep.DONE); diff --git a/apps/meteor/app/lib/server/functions/updateGroupDMsName.ts b/apps/meteor/app/lib/server/functions/updateGroupDMsName.ts index feb26ce6a1b0..ac204af51439 100644 --- a/apps/meteor/app/lib/server/functions/updateGroupDMsName.ts +++ b/apps/meteor/app/lib/server/functions/updateGroupDMsName.ts @@ -8,14 +8,14 @@ const getName = (members: IUser[]): string => members.map(({ username }) => user async function getUsersWhoAreInTheSameGroupDMsAs(user: IUser) { // add all users to single array so we can fetch details from them all at once - const rooms = Rooms.findGroupDMsByUids([user._id], { projection: { uids: 1 } }); - if ((await rooms.count()) === 0) { + if ((await Rooms.countGroupDMsByUids([user._id])) === 0) { return; } const userIds = new Set(); const users = new Map(); + const rooms = Rooms.findGroupDMsByUids([user._id], { projection: { uids: 1 } }); await rooms.forEach((room) => { if (!room.uids) { return; diff --git a/apps/meteor/app/lib/server/methods/leaveRoom.ts b/apps/meteor/app/lib/server/methods/leaveRoom.ts index 4fc85b35fd05..8ded8b56ce2b 100644 --- a/apps/meteor/app/lib/server/methods/leaveRoom.ts +++ b/apps/meteor/app/lib/server/methods/leaveRoom.ts @@ -45,8 +45,7 @@ export const leaveRoomMethod = async (user: IUser, rid: string): Promise = // If user is room owner, check if there are other owners. If there isn't anyone else, warn user to set a new owner. if (await hasRoleAsync(user._id, 'owner', room._id)) { - const cursor = await Roles.findUsersInRole('owner', room._id); - const numOwners = await cursor.count(); + const numOwners = await Roles.countUsersInRole('owner', room._id); if (numOwners === 1) { throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', { method: 'leaveRoom', diff --git a/apps/meteor/app/slashcommands-inviteall/server/server.ts b/apps/meteor/app/slashcommands-inviteall/server/server.ts index e74bb89899c2..436d283d4b5a 100644 --- a/apps/meteor/app/slashcommands-inviteall/server/server.ts +++ b/apps/meteor/app/slashcommands-inviteall/server/server.ts @@ -64,20 +64,20 @@ function inviteAll(type: T): SlashCommand['callback'] { return; } - const cursor = Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, { - projection: { 'u.username': 1 }, - }); - try { const APIsettings = settings.get('API_User_Limit'); if (!APIsettings) { return; } - if ((await cursor.count()) > APIsettings) { + if ((await Subscriptions.countByRoomIdWhenUsernameExists(baseChannel._id)) > APIsettings) { throw new Meteor.Error('error-user-limit-exceeded', 'User Limit Exceeded', { method: 'addAllToRoom', }); } + + const cursor = Subscriptions.findByRoomIdWhenUsernameExists(baseChannel._id, { + projection: { 'u.username': 1 }, + }); const users = (await cursor.toArray()).map((s: ISubscription) => s.u.username).filter(isTruthy); if (!targetChannel && ['c', 'p'].indexOf(baseChannel.t) > -1) { diff --git a/apps/meteor/ee/server/configuration/videoConference.ts b/apps/meteor/ee/server/configuration/videoConference.ts index 035110904840..2fd6e31fba95 100644 --- a/apps/meteor/ee/server/configuration/videoConference.ts +++ b/apps/meteor/ee/server/configuration/videoConference.ts @@ -38,7 +38,7 @@ Meteor.startup(async () => { } } - if ((await Subscriptions.findByRoomId(_id).count()) > 10) { + if ((await Subscriptions.countByRoomId(_id)) > 10) { return false; } diff --git a/apps/meteor/server/methods/browseChannels.ts b/apps/meteor/server/methods/browseChannels.ts index 965accc83954..0b02f009672e 100644 --- a/apps/meteor/server/methods/browseChannels.ts +++ b/apps/meteor/server/methods/browseChannels.ts @@ -116,7 +116,7 @@ const getChannelsAndGroups = async ( }; }; -const getChannelsCountForTeam = mem((teamId) => Rooms.findByTeamId(teamId, { projection: { _id: 1 } }).count(), { +const getChannelsCountForTeam = mem((teamId) => Rooms.countByTeamId(teamId), { maxAge: 2000, }); diff --git a/apps/meteor/server/methods/removeRoomOwner.ts b/apps/meteor/server/methods/removeRoomOwner.ts index 91046655a4a6..bc4ddf7964ee 100644 --- a/apps/meteor/server/methods/removeRoomOwner.ts +++ b/apps/meteor/server/methods/removeRoomOwner.ts @@ -1,11 +1,10 @@ import { api, Message, Team } from '@rocket.chat/core-services'; import { isRoomFederated } from '@rocket.chat/core-typings'; import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Subscriptions, Rooms, Users } from '@rocket.chat/models'; +import { Subscriptions, Rooms, Users, Roles } from '@rocket.chat/models'; import { check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { getUsersInRole } from '../../app/authorization/server'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { notifyOnSubscriptionChangedById } from '../../app/lib/server/lib/notifyListener'; import { settings } from '../../app/settings/server'; @@ -64,7 +63,7 @@ Meteor.methods({ }); } - const numOwners = await (await getUsersInRole('owner', rid)).count(); + const numOwners = await Roles.countUsersInRole('owner', rid); if (numOwners === 1) { throw new Meteor.Error('error-remove-last-owner', 'This is the last owner. Please set a new owner before removing this one.', { diff --git a/apps/meteor/server/methods/removeUserFromRoom.ts b/apps/meteor/server/methods/removeUserFromRoom.ts index 781ffe3a2671..8d1cb2e2ba2b 100644 --- a/apps/meteor/server/methods/removeUserFromRoom.ts +++ b/apps/meteor/server/methods/removeUserFromRoom.ts @@ -2,11 +2,11 @@ import { Apps, AppEvents } from '@rocket.chat/apps'; import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions'; import { Message, Team, Room } from '@rocket.chat/core-services'; import type { ServerMethods } from '@rocket.chat/ddp-client'; -import { Subscriptions, Rooms, Users } from '@rocket.chat/models'; +import { Subscriptions, Rooms, Users, Roles } from '@rocket.chat/models'; import { Match, check } from 'meteor/check'; import { Meteor } from 'meteor/meteor'; -import { canAccessRoomAsync, getUsersInRole } from '../../app/authorization/server'; +import { canAccessRoomAsync } from '../../app/authorization/server'; import { hasPermissionAsync } from '../../app/authorization/server/functions/hasPermission'; import { hasRoleAsync } from '../../app/authorization/server/functions/hasRole'; import { notifyOnRoomChanged, notifyOnSubscriptionChanged } from '../../app/lib/server/lib/notifyListener'; @@ -70,7 +70,7 @@ export const removeUserFromRoomMethod = async (fromId: string, data: { rid: stri } if (await hasRoleAsync(removedUser._id, 'owner', room._id)) { - const numOwners = await (await getUsersInRole('owner', room._id)).count(); + const numOwners = await Roles.countUsersInRole('owner', room._id); if (numOwners === 1) { throw new Meteor.Error('error-you-are-last-owner', 'You are the last owner. Please set new owner before leaving the room.', { diff --git a/apps/meteor/server/methods/requestDataDownload.ts b/apps/meteor/server/methods/requestDataDownload.ts index 80769aae6340..cf30a6768b7c 100644 --- a/apps/meteor/server/methods/requestDataDownload.ts +++ b/apps/meteor/server/methods/requestDataDownload.ts @@ -34,7 +34,7 @@ Meteor.methods({ const lastOperation = await ExportOperations.findLastOperationByUser(userId, fullExport); const requestDay = lastOperation ? lastOperation.createdAt : new Date(); - const pendingOperationsBeforeMyRequestCount = await ExportOperations.findAllPendingBeforeMyRequest(requestDay).count(); + const pendingOperationsBeforeMyRequestCount = await ExportOperations.countAllPendingBeforeMyRequest(requestDay); if (lastOperation) { const yesterday = new Date(); diff --git a/apps/meteor/server/models/raw/ExportOperations.ts b/apps/meteor/server/models/raw/ExportOperations.ts index d7afb8ad6010..0b544ddaa6c9 100644 --- a/apps/meteor/server/models/raw/ExportOperations.ts +++ b/apps/meteor/server/models/raw/ExportOperations.ts @@ -48,6 +48,15 @@ export class ExportOperationsRaw extends BaseRaw implements IE return this.find(query); } + countAllPendingBeforeMyRequest(requestDay: Date): Promise { + const query = { + status: { $nin: ['completed', 'skipped'] }, + createdAt: { $lt: requestDay }, + }; + + return this.countDocuments(query); + } + updateOperation(data: IExportOperation): Promise { const update = { $set: { diff --git a/apps/meteor/server/models/raw/Roles.ts b/apps/meteor/server/models/raw/Roles.ts index 4e1cb09348c4..c3dfab7a702a 100644 --- a/apps/meteor/server/models/raw/Roles.ts +++ b/apps/meteor/server/models/raw/Roles.ts @@ -254,6 +254,22 @@ export class RolesRaw extends BaseRaw implements IRolesModel { } } + async countUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise { + const role = await this.findOneById>(roleId, { projection: { scope: 1 } }); + + if (!role) { + throw new Error('RolesRaw.countUsersInRole: role not found'); + } + + switch (role.scope) { + case 'Subscriptions': + return Subscriptions.countUsersInRoles([role._id], scope); + case 'Users': + default: + return Users.countUsersInRoles([role._id]); + } + } + async createWithRandomId( name: IRole['name'], scope: IRole['scope'] = 'Users', diff --git a/apps/meteor/server/models/raw/Rooms.ts b/apps/meteor/server/models/raw/Rooms.ts index 7fc3be7479ed..0d0770994fbd 100644 --- a/apps/meteor/server/models/raw/Rooms.ts +++ b/apps/meteor/server/models/raw/Rooms.ts @@ -1464,6 +1464,13 @@ export class RoomsRaw extends BaseRaw implements IRoomsModel { ); } + countGroupDMsByUids(uids: NonNullable): Promise { + return this.countDocuments({ + usersCount: { $gt: 2 }, + uids: { $in: uids }, + }); + } + find1On1ByUserId(userId: IRoom['_id'], options: FindOptions = {}): FindCursor { return this.find( { diff --git a/apps/meteor/server/models/raw/Subscriptions.ts b/apps/meteor/server/models/raw/Subscriptions.ts index 829261e7f94a..7d3eda60edd3 100644 --- a/apps/meteor/server/models/raw/Subscriptions.ts +++ b/apps/meteor/server/models/raw/Subscriptions.ts @@ -264,6 +264,17 @@ export class SubscriptionsRaw extends BaseRaw implements ISubscri return Users.find

({ _id: { $in: users } }, options || {}); } + async countUsersInRoles(roles: IRole['_id'][], rid: IRoom['_id'] | undefined): Promise { + const query = { + roles: { $in: roles }, + ...(rid && { rid }), + }; + + // Ideally, the count of subscriptions would be the same (or really similar) to the count in users + // As sub/user/room is a 1:1 relation. + return this.countDocuments(query); + } + addRolesByUserId(uid: IUser['_id'], roles: IRole['_id'][], rid?: IRoom['_id']): Promise { if (!Array.isArray(roles)) { roles = [roles]; diff --git a/apps/meteor/server/models/raw/Users.js b/apps/meteor/server/models/raw/Users.js index a5bd1d4fc16b..3c1badc55e25 100644 --- a/apps/meteor/server/models/raw/Users.js +++ b/apps/meteor/server/models/raw/Users.js @@ -135,6 +135,16 @@ export class UsersRaw extends BaseRaw { return this.find(query, options); } + countUsersInRoles(roles) { + roles = [].concat(roles); + + const query = { + roles: { $in: roles }, + }; + + return this.countDocuments(query); + } + findPaginatedUsersInRoles(roles, options) { roles = [].concat(roles); @@ -3074,6 +3084,16 @@ export class UsersRaw extends BaseRaw { return this.find(query, options); } + countAllUsersWithPendingAvatar() { + const query = { + _pendingAvatarUrl: { + $exists: true, + }, + }; + + return this.countDocuments(query); + } + updateCustomFieldsById(userId, customFields) { return this.updateOne( { _id: userId }, diff --git a/apps/meteor/server/startup/initialData.js b/apps/meteor/server/startup/initialData.js index cefed3eef922..db8ebeddc8cc 100644 --- a/apps/meteor/server/startup/initialData.js +++ b/apps/meteor/server/startup/initialData.js @@ -1,9 +1,8 @@ -import { Settings, Rooms, Users } from '@rocket.chat/models'; +import { Settings, Rooms, Users, Roles } from '@rocket.chat/models'; import colors from 'colors/safe'; import { Accounts } from 'meteor/accounts-base'; import { Meteor } from 'meteor/meteor'; -import { getUsersInRole } from '../../app/authorization/server'; import { FileUpload } from '../../app/file-upload/server'; import { RocketChatFile } from '../../app/file/server'; import { addUserToDefaultChannels } from '../../app/lib/server/functions/addUserToDefaultChannels'; @@ -15,7 +14,7 @@ import { addUserRolesAsync } from '../lib/roles/addUserRoles'; export async function insertAdminUserFromEnv() { if (process.env.ADMIN_PASS) { - if ((await (await getUsersInRole('admin')).count()) === 0) { + if ((await Roles.countUsersInRole('admin')) === 0) { const adminUser = { name: 'Administrator', username: 'admin', @@ -188,7 +187,7 @@ Meteor.startup(async () => { } } - if ((await (await getUsersInRole('admin')).count()) === 0) { + if ((await Roles.countUsersInRole('admin')) === 0) { const oldestUser = await Users.getOldest({ projection: { _id: 1, username: 1, name: 1 } }); if (oldestUser) { @@ -197,7 +196,7 @@ Meteor.startup(async () => { } } - if ((await (await getUsersInRole('admin')).count()) !== 0) { + if ((await Roles.countUsersInRole('admin')) !== 0) { if (settings.get('Show_Setup_Wizard') === 'pending') { console.log('Setting Setup Wizard to "in_progress" because, at least, one admin was found'); diff --git a/apps/meteor/tests/unit/server/startup/initialData.tests.ts b/apps/meteor/tests/unit/server/startup/initialData.tests.ts index c9980db5ba3e..b139ea571e1a 100644 --- a/apps/meteor/tests/unit/server/startup/initialData.tests.ts +++ b/apps/meteor/tests/unit/server/startup/initialData.tests.ts @@ -3,7 +3,6 @@ import { beforeEach, it } from 'mocha'; import proxyquire from 'proxyquire'; import sinon from 'sinon'; -const getUsersInRole = sinon.stub(); const checkUsernameAvailability = sinon.stub(); const validateEmail = sinon.stub(); const addUserRolesAsync = sinon.stub(); @@ -14,6 +13,9 @@ const models = { create: sinon.stub(), findOneByEmailAddress: sinon.stub(), }, + Roles: { + countUsersInRole: sinon.stub(), + }, }; const setPasswordAsync = sinon.stub(); const settingsGet = sinon.stub(); @@ -29,9 +31,6 @@ const { insertAdminUserFromEnv } = proxyquire.noCallThru().load('../../../../ser startup: sinon.stub(), }, }, - '../../app/authorization/server': { - getUsersInRole, - }, '../../app/file-upload/server': {}, '../../app/file/server': {}, '../../app/lib/server/functions/addUserToDefaultChannels': {}, @@ -52,7 +51,6 @@ const { insertAdminUserFromEnv } = proxyquire.noCallThru().load('../../../../ser describe('insertAdminUserFromEnv', () => { beforeEach(() => { - getUsersInRole.reset(); checkUsernameAvailability.reset(); validateEmail.reset(); addUserRolesAsync.reset(); @@ -61,30 +59,30 @@ describe('insertAdminUserFromEnv', () => { setPasswordAsync.reset(); settingsGet.reset(); process.env.ADMIN_PASS = 'pass'; + models.Roles.countUsersInRole.reset(); }); it('should do nothing if process.env.ADMIN_PASS is empty', async () => { process.env.ADMIN_PASS = ''; const result = await insertAdminUserFromEnv(); - expect(getUsersInRole.called).to.be.false; expect(result).to.be.undefined; }); it('should do nothing if theres already an admin user', async () => { - getUsersInRole.returns({ count: () => 1 }); + models.Roles.countUsersInRole.resolves(1); const result = await insertAdminUserFromEnv(); - expect(getUsersInRole.called).to.be.true; + expect(models.Roles.countUsersInRole.called).to.be.true; expect(validateEmail.called).to.be.false; expect(result).to.be.undefined; }); it('should try to validate an email when process.env.ADMIN_EMAIL is set', async () => { process.env.ADMIN_EMAIL = 'email'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(false); models.Users.create.returns({ insertedId: 'newuserid' }); const result = await insertAdminUserFromEnv(); - expect(getUsersInRole.called).to.be.true; + expect(models.Roles.countUsersInRole.called).to.be.true; expect(validateEmail.called).to.be.true; expect(validateEmail.calledWith('email')).to.be.true; expect(models.Users.create.called).to.be.true; @@ -94,7 +92,7 @@ describe('insertAdminUserFromEnv', () => { it('should override the admins name when process.env.ADMIN_NAME is set', async () => { process.env.ADMIN_EMAIL = 'email'; process.env.ADMIN_NAME = 'name'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); validateEmail.returns(false); models.Users.create.returns({ insertedId: 'newuserid' }); @@ -117,7 +115,7 @@ describe('insertAdminUserFromEnv', () => { }); it('should ignore the admin email when another user already has it set', async () => { process.env.ADMIN_EMAIL = 'email'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); models.Users.create.returns({ insertedId: 'newuserid' }); models.Users.findOneByEmailAddress.returns({ _id: 'someuser' }); @@ -128,7 +126,7 @@ describe('insertAdminUserFromEnv', () => { }); it('should add the email from env when its valid and no users are using it', async () => { process.env.ADMIN_EMAIL = 'email'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); models.Users.create.returns({ insertedId: 'newuserid' }); models.Users.findOneByEmailAddress.returns(undefined); @@ -142,7 +140,7 @@ describe('insertAdminUserFromEnv', () => { it('should mark the admin email as verified when process.env.ADMIN_EMAIL_VERIFIED is set to true', async () => { process.env.ADMIN_EMAIL = 'email'; process.env.ADMIN_EMAIL_VERIFIED = 'true'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); models.Users.create.returns({ insertedId: 'newuserid' }); models.Users.findOneByEmailAddress.returns(undefined); @@ -155,7 +153,7 @@ describe('insertAdminUserFromEnv', () => { }); it('should validate a username with setting UTF8_User_Names_Validation when process.env.ADMIN_USERNAME is set', async () => { process.env.ADMIN_USERNAME = '1234'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); settingsGet.returns('[0-9]+'); models.Users.create.returns({ insertedId: 'newuserid' }); @@ -166,7 +164,7 @@ describe('insertAdminUserFromEnv', () => { }); it('should override the username from admin if the env ADMIN_USERNAME is set, is valid and the username is available', async () => { process.env.ADMIN_USERNAME = '1234'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); settingsGet.returns('[0-9]+'); checkUsernameAvailability.returns(true); @@ -178,7 +176,7 @@ describe('insertAdminUserFromEnv', () => { }); it('should ignore the username when it does not pass setting regexp validation', async () => { process.env.ADMIN_USERNAME = '1234'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); settingsGet.returns('[A-Z]+'); checkUsernameAvailability.returns(true); @@ -194,7 +192,7 @@ describe('insertAdminUserFromEnv', () => { process.env.ADMIN_USERNAME = '1234'; process.env.ADMIN_EMAIL_VERIFIED = 'true'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); validateEmail.returns(true); settingsGet.returns('[0-9]+'); checkUsernameAvailability.returns(true); @@ -212,7 +210,7 @@ describe('insertAdminUserFromEnv', () => { process.env.ADMIN_NAME = 'name'; process.env.ADMIN_USERNAME = '$$$$$$'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); settingsGet.returns('['); checkUsernameAvailability.returns(true); models.Users.create.returns({ insertedId: 'newuserid' }); @@ -224,7 +222,7 @@ describe('insertAdminUserFromEnv', () => { it('should ignore the username when is not available', async () => { process.env.ADMIN_USERNAME = '1234'; - getUsersInRole.returns({ count: () => 0 }); + models.Roles.countUsersInRole.resolves(0); checkUsernameAvailability.throws('some error'); models.Users.create.returns({ insertedId: 'newuserid' }); diff --git a/packages/model-typings/src/models/IExportOperationsModel.ts b/packages/model-typings/src/models/IExportOperationsModel.ts index c834f1bc46ef..c6ef5212b23d 100644 --- a/packages/model-typings/src/models/IExportOperationsModel.ts +++ b/packages/model-typings/src/models/IExportOperationsModel.ts @@ -9,4 +9,5 @@ export interface IExportOperationsModel extends IBaseModel { findLastOperationByUser(userId: string, fullExport: boolean): Promise; findAllPendingBeforeMyRequest(requestDay: Date): FindCursor; updateOperation(data: IExportOperation): Promise; + countAllPendingBeforeMyRequest(requestDay: Date): Promise; } diff --git a/packages/model-typings/src/models/IRolesModel.ts b/packages/model-typings/src/models/IRolesModel.ts index a6e8354a0bc0..f039253f7bc3 100644 --- a/packages/model-typings/src/models/IRolesModel.ts +++ b/packages/model-typings/src/models/IRolesModel.ts @@ -61,4 +61,5 @@ export interface IRolesModel extends IBaseModel { ): Promise; canAddUserToRole(uid: IUser['_id'], roleId: IRole['_id'], scope?: IRoom['_id']): Promise; + countUsersInRole(roleId: IRole['_id'], scope?: IRoom['_id']): Promise; } diff --git a/packages/model-typings/src/models/IRoomsModel.ts b/packages/model-typings/src/models/IRoomsModel.ts index 6419d021887e..d1235fc58958 100644 --- a/packages/model-typings/src/models/IRoomsModel.ts +++ b/packages/model-typings/src/models/IRoomsModel.ts @@ -308,4 +308,5 @@ export interface IRoomsModel extends IBaseModel { e2eKeyId: string, e2eQueue?: IRoom['usersWaitingForE2EKeys'], ): Promise>; + countGroupDMsByUids(uids: NonNullable): Promise; } diff --git a/packages/model-typings/src/models/ISubscriptionsModel.ts b/packages/model-typings/src/models/ISubscriptionsModel.ts index 27c5f80e20e7..67a21945261c 100644 --- a/packages/model-typings/src/models/ISubscriptionsModel.ts +++ b/packages/model-typings/src/models/ISubscriptionsModel.ts @@ -321,4 +321,5 @@ export interface ISubscriptionsModel extends IBaseModel { countByRoomIdAndNotUserId(rid: string, uid: string): Promise; countByRoomIdWhenUsernameExists(rid: string): Promise; setE2EKeyByUserIdAndRoomId(userId: string, rid: string, key: string): Promise>; + countUsersInRoles(roles: IRole['_id'][], rid: IRoom['_id'] | undefined): Promise; } diff --git a/packages/model-typings/src/models/IUsersModel.ts b/packages/model-typings/src/models/IUsersModel.ts index 83c499a1356e..6029b4913f2d 100644 --- a/packages/model-typings/src/models/IUsersModel.ts +++ b/packages/model-typings/src/models/IUsersModel.ts @@ -406,4 +406,6 @@ export interface IUsersModel extends IBaseModel { setFreeSwitchExtension(userId: string, extension: string | undefined): Promise; findAssignedFreeSwitchExtensions(): FindCursor; findUsersWithAssignedFreeSwitchExtensions(options?: FindOptions): FindCursor; + countUsersInRoles(roles: IRole['_id'][]): Promise; + countAllUsersWithPendingAvatar(): Promise; } diff --git a/yarn.lock b/yarn.lock index 663944e2b63f..75e77637a557 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8133,8 +8133,6 @@ __metadata: typescript: "npm:~5.1.6" uglify-es: "npm:^3.3.10" uuid: "npm:~8.3.2" - peerDependencies: - "@rocket.chat/ui-kit": "workspace:^" languageName: unknown linkType: soft