diff --git a/.changeset/five-chefs-destroy.md b/.changeset/five-chefs-destroy.md new file mode 100644 index 0000000000000..adf609961cf93 --- /dev/null +++ b/.changeset/five-chefs-destroy.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': minor +'@rocket.chat/meteor': minor +--- + +Migrated chat.syncMessages exisiting REST APIs to the 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..93e4153d4a068 100644 --- a/apps/meteor/app/api/server/v1/chat.ts +++ b/apps/meteor/app/api/server/v1/chat.ts @@ -1,5 +1,5 @@ import { Message } from '@rocket.chat/core-services'; -import type { IMessage, IThreadMainMessage } from '@rocket.chat/core-typings'; +import type { IMessage, IThreadMainMessage, IRoom } from '@rocket.chat/core-typings'; import { MessageTypes } from '@rocket.chat/message-types'; import { Messages, Users, Rooms, Subscriptions } from '@rocket.chat/models'; import { @@ -9,7 +9,6 @@ import { isChatUpdateProps, isChatGetThreadsListProps, isChatDeleteProps, - isChatSyncMessagesProps, isChatGetMessageProps, isChatPostMessageProps, isChatSearchProps, @@ -168,50 +167,6 @@ API.v1.addRoute( }, ); -API.v1.addRoute( - 'chat.syncMessages', - { authRequired: true, validateParams: isChatSyncMessagesProps }, - { - async get() { - const { roomId, lastUpdate, count, next, previous, type } = this.queryParams; - - if (!roomId) { - throw new Meteor.Error('error-param-required', 'The required "roomId" query param is missing'); - } - - if (!lastUpdate && !type) { - throw new Meteor.Error('error-param-required', 'The "type" or "lastUpdate" parameters must be provided'); - } - - if (lastUpdate && isNaN(Date.parse(lastUpdate))) { - throw new Meteor.Error('error-lastUpdate-param-invalid', 'The "lastUpdate" query parameter must be a valid date'); - } - - const getMessagesQuery = { - ...(lastUpdate && { lastUpdate: new Date(lastUpdate) }), - ...(next && { next }), - ...(previous && { previous }), - ...(count && { count }), - ...(type && { type }), - }; - - const result = await getMessageHistory(roomId, this.userId, getMessagesQuery); - - if (!result) { - return API.v1.failure(); - } - - return API.v1.success({ - result: { - updated: 'updated' in result ? await normalizeMessagesForUser(result.updated, this.userId) : [], - deleted: 'deleted' in result ? result.deleted : [], - cursor: 'cursor' in result ? result.cursor : undefined, - }, - }); - }, - }, -); - API.v1.addRoute( 'chat.getMessage', { @@ -275,6 +230,99 @@ const isChatPinMessageProps = ajv.compile(ChatPinMessageSchema); const isChatUnpinMessageProps = ajv.compile(ChatUnpinMessageSchema); +type ChatSyncMessages = { + roomId: IRoom['_id']; + lastUpdate?: string; + count?: number; + next?: string; + previous?: string; + type?: 'UPDATED' | 'DELETED'; +}; + +const ChatSyncMessagesSchema = { + type: 'object', + properties: { + roomId: { + type: 'string', + }, + lastUpdate: { + type: 'string', + nullable: true, + }, + count: { + type: 'number', + nullable: true, + }, + next: { + type: 'string', + nullable: true, + }, + previous: { + type: 'string', + nullable: true, + }, + type: { + type: 'string', + enum: ['UPDATED', 'DELETED'], + nullable: true, + }, + }, + required: ['roomId'], + additionalProperties: false, +}; + +const isChatSyncMessagesLocalProps = ajv.compile(ChatSyncMessagesSchema); + +const isSyncMessagesResponse = ajv.compile<{ + result: { + updated: IMessage[]; + deleted: IMessage[]; + cursor?: { + next: string | null; + previous: string | null; + } | null; + }; +}>({ + type: 'object', + properties: { + result: { + type: 'object', + properties: { + updated: { + type: 'array', + items: { $ref: '#/components/schemas/IMessage' }, + }, + deleted: { + type: 'array', + items: { $ref: '#/components/schemas/IMessage' }, + }, + cursor: { + oneOf: [ + { + type: 'object', + properties: { + next: { type: 'string', nullable: true }, + previous: { type: 'string', nullable: true }, + }, + required: ['next', 'previous'], + additionalProperties: false, + }, + { type: 'null' }, + ], + }, + }, + required: ['updated', 'deleted'], + additionalProperties: false, + }, + success: { + type: 'boolean', + enum: [true], + }, + }, + required: ['result', 'success'], + additionalProperties: false, +});; + const chatEndpoints = API.v1 .post( 'chat.pinMessage', @@ -558,6 +606,55 @@ const chatEndpoints = API.v1 return API.v1.success(); }, + ) + .get( + 'chat.syncMessages', + { + authRequired: true, + query: isChatSyncMessagesLocalProps, + response: { + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 200: isSyncMessagesResponse, + }, + }, + async function action() { + const { roomId, lastUpdate, count, next, previous, type } = this.queryParams; + + if (!roomId) { + throw new Meteor.Error('error-param-required', 'The required "roomId" query param is missing'); + } + + if (!lastUpdate && !type) { + throw new Meteor.Error('error-param-required', 'The "type" or "lastUpdate" parameters must be provided'); + } + + if (lastUpdate && isNaN(Date.parse(lastUpdate))) { + throw new Meteor.Error('error-lastUpdate-param-invalid', 'The "lastUpdate" query parameter must be a valid date'); + } + + const getMessagesQuery = { + ...(lastUpdate && { lastUpdate: new Date(lastUpdate) }), + ...(next && { next }), + ...(previous && { previous }), + ...(count && { count }), + ...(type && { type }), + }; + + const result = await getMessageHistory(roomId, this.userId, getMessagesQuery); + + if (!result) { + return API.v1.failure(); + } + + return API.v1.success({ + result: { + updated: 'updated' in result ? await normalizeMessagesForUser(result.updated, this.userId) : [], + deleted: 'deleted' in result ? result.deleted : [], + cursor: 'cursor' in result ? result.cursor : undefined, + }, + }); + }, ); API.v1.addRoute( diff --git a/apps/meteor/tests/end-to-end/api/chat.ts b/apps/meteor/tests/end-to-end/api/chat.ts index 7fd725ed5bfbc..ace2ba52ec37f 100644 --- a/apps/meteor/tests/end-to-end/api/chat.ts +++ b/apps/meteor/tests/end-to-end/api/chat.ts @@ -3676,7 +3676,7 @@ describe('[Chat]', () => { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('invalid-params'); + expect(res.body.errorType).to.be.equal('error-invalid-params'); expect(res.body.error).to.include(`must have required property 'roomId'`); }) .end(done); @@ -3736,7 +3736,7 @@ describe('[Chat]', () => { .expect(400) .expect((res) => { expect(res.body).to.have.property('success', false); - expect(res.body.errorType).to.be.equal('invalid-params'); + expect(res.body.errorType).to.be.equal('error-invalid-params'); expect(res.body.error).to.include('must be equal to one of the allowed values'); }) .end(done); diff --git a/packages/rest-typings/src/v1/chat.ts b/packages/rest-typings/src/v1/chat.ts index f3a6e61f677b0..d94c0fc200428 100644 --- a/packages/rest-typings/src/v1/chat.ts +++ b/packages/rest-typings/src/v1/chat.ts @@ -595,49 +595,6 @@ const GetMentionedMessagesSchema = { export const isChatGetMentionedMessagesProps = ajvQuery.compile(GetMentionedMessagesSchema); -type ChatSyncMessages = { - roomId: IRoom['_id']; - lastUpdate?: string; - count?: number; - next?: string; - previous?: string; - type?: 'UPDATED' | 'DELETED'; -}; - -const ChatSyncMessagesSchema = { - type: 'object', - properties: { - roomId: { - type: 'string', - }, - lastUpdate: { - type: 'string', - nullable: true, - }, - count: { - type: 'number', - nullable: true, - }, - next: { - type: 'string', - nullable: true, - }, - previous: { - type: 'string', - nullable: true, - }, - type: { - type: 'string', - enum: ['UPDATED', 'DELETED'], - nullable: true, - }, - }, - required: ['roomId'], - additionalProperties: false, -}; - -export const isChatSyncMessagesProps = ajvQuery.compile(ChatSyncMessagesSchema); - type ChatSyncThreadMessages = PaginatedRequest<{ tmid: string; updatedSince: string; @@ -970,18 +927,6 @@ export type ChatEndpoints = { total: number; }; }; - '/v1/chat.syncMessages': { - GET: (params: ChatSyncMessages) => { - result: { - updated: IMessage[]; - deleted: IMessage[]; - cursor: { - next: string | null; - previous: string | null; - }; - }; - }; - }; '/v1/chat.postMessage': { POST: (params: ChatPostMessage) => { ts: number;