Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/nine-news-ring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@rocket.chat/rest-typings': minor
'@rocket.chat/meteor': minor
---

Migrate groups.history with AJV validation and schema types
211 changes: 151 additions & 60 deletions apps/meteor/app/api/server/v1/groups.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
import { Team, isMeteorError } from '@rocket.chat/core-services';
import type { IIntegration, IUser, IRoom, RoomType, UserStatus } from '@rocket.chat/core-typings';
import type { IIntegration, IUser, IRoom, RoomType, UserStatus, IMessage } from '@rocket.chat/core-typings';
import { Integrations, Messages, Rooms, Subscriptions, Uploads, Users } from '@rocket.chat/models';
import { isGroupsOnlineProps, isGroupsMessagesProps, isGroupsFilesProps } from '@rocket.chat/rest-typings';
import type { PaginatedRequest, GroupsBaseProps } from '@rocket.chat/rest-typings';
import {
isGroupsOnlineProps,
isGroupsMessagesProps,
isGroupsFilesProps,
ajv,
validateBadRequestErrorResponse,
validateUnauthorizedErrorResponse,
validateForbiddenErrorResponse,
withGroupBaseProperties,
} from '@rocket.chat/rest-typings';
import { isTruthy } from '@rocket.chat/tools';
import { check, Match } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
Expand Down Expand Up @@ -31,12 +41,144 @@ import { executeGetRoomRoles } from '../../../lib/server/methods/getRoomRoles';
import { leaveRoomMethod } from '../../../lib/server/methods/leaveRoom';
import { executeUnarchiveRoom } from '../../../lib/server/methods/unarchiveRoom';
import { normalizeMessagesForUser } from '../../../utils/server/lib/normalizeMessagesForUser';
import type { ExtractRoutesFromAPI } from '../ApiClass';
import { API } from '../api';
import { addUserToFileObj } from '../helpers/addUserToFileObj';
import { composeRoomWithLastMessage } from '../helpers/composeRoomWithLastMessage';
import { getPaginationItems } from '../helpers/getPaginationItems';
import { getUserFromParams, getUserListFromParams } from '../helpers/getUserFromParams';

type GroupsHistoryProps = PaginatedRequest<
GroupsBaseProps & {
latest?: string;
oldest?: string;
inclusive?: 'true' | 'false';
unreads?: 'true' | 'false';
showThreadMessages?: string;
}
>;
const groupsHistoryPropsSchema = {
type: 'object',
oneOf: [
{
type: 'object',
properties: {
roomId: { type: 'string' },
latest: { type: 'string' },
oldest: { type: 'string' },
inclusive: { type: 'string' },
unreads: { type: 'string' },
showThreadMessages: { type: 'string' },
count: { type: 'string' },
offset: { type: 'string' },
sort: { type: 'string' },
},
required: ['roomId'],
additionalProperties: false,
},
{
type: 'object',
properties: {
roomName: { type: 'string' },
latest: { type: 'string' },
oldest: { type: 'string' },
inclusive: { type: 'string' },
unreads: { type: 'string' },
showThreadMessages: { type: 'string' },
count: { type: 'string' },
offset: { type: 'string' },
sort: { type: 'string' },
},
required: ['roomName'],
additionalProperties: false,
},
],
} as const;

const isGroupsHistoryProps = ajv.compile<GroupsHistoryProps>(groupsHistoryPropsSchema);

const isGroupsHistoryResponse = ajv.compile({
type: 'object',
properties: {
success: { type: 'boolean', enum: [true] },
messages: {
type: 'array',
items: { $ref: '#/components/schemas/IMessage' },
},
count: { type: 'integer' },
offset: { type: 'integer' },
total: { type: 'integer' },
unreadNotLoaded: { type: 'integer' },
firstUnread: { $ref: '#/components/schemas/IMessage' },
},
required: ['success', 'messages'],
additionalProperties: false,
});

