diff --git a/.changeset/calm-penguins-do.md b/.changeset/calm-penguins-do.md new file mode 100644 index 0000000000000..6a83fae9030d8 --- /dev/null +++ b/.changeset/calm-penguins-do.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Now we are considering channels with auto-join inside teams on user creation diff --git a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts index 835f59419ad58..563022ea5a6af 100644 --- a/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts +++ b/apps/meteor/app/lib/server/functions/addUserToDefaultChannels.ts @@ -1,15 +1,15 @@ import { Message } from '@rocket.chat/core-services'; import type { IUser } from '@rocket.chat/core-typings'; -import { Subscriptions, Rooms } from '@rocket.chat/models'; +import { Subscriptions } from '@rocket.chat/models'; import { callbacks } from '../../../../lib/callbacks'; import { getSubscriptionAutotranslateDefaultConfig } from '../../../../server/lib/getSubscriptionAutotranslateDefaultConfig'; +import { getDefaultChannels } from './getDefaultChannels'; export const addUserToDefaultChannels = async function (user: IUser, silenced?: boolean): Promise { await callbacks.run('beforeJoinDefaultChannels', user); - const defaultRooms = await Rooms.findByDefaultAndTypes(true, ['c', 'p'], { - projection: { usernames: 0 }, - }).toArray(); + const defaultRooms = await getDefaultChannels(); + for await (const room of defaultRooms) { if (!(await Subscriptions.findOneByRoomIdAndUserId(room._id, user._id, { projection: { _id: 1 } }))) { const autoTranslateConfig = getSubscriptionAutotranslateDefaultConfig(user); diff --git a/apps/meteor/app/lib/server/functions/getDefaultChannels.ts b/apps/meteor/app/lib/server/functions/getDefaultChannels.ts new file mode 100644 index 0000000000000..e47518dd604f5 --- /dev/null +++ b/apps/meteor/app/lib/server/functions/getDefaultChannels.ts @@ -0,0 +1,25 @@ +import type { IRoom } from '@rocket.chat/core-typings'; +import { Rooms } from '@rocket.chat/models'; + +export async function getDefaultChannels(): Promise { + const defaultRooms = await Rooms.findByDefaultAndTypes(true, ['c', 'p'], { + projection: { usernames: 0 }, + }).toArray(); + const roomsThatAreGoingToBeJoined = new Set(defaultRooms.map((room) => room._id)); + + // If any of those are teams, we need to get all the channels that have the auto-join flag as well + const teamRooms = defaultRooms.filter((room) => room.teamMain && room.teamId); + if (teamRooms.length > 0) { + for await (const teamRoom of teamRooms) { + const defaultTeamRooms = await Rooms.findDefaultRoomsForTeam(teamRoom.teamId).toArray(); + + const defaultTeamRoomsThatWereNotAlreadyAdded = defaultTeamRooms.filter((channel) => !roomsThatAreGoingToBeJoined.has(channel._id)); + + defaultTeamRoomsThatWereNotAlreadyAdded.forEach((channel) => roomsThatAreGoingToBeJoined.add(channel._id)); + // Add the channels to the defaultRooms list + defaultRooms.push(...defaultTeamRoomsThatWereNotAlreadyAdded); + } + } + + return defaultRooms; +} diff --git a/apps/meteor/tests/data/rooms.helper.js b/apps/meteor/tests/data/rooms.helper.js index 78e07f1100393..c28f763c00f97 100644 --- a/apps/meteor/tests/data/rooms.helper.js +++ b/apps/meteor/tests/data/rooms.helper.js @@ -1,6 +1,6 @@ import { api, credentials, request } from './api-data'; -export const createRoom = ({ name, type, username, token, agentId, members, credentials: customCredentials, voipCallDirection = 'inbound' }) => { +export const createRoom = ({ name, type, username, token, agentId, members, credentials: customCredentials, extraData, voipCallDirection = 'inbound' }) => { if (!type) { throw new Error('"type" is required in "createRoom.ts" test helper'); } @@ -31,6 +31,7 @@ export const createRoom = ({ name, type, username, token, agentId, members, cred .send({ ...params, ...(members && { members }), + ...(extraData && { extraData }), }); }; @@ -65,3 +66,17 @@ function actionRoom({ action, type, roomId }) { export const deleteRoom = ({ type, roomId }) => actionRoom({ action: 'delete', type, roomId }); export const closeRoom = ({ type, roomId }) => actionRoom({ action: 'close', type, roomId }); + +export const setRoomConfig = ({ roomId, favorite, isDefault }) => { + return request + .post(api('rooms.saveRoomSettings')) + .set(credentials) + .send({ + rid: roomId, + default: isDefault, + favorite: favorite ? { + defaultValue: true, + favorite: false + } : undefined + }); +}; diff --git a/apps/meteor/tests/data/uploads.helper.ts b/apps/meteor/tests/data/uploads.helper.ts index 8eb1e79319650..eabd49e11a3b1 100644 --- a/apps/meteor/tests/data/uploads.helper.ts +++ b/apps/meteor/tests/data/uploads.helper.ts @@ -45,6 +45,7 @@ export async function testFileUploads(filesEndpoint: 'channels.files' | 'groups. name: null, username: null, members: null, + extraData: null, }); return roomResponse.body.room; diff --git a/apps/meteor/tests/end-to-end/api/01-users.js b/apps/meteor/tests/end-to-end/api/01-users.js index 1aa30d3322bb3..204d1413155f1 100644 --- a/apps/meteor/tests/end-to-end/api/01-users.js +++ b/apps/meteor/tests/end-to-end/api/01-users.js @@ -12,7 +12,8 @@ import { imgURL } from '../../data/interactions'; import { createAgent, makeAgentAvailable } from '../../data/livechat/rooms'; import { removeAgent, getAgent } from '../../data/livechat/users'; import { updatePermission, updateSetting } from '../../data/permissions.helper'; -import { createRoom, deleteRoom } from '../../data/rooms.helper'; +import { createRoom, deleteRoom, setRoomConfig } from '../../data/rooms.helper'; +import { createTeam, deleteTeam } from '../../data/teams.helper'; import { adminEmail, preferences, password, adminUsername } from '../../data/user'; import { createUser, login, deleteUser, getUserStatus, getUserByUsername } from '../../data/users.helper.js'; @@ -279,6 +280,162 @@ describe('[Users]', function () { .end(done); }); }); + + describe('auto join default channels', () => { + let defaultTeamRoomId; + let defaultTeamId; + let group; + let user; + let userCredentials; + let user2; + let user3; + let userNoDefault; + const teamName = `defaultTeam_${Date.now()}`; + + before(async () => { + const defaultTeam = await createTeam(credentials, teamName, 0); + defaultTeamRoomId = defaultTeam.roomId; + defaultTeamId = defaultTeam._id; + }); + + before(async () => { + const { body } = await createRoom({ + name: `defaultGroup_${Date.now()}`, + type: 'p', + credentials, + extraData: { + broadcast: false, + encrypted: false, + teamId: defaultTeamId, + topic: '', + }, + }); + group = body.group; + }); + + after(() => + Promise.all([ + deleteRoom({ roomId: group._id, type: 'p' }), + deleteTeam(credentials, teamName), + deleteUser(user), + deleteUser(user2), + deleteUser(user3), + deleteUser(userNoDefault), + ]), + ); + + it('should not create subscriptions to non default teams or rooms even if joinDefaultChannels is true', async () => { + userNoDefault = await createUser({ joinDefaultChannels: true }); + const noDefaultUserCredentials = await login(userNoDefault.username, password); + await request + .get(api('subscriptions.getOne')) + .set(noDefaultUserCredentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + + await request + .get(api('subscriptions.getOne')) + .set(noDefaultUserCredentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + + it('should create a subscription for a default team room if joinDefaultChannels is true', async () => { + await setRoomConfig({ roomId: defaultTeamRoomId, favorite: true, isDefault: true }); + + user = await createUser({ joinDefaultChannels: true }); + userCredentials = await login(user.username, password); + await request + .get(api('subscriptions.getOne')) + .set(userCredentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', defaultTeamRoomId); + }); + }); + + it('should NOT create a subscription for non auto-join rooms inside a default team', async () => { + await request + .get(api('subscriptions.getOne')) + .set(userCredentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + + it('should create a subscription for the user in all the auto join rooms of the team', async () => { + await request.post(api('teams.updateRoom')).set(credentials).send({ + roomId: group._id, + isDefault: true, + }); + + user2 = await createUser({ joinDefaultChannels: true }); + const user2Credentials = await login(user2.username, password); + + await request + .get(api('subscriptions.getOne')) + .set(user2Credentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', group._id); + }); + }); + + it('should create a subscription for a default room inside a non default team', async () => { + await setRoomConfig({ roomId: defaultTeamRoomId, isDefault: false }); + await setRoomConfig({ roomId: group._id, favorite: true, isDefault: true }); + + user3 = await createUser({ joinDefaultChannels: true }); + const user3Credentials = await login(user3.username, password); + + // New user should be subscribed to the default room inside a team + await request + .get(api('subscriptions.getOne')) + .set(user3Credentials) + .query({ roomId: group._id }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription'); + expect(res.body.subscription).to.have.property('rid', group._id); + }); + + // New user should not be subscribed to the parent team + await request + .get(api('subscriptions.getOne')) + .set(user3Credentials) + .query({ roomId: defaultTeamRoomId }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success', true); + expect(res.body).to.have.property('subscription').that.is.null; + }); + }); + }); }); describe('[/users.register]', () => { diff --git a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts index 579e9894ab7bd..fb7f42ccdabdb 100644 --- a/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts +++ b/apps/meteor/tests/end-to-end/apps/05-video-conferences.ts @@ -25,6 +25,7 @@ describe('Apps - Video Conferences', function () { agentId: undefined, members: undefined, credentials: undefined, + extraData: undefined, }); roomId = res.body.group._id;