diff --git a/.changeset/good-tigers-lie.md b/.changeset/good-tigers-lie.md new file mode 100644 index 0000000000000..2a9a13b858af4 --- /dev/null +++ b/.changeset/good-tigers-lie.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +Migrated chat.syncThreadMessages old Rest Api strucutre to new typed rest (openAPI) structure diff --git a/apps/meteor/app/api/server/v1/chat.ts b/apps/meteor/app/api/server/v1/chat.ts index a00d57e46ae72..ce4f72d67407c 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -21,12 +21,12 @@ import { isChatGetDeletedMessagesProps, isChatSyncThreadsListProps, isChatGetThreadMessagesProps, - isChatSyncThreadMessagesProps, isChatGetStarredMessagesProps, isChatGetDiscussionsProps, validateBadRequestErrorResponse, validateUnauthorizedErrorResponse, } from '@rocket.chat/rest-typings'; +import type { PaginatedRequest } from '@rocket.chat/rest-typings'; import { escapeRegExp } from '@rocket.chat/string-helpers'; import { Meteor } from 'meteor/meteor'; @@ -275,6 +275,85 @@ const isChatPinMessageProps = ajv.compile(ChatPinMessageSchema); const isChatUnpinMessageProps = ajv.compile(ChatUnpinMessageSchema); +type ChatSyncThreadMessages = PaginatedRequest<{ + tmid: string; + updatedSince: string; +}>; + +const ChatSyncThreadMessagesSchema = { + type: 'object', + properties: { + tmid: { + type: 'string', + minLength: 1, + }, + updatedSince: { + type: 'string', + format: 'iso-date-time', + }, + count: { + type: 'number', + nullable: true, + }, + offset: { + type: 'number', + nullable: true, + }, + sort: { + type: 'string', + nullable: true, + }, + query: { + type: 'string', + nullable: true, + }, + fields: { + type: 'string', + nullable: true, + }, + }, + required: ['tmid', 'updatedSince'], + additionalProperties: false, +}; + +const isChatSyncThreadMessagesLocalProps = ajv.compile(ChatSyncThreadMessagesSchema); + +const isSyncThreadMessagesResponse = ajv.compile<{ + messages: { + update: IMessage[]; + remove: IMessage[]; + }; +}>({ + type: 'object', + properties: { + messages: { + type: 'object', + properties: { + update: { + type: 'array', + items: { + $ref: '#/components/schemas/IMessage', + }, + }, + remove: { + type: 'array', + items: { + $ref: '#/components/schemas/IMessage', + }, + }, + }, + required: ['update', 'remove'], + additionalProperties: false, + }, + success: { + type: 'boolean', + enum: [true], + }, + }, + required: ['messages', 'success'], + additionalProperties: false, +}); + const chatEndpoints = API.v1 .post( 'chat.pinMessage', @@ -558,6 +637,51 @@ const chatEndpoints = API.v1 return API.v1.success(); }, + ) + .get( + 'chat.syncThreadMessages', + { + authRequired: true, + query: isChatSyncThreadMessagesLocalProps, + response: { + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 200: isSyncThreadMessagesResponse, + }, + }, + async function action() { + const { tmid } = this.queryParams; + const { query, fields, sort } = await this.parseJsonQuery(); + const { updatedSince } = this.queryParams; + let updatedSinceDate; + if (!settings.get('Threads_enabled')) { + throw new Meteor.Error('error-not-allowed', 'Threads Disabled'); + } + + if (isNaN(Date.parse(updatedSince))) { + throw new Meteor.Error('error-updatedSince-param-invalid', 'The "updatedSince" query parameter must be a valid date.'); + } else { + updatedSinceDate = new Date(updatedSince); + } + const thread = await Messages.findOneById(tmid, { projection: { rid: 1 } }); + if (!thread?.rid) { + throw new Meteor.Error('error-invalid-message', 'Invalid Message'); + } + const [user, room] = await Promise.all([ + Users.findOneById(this.userId, { projection: { _id: 1 } }), + Rooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }), + ]); + + if (!room || !user || !(await canAccessRoomAsync(room, user))) { + throw new Meteor.Error('error-not-allowed', 'Not Allowed'); + } + return API.v1.success({ + messages: { + update: await Messages.find({ ...query, tmid, _updatedAt: { $gt: updatedSinceDate } }, { projection: fields, sort }).toArray(), + remove: await Messages.trashFindDeletedAfter(updatedSinceDate, { ...query, tmid }, { projection: fields, sort }).toArray(), + }, + }); + }, ); API.v1.addRoute( @@ -918,45 +1042,6 @@ API.v1.addRoute( }, ); -API.v1.addRoute( - 'chat.syncThreadMessages', - { authRequired: true, validateParams: isChatSyncThreadMessagesProps }, - { - async get() { - const { tmid } = this.queryParams; - const { query, fields, sort } = await this.parseJsonQuery(); - const { updatedSince } = this.queryParams; - let updatedSinceDate; - if (!settings.get('Threads_enabled')) { - throw new Meteor.Error('error-not-allowed', 'Threads Disabled'); - } - - if (isNaN(Date.parse(updatedSince))) { - throw new Meteor.Error('error-updatedSince-param-invalid', 'The "updatedSince" query parameter must be a valid date.'); - } else { - updatedSinceDate = new Date(updatedSince); - } - const thread = await Messages.findOneById(tmid, { projection: { rid: 1 } }); - if (!thread?.rid) { - throw new Meteor.Error('error-invalid-message', 'Invalid Message'); - } - // TODO: promise.all? this.user? - const user = await Users.findOneById(this.userId, { projection: { _id: 1 } }); - const room = await Rooms.findOneById(thread.rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } }); - - if (!room || !user || !(await canAccessRoomAsync(room, user))) { - throw new Meteor.Error('error-not-allowed', 'Not Allowed'); - } - return API.v1.success({ - messages: { - update: await Messages.find({ ...query, tmid, _updatedAt: { $gt: updatedSinceDate } }, { projection: fields, sort }).toArray(), - remove: await Messages.trashFindDeletedAfter(updatedSinceDate, { ...query, tmid }, { projection: fields, sort }).toArray(), - }, - }); - }, - }, -); - API.v1.addRoute( 'chat.getMentionedMessages', { authRequired: true, validateParams: isChatGetMentionedMessagesProps }, diff --git a/apps/meteor/tests/end-to-end/api/chat.ts b/apps/meteor/tests/end-to-end/api/chat.ts index 7fd725ed5bfbc..2c3c949ec5aaa 100644 --- a/apps/meteor/tests/end-to-end/api/chat.ts +++ b/apps/meteor/tests/end-to-end/api/chat.ts @@ -4568,7 +4568,7 @@ describe('Threads', () => { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('errorType', 'invalid-params'); + expect(res.body).to.have.property('errorType', 'error-invalid-params'); }) .end(done); }); @@ -4586,7 +4586,7 @@ describe('Threads', () => { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('errorType', 'invalid-params'); + expect(res.body).to.have.property('errorType', 'error-invalid-params'); }) .end(done); }); @@ -4605,7 +4605,7 @@ describe('Threads', () => { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body).to.have.property('errorType', 'invalid-params'); + expect(res.body).to.have.property('errorType', 'error-invalid-params'); }) .end(done); }); diff --git a/packages/rest-typings/src/v1/chat.ts b/packages/rest-typings/src/v1/chat.ts index f3a6e61f677b0..96fff08072acb 100644 --- a/packages/rest-typings/src/v1/chat.ts +++ b/packages/rest-typings/src/v1/chat.ts @@ -638,41 +638,6 @@ const ChatSyncMessagesSchema = { export const isChatSyncMessagesProps = ajvQuery.compile(ChatSyncMessagesSchema); -type ChatSyncThreadMessages = PaginatedRequest<{ - tmid: string; - updatedSince: string; -}>; - -const ChatSyncThreadMessagesSchema = { - type: 'object', - properties: { - tmid: { - type: 'string', - minLength: 1, - }, - updatedSince: { - type: 'string', - format: 'iso-date-time', - }, - count: { - type: 'number', - nullable: true, - }, - offset: { - type: 'number', - nullable: true, - }, - sort: { - type: 'string', - nullable: true, - }, - }, - required: ['tmid', 'updatedSince'], - additionalProperties: false, -}; - -export const isChatSyncThreadMessagesProps = ajvQuery.compile(ChatSyncThreadMessagesSchema); - type ChatGetThreadMessages = PaginatedRequest<{ tmid: string; }>; @@ -989,14 +954,6 @@ export type ChatEndpoints = { message: IMessage; }; }; - '/v1/chat.syncThreadMessages': { - GET: (params: ChatSyncThreadMessages) => { - messages: { - update: IMessage[]; - remove: IMessage[]; - }; - }; - }; '/v1/chat.getThreadMessages': { GET: (params: ChatGetThreadMessages) => { messages: IMessage[];