const groupsHistoryEndpoints = API.v1.get(
'groups.history',
{
authRequired: true,
query: isGroupsHistoryProps,
response: {
200: isGroupsHistoryResponse,
400: validateBadRequestErrorResponse,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const findResult = await findPrivateGroupByIdOrName({
params: this.queryParams,
userId: this.userId,
checkedArchived: false,
});

let latestDate = new Date();
if (this.queryParams.latest) {
latestDate = new Date(this.queryParams.latest);
}

let oldestDate = undefined;
if (this.queryParams.oldest) {
oldestDate = new Date(this.queryParams.oldest);
}

const inclusive = this.queryParams.inclusive === 'true';

let count = 20;
if (this.queryParams.count) {
count = parseInt(String(this.queryParams.count));
}

let offset = 0;
if (this.queryParams.offset) {
offset = parseInt(String(this.queryParams.offset));
}

const unreads = this.queryParams.unreads === 'true';
const showThreadMessages = this.queryParams.showThreadMessages !== 'false';

const result = await getChannelHistory({
rid: findResult.rid,
fromUserId: this.userId,
latest: latestDate,
oldest: oldestDate,
inclusive,
offset,
count,
unreads,
showThreadMessages,
});

if (!result) {
return API.v1.forbidden('User does not have the permissions required for this action')
}

return API.v1.success(result as { messages: IMessage[]; firstUnread?: IMessage; unreadNotLoaded?: number });
},
);

async function getRoomFromParams(params: { roomId?: string } | { roomName?: string }): Promise<IRoom> {
if (
(!('roomId' in params) && !('roomName' in params)) ||
Expand Down Expand Up @@ -488,64 +630,6 @@ API.v1.addRoute(
},
);

API.v1.addRoute(
'groups.history',
{ authRequired: true },
{
async get() {
const findResult = await findPrivateGroupByIdOrName({
params: this.queryParams,
userId: this.userId,
checkedArchived: false,
});

let latestDate = new Date();
if (this.queryParams.latest) {
latestDate = new Date(this.queryParams.latest);
}

let oldestDate = undefined;
if (this.queryParams.oldest) {
oldestDate = new Date(this.queryParams.oldest);
}

const inclusive = this.queryParams.inclusive === 'true';

let count = 20;
if (this.queryParams.count) {
count = parseInt(String(this.queryParams.count));
}

let offset = 0;
if (this.queryParams.offset) {
offset = parseInt(String(this.queryParams.offset));
}

const unreads = this.queryParams.unreads === 'true';

const showThreadMessages = this.queryParams.showThreadMessages !== 'false';

const result = await getChannelHistory({
rid: findResult.rid,
fromUserId: this.userId,
latest: latestDate,
oldest: oldestDate,
inclusive,
offset,
count,
unreads,
showThreadMessages,
});

if (!result) {
return API.v1.forbidden();
}

return API.v1.success(result);
},
},
);

API.v1.addRoute(
'groups.info',
{ authRequired: true },
Expand Down Expand Up @@ -1300,3 +1384,10 @@ API.v1.addRoute(
},
},
);

export type GroupsHistoryEndpoints = ExtractRoutesFromAPI<typeof groupsHistoryEndpoints>;

declare module '@rocket.chat/rest-typings' {
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
interface Endpoints extends GroupsHistoryEndpoints {}
}
49 changes: 0 additions & 49 deletions packages/rest-typings/src/v1/groups/GroupsHistoryProps.ts

This file was deleted.

6 changes: 0 additions & 6 deletions packages/rest-typings/src/v1/groups/groups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import type { GroupsCreateProps } from './GroupsCreateProps';
import type { GroupsDeleteProps } from './GroupsDeleteProps';
import type { GroupsFilesProps } from './GroupsFilesProps';
import type { GroupsGetIntegrationsProps } from './GroupsGetIntegrationsProps';
import type { GroupsHistoryProps } from './GroupsHistoryProps';
import type { GroupsInfoProps } from './GroupsInfoProps';
import type { GroupsInviteProps } from './GroupsInviteProps';
import type { GroupsKickProps } from './GroupsKickProps';
Expand Down Expand Up @@ -54,11 +53,6 @@ export type GroupsEndpoints = {
total: number;
};
};
'/v1/groups.history': {
GET: (params: GroupsHistoryProps) => PaginatedResult<{
messages: IMessage[];
}>;
};
'/v1/groups.archive': {
POST: (params: GroupsArchiveProps) => void;
};
Expand Down
4 changes: 2 additions & 2 deletions packages/rest-typings/src/v1/groups/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export type * from './groups';

export * from './BaseProps';
export * from './GroupsArchiveProps';
export * from './GroupsCloseProps';
export * from './GroupsConvertToTeamProps';
Expand Down Expand Up @@ -35,5 +36,4 @@ export * from './GroupsSetPurposeProps';
export * from './GroupsSetReadOnlyProps';
export * from './GroupsSetTopicProps';
export * from './GroupsSetTypeProps';
export * from './GroupsModeratorsProps';
export * from './GroupsHistoryProps';
export * from './GroupsModeratorsProps';