Skip to content

Commit

Permalink
chore: remove query field on groups messages listing
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardogarim committed Oct 26, 2024
1 parent 3759af8 commit c39e1f0
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 40 deletions.
21 changes: 16 additions & 5 deletions apps/meteor/app/api/server/v1/groups.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Team, isMeteorError } from '@rocket.chat/core-services';
import type { IIntegration, IUser, IRoom, RoomType } from '@rocket.chat/core-typings';
import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models';
import { isGroupsOnlineProps } from '@rocket.chat/rest-typings';
import { isGroupsOnlineProps, isGroupsMessagesProps } from '@rocket.chat/rest-typings';
import { check, Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
import type { Filter } from 'mongodb';
Expand Down Expand Up @@ -746,19 +746,30 @@ API.v1.addRoute(

API.v1.addRoute(
'groups.messages',
{ authRequired: true },
{ authRequired: true, validateParams: isGroupsMessagesProps },
{
async get() {
const { roomId, mentionIds, starredIds, pinned } = this.queryParams;

const findResult = await findPrivateGroupByIdOrName({
params: this.queryParams,
params: { roomId },
userId: this.userId,
});
const { offset, count } = await getPaginationItems(this.queryParams);
const { sort, fields, query } = await this.parseJsonQuery();

const ourQuery = Object.assign({}, query, { rid: findResult.rid });
const parseIds = (ids: string | undefined, field: string) =>
typeof ids === 'string' && ids ? { [field]: { $in: ids.split(',').map((id) => id.trim()) } } : {};

const ourQuery = {
...query,
rid: findResult.rid,
...parseIds(mentionIds, 'mentions._id'),
...parseIds(starredIds, 'starred._id'),
...(pinned && pinned.toLowerCase() === 'true' ? { pinned: true } : {}),
};

const { cursor, totalCount } = await Messages.findPaginated(ourQuery, {
const { cursor, totalCount } = Messages.findPaginated(ourQuery, {
sort: sort || { ts: -1 },
skip: offset,
limit: count,
Expand Down
12 changes: 12 additions & 0 deletions apps/meteor/tests/data/chat.helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ export const sendSimpleMessage = ({
return request.post(api('chat.sendMessage')).set(credentials).send({ message });
};

export const sendMessage = ({ message }: { message: { rid: IRoom['_id']; msg: string } & Partial<Omit<IMessage, 'rid' | 'msg'>> }) => {
return request.post(api('chat.sendMessage')).set(credentials).send({ message });
};

export const starMessage = ({ messageId }: { messageId: IMessage['_id'] }) => {
return request.post(api('chat.starMessage')).set(credentials).send({ messageId });
};

export const pinMessage = ({ messageId }: { messageId: IMessage['_id'] }) => {
return request.post(api('chat.pinMessage')).set(credentials).send({ messageId });
};

export const deleteMessage = ({ roomId, msgId }: { roomId: IRoom['_id']; msgId: IMessage['_id'] }) => {
if (!roomId) {
throw new Error('"roomId" is required in "deleteMessage" test helper');
Expand Down
141 changes: 141 additions & 0 deletions apps/meteor/tests/end-to-end/api/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { assert, expect } from 'chai';
import { after, before, describe, it } from 'mocha';

import { getCredentials, api, request, credentials, apiPrivateChannelName } from '../../data/api-data';
import { pinMessage, starMessage, sendMessage } from '../../data/chat.helper';
import { CI_MAX_ROOMS_PER_GUEST as maxRoomsPerGuest } from '../../data/constants';
import { createGroup, deleteGroup } from '../../data/groups.helper';

Check failure on line 9 in apps/meteor/tests/end-to-end/api/groups.ts

View workflow job for this annotation

GitHub Actions / 🔎 Code Check / Code Lint

Unable to resolve path to module '../../data/groups.helper'
import { createIntegration, removeIntegration } from '../../data/integration.helper';
import { updatePermission, updateSetting } from '../../data/permissions.helper';
import { createRoom } from '../../data/rooms.helper';
Expand Down Expand Up @@ -494,6 +496,145 @@ describe('[Groups]', () => {
});
});

describe('/groups.messages', () => {
let testGroup: IRoom;
let firstUser: IUser;
let secondUser: IUser;

before(async () => {
testGroup = (await createGroup({ name: `test-group-${Date.now()}` })).body.group;
firstUser = await createUser({ joinDefaultChannels: false });
secondUser = await createUser({ joinDefaultChannels: false });

const messages = [
{
rid: testGroup._id,
msg: `@${firstUser.username} youre being mentioned`,
mentions: [{ username: firstUser.username, _id: firstUser._id, name: firstUser.name }],
},
{
rid: testGroup._id,
msg: `@${secondUser.username} youre being mentioned`,
mentions: [{ username: secondUser.username, _id: secondUser._id, name: secondUser.name }],
},
{
rid: testGroup._id,
msg: `A simple message`,
},
{
rid: testGroup._id,
msg: `A pinned simple message`,
},
];

const [, , starredMessage, pinnedMessage] = await Promise.all(messages.map((message) => sendMessage({ message })));

await Promise.all([
starMessage({ messageId: starredMessage.body.message._id }),
pinMessage({ messageId: pinnedMessage.body.message._id }),
]);
});

after(async () => {
await deleteGroup({ roomName: testGroup.name });
});

it('should return all messages from a group', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: testGroup._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
expect(res.body.messages).to.have.lengthOf(5);
});
});

it('should return messages that mention a single user', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
mentionIds: firstUser._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('mentions').that.is.an('array').and.to.have.lengthOf(1);
expect(res.body.messages[0].mentions[0]).to.have.property('_id', firstUser._id);
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});

it('should return messages that mention multiple users', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
mentionIds: `${firstUser._id},${secondUser._id}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(2);
expect(res.body).to.have.property('count', 2);
expect(res.body).to.have.property('total', 2);

const mentionIds = res.body.messages.map((message: any) => message.mentions[0]._id);
expect(mentionIds).to.include.members([firstUser._id, secondUser._id]);
});
});

it('should return messages that are starred by a specific user', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
starredIds: 'rocketchat.internal.admin.test',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('starred').that.is.an('array').and.to.have.lengthOf(1);
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});

it('should return messages that are pinned', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
pinned: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('pinned').that.is.an('boolean').and.to.be.true;
expect(res.body.messages[0]).to.have.nested.property('pinnedBy').that.is.an('object');
expect(res.body.messages[0].pinnedBy).to.have.property('_id', 'rocketchat.internal.admin.test');
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});
});

describe('/groups.invite', async () => {
let roomInfo: { group: IRoom };

Expand Down
32 changes: 0 additions & 32 deletions packages/rest-typings/src/v1/groups/GroupsMessageProps.ts

This file was deleted.

50 changes: 50 additions & 0 deletions packages/rest-typings/src/v1/groups/GroupsMessagesProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { IRoom } from '@rocket.chat/core-typings';
import Ajv from 'ajv';

import type { PaginatedRequest } from '../../helpers/PaginatedRequest';
import { withGroupBaseProperties } from './BaseProps';

const ajv = new Ajv({
coerceTypes: true,
});

export type GroupsMessagesProps = PaginatedRequest<{
roomId: IRoom['_id'];
mentionIds?: string;
starredIds?: string;
pinned?: boolean;
query?: Record<string, any>;
}>;

const GroupsMessagesPropsSchema = withGroupBaseProperties({
roomId: {
type: 'string',
},
mentionIds: {
type: 'string',
},
starredIds: {
type: 'string',
},
pinned: {
type: 'string',
},
count: {
type: 'number',
nullable: true,
},
offset: {
type: 'number',
nullable: true,
},
sort: {
type: 'string',
nullable: true,
},
query: {
type: 'string',
nullable: true,
},
});

export const isGroupsMessagesProps = ajv.compile<GroupsMessagesProps>(GroupsMessagesPropsSchema);
4 changes: 2 additions & 2 deletions packages/rest-typings/src/v1/groups/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import type { GroupsKickProps } from './GroupsKickProps';
import type { GroupsLeaveProps } from './GroupsLeaveProps';
import type { GroupsListProps } from './GroupsListProps';
import type { GroupsMembersProps } from './GroupsMembersProps';
import type { GroupsMessageProps } from './GroupsMessageProps';
import type { GroupsMessagesProps } from './GroupsMessagesProps';
import type { GroupsModeratorsProps } from './GroupsModeratorsProps';
import type { GroupsOnlineProps } from './GroupsOnlineProps';
import type { GroupsOpenProps } from './GroupsOpenProps';
Expand Down Expand Up @@ -99,7 +99,7 @@ export type GroupsEndpoints = {
GET: (params: GroupsRolesProps) => { roles: IGetRoomRoles[] };
};
'/v1/groups.messages': {
GET: (params: GroupsMessageProps) => PaginatedResult<{
GET: (params: GroupsMessagesProps) => PaginatedResult<{
messages: IMessage[];
}>;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/rest-typings/src/v1/groups/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export * from './GroupsFilesProps';
export * from './GroupsKickProps';
export * from './GroupsLeaveProps';
export * from './GroupsMembersProps';
export * from './GroupsMessageProps';
export * from './GroupsMessagesProps';
export * from './GroupsRolesProps';
export * from './GroupsUnarchiveProps';
export * from './GroupsAddAllProps';
Expand Down

0 comments on commit c39e1f0

Please sign in to comment.