Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7e1c022
chore: added types for rooms.close endpoint
aleksandernsilva Dec 17, 2024
0013cdd
feat: added rooms.close endpoint
aleksandernsilva Dec 17, 2024
4615d04
feat: added checks
aleksandernsilva Jan 3, 2025
bb86b9e
test: added e2e api tests
aleksandernsilva Jan 3, 2025
0242718
refactor: adjusted error response
aleksandernsilva Feb 4, 2025
6ef6a5d
test: added more test cases
aleksandernsilva Feb 4, 2025
ca8f84c
test: changed to use promisses
aleksandernsilva Feb 4, 2025
86e48fa
refactor: changed error messages to follow error-xxxx pattern
aleksandernsilva Feb 4, 2025
d716f93
refactor: added a projection to subscription find
aleksandernsilva Feb 4, 2025
7a78061
fix: adjusted projection
aleksandernsilva Feb 4, 2025
b2fee10
test: adjusted error messages
aleksandernsilva Feb 4, 2025
3df5f6b
chore: changeset
aleksandernsilva Feb 10, 2025
526abdf
refactor: renamed `rooms.close` to `rooms.hide`
aleksandernsilva Feb 10, 2025
9bec9ce
refactor: adjusted error message
aleksandernsilva Feb 10, 2025
c13c2ba
refactor: changed user query projection
aleksandernsilva Feb 10, 2025
7c74480
test: updated error message
aleksandernsilva Feb 11, 2025
1009b2a
feat: added method findOneOpenByRoomIdAndUserId
aleksandernsilva Feb 11, 2025
f18a2ae
chore: updated changeset
aleksandernsilva Feb 11, 2025
4965533
refactor: removed method findOneOpenByRoomIdAndUserId
aleksandernsilva Feb 12, 2025
7d30b3b
fix: hideByRoomAndUserId to update only when sub is closed
aleksandernsilva Feb 12, 2025
000dd77
refactor: removed find added count check
aleksandernsilva Feb 12, 2025
bcaac55
test: cleanup users after tests
aleksandernsilva Feb 12, 2025
c7166bd
refactor: removed findOneOpenByRoomIdAndUserId type
aleksandernsilva Feb 12, 2025
8625bce
chore: updated changeset
aleksandernsilva Feb 12, 2025
79b2c70
test: added clean up for roomB
aleksandernsilva Feb 13, 2025
856ea60
Merge branch 'develop' into feat/rooms-close-endpoint
kodiakhq[bot] Feb 14, 2025
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
36 changes: 36 additions & 0 deletions apps/meteor/app/api/server/v1/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
isRoomsCleanHistoryProps,
isRoomsOpenProps,
isRoomsMembersOrderedByRoleProps,
isRoomsCloseProps,
} from '@rocket.chat/rest-typings';
import { Meteor } from 'meteor/meteor';

Expand All @@ -21,6 +22,7 @@ import * as dataExport from '../../../../server/lib/dataExport';
import { eraseRoom } from '../../../../server/lib/eraseRoom';
import { findUsersOfRoomOrderedByRole } from '../../../../server/lib/findUsersOfRoomOrderedByRole';
import { openRoom } from '../../../../server/lib/openRoom';
import { hideRoomMethod } from '../../../../server/methods/hideRoom';
import { muteUserInRoom } from '../../../../server/methods/muteUserInRoom';
import { unmuteUserInRoom } from '../../../../server/methods/unmuteUserInRoom';
import { canAccessRoomAsync, canAccessRoomIdAsync } from '../../../authorization/server/functions/canAccessRoom';
Expand Down Expand Up @@ -962,3 +964,37 @@ API.v1.addRoute(
},
},
);

API.v1.addRoute(
'rooms.close',
Comment thread
aleksandernsilva marked this conversation as resolved.
Outdated
{ authRequired: true, validateParams: isRoomsCloseProps },
{
async post() {
const { roomId } = this.bodyParams;

if (!(await canAccessRoomIdAsync(roomId, this.userId))) {
return API.v1.unauthorized();
}

const user = await Users.findOneById(this.userId, { projections: { username: 1 } });
Comment thread
aleksandernsilva marked this conversation as resolved.
Outdated

if (!user) {
return API.v1.failure('error-invalid-user');
}

const subscription = await Subscriptions.findOneByRoomIdAndUserId(roomId, this.userId);
Comment thread
aleksandernsilva marked this conversation as resolved.
Outdated

if (!subscription) {
return API.v1.failure(`error-invalid-subscription`);
}

if (!subscription.open) {
return API.v1.failure('error-room-already-closed');
Comment thread
aleksandernsilva marked this conversation as resolved.
Outdated
}

await hideRoomMethod(this.userId, roomId);

return API.v1.success();
},
},
);
99 changes: 99 additions & 0 deletions apps/meteor/tests/end-to-end/api/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4204,4 +4204,103 @@ describe('[Rooms]', () => {
});
});
});

describe('/rooms.close', () => {
let roomA: IRoom;
let roomB: IRoom;
const roomName = `rooms.close.test.${Date.now()}`;
let memberA: TestUser<IUser>;
let memberB: TestUser<IUser>;
let nonMember: TestUser<IUser>;
let nonMemberCredentials: Credentials;

before(async () => {
memberA = await createUser();
memberB = await createUser();
nonMember = await createUser();
nonMemberCredentials = await login(nonMember.username, password);
});

before(async () => {
roomA = (await createRoom({ type: 'c', name: roomName, members: [memberA.username, memberB.username] })).body.channel;
roomB = (await createRoom({ type: 'd', username: memberB.username })).body.room;
Comment thread
aleksandernsilva marked this conversation as resolved.
});

after(async () => {
await deleteRoom({ type: 'c', roomId: roomA._id });
});

it('should close the room', async () => {
await request
.post(api('rooms.close'))
.set(credentials)
.send({ roomId: roomA._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});

it('should be already closed', async () => {
await request
.post(api('rooms.close'))
.set(credentials)
.send({ roomId: roomA._id })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', `The room, ${roomName}, is already closed`);
});
});

it('should fail if roomId is not provided', async () => {
await request
.post(api('rooms.close'))
.set(credentials)
.send()
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res: Response) => {
expect(res.body).to.have.property('success', false);
});
});

it('should return 401 if user is not logged in', async () => {
await request
.post(api('rooms.close'))
.expect('Content-Type', 'application/json')
.expect(401)
.expect((res) => {
expect(res.body).to.have.property('status', 'error');
expect(res.body).to.have.property('message');
});
});

it('should fail if user not subscribed to the room', async () => {
await request
.post(api('rooms.close'))
.set(nonMemberCredentials)
.send({ roomId: roomA._id })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'The user is not subscribed to the room');
});
});

it('should return forbidden if user does not have access to the room', async () => {
await request
.post(api('rooms.close'))
.set(nonMemberCredentials)
.send({ roomId: roomB._id })
.expect('Content-Type', 'application/json')
.expect(403)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
});
});
22 changes: 22 additions & 0 deletions packages/rest-typings/src/v1/rooms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,24 @@ const membersOrderedByRoleRolePropsSchema = {

export const isRoomsMembersOrderedByRoleProps = ajv.compile<RoomsMembersOrderedByRoleProps>(membersOrderedByRoleRolePropsSchema);

type RoomsCloseProps = {
roomId: string;
};

const roomsCloseSchema = {
type: 'object',
properties: {
roomId: {
type: 'string',
minLength: 1,
},
},
required: ['roomId'],
additionalProperties: false,
};

export const isRoomsCloseProps = ajv.compile<RoomsCloseProps>(roomsCloseSchema);

export type RoomsEndpoints = {
'/v1/rooms.autocomplete.channelAndPrivate': {
GET: (params: RoomsAutoCompleteChannelAndPrivateProps) => {
Expand Down Expand Up @@ -846,4 +864,8 @@ export type RoomsEndpoints = {
members: (IUser & { roles?: IRole['_id'][] })[];
}>;
};

'/v1/rooms.close': {
POST: (params: RoomsCloseProps) => void;
};
};