diff --git a/.changeset/migrate-livechat-analytics.md b/.changeset/migrate-livechat-analytics.md new file mode 100644 index 0000000000000..ae5ddcbf1592d --- /dev/null +++ b/.changeset/migrate-livechat-analytics.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Migrated `livechat/analytics/agent-overview` and `livechat/analytics/overview` GET endpoints from `API.v1.addRoute` to chained `.get()` pattern with AJV response schema validation. diff --git a/apps/meteor/app/livechat/server/api/v1/statistics.ts b/apps/meteor/app/livechat/server/api/v1/statistics.ts index 8289e01e21bb0..40a71fc4f3da7 100644 --- a/apps/meteor/app/livechat/server/api/v1/statistics.ts +++ b/apps/meteor/app/livechat/server/api/v1/statistics.ts @@ -1,19 +1,65 @@ import { Users } from '@rocket.chat/models'; -import { isLivechatAnalyticsAgentOverviewProps, isLivechatAnalyticsOverviewProps } from '@rocket.chat/rest-typings'; +import { + ajv, + validateUnauthorizedErrorResponse, + validateForbiddenErrorResponse, + isLivechatAnalyticsAgentOverviewProps, + isLivechatAnalyticsOverviewProps, +} from '@rocket.chat/rest-typings'; import { API } from '../../../../api/server'; +import type { ExtractRoutesFromAPI } from '../../../../api/server/ApiClass'; import { settings } from '../../../../settings/server'; import { getAgentOverviewDataCached, getAnalyticsOverviewDataCached } from '../../lib/AnalyticsTyped'; -API.v1.addRoute( - 'livechat/analytics/agent-overview', - { - authRequired: true, - permissionsRequired: ['view-livechat-manager'], - validateParams: isLivechatAnalyticsAgentOverviewProps, - }, - { - async get() { +const livechatAnalyticsEndpoints = API.v1 + .get( + 'livechat/analytics/agent-overview', + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + query: isLivechatAnalyticsAgentOverviewProps, + response: { + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, + 200: ajv.compile<{ + head: { name: string }[]; + data: { name: string; value: number }[]; + success: boolean; + }>({ + type: 'object', + properties: { + head: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + }, + required: ['name'], + additionalProperties: false, + }, + }, + data: { + type: 'array', + items: { + type: 'object', + properties: { + name: { type: 'string' }, + value: { type: 'number' }, + }, + required: ['name', 'value'], + additionalProperties: false, + }, + }, + success: { type: 'boolean', enum: [true] }, + }, + required: ['head', 'data', 'success'], + additionalProperties: false, + }), + }, + }, + async function action() { const { name, departmentId, from, to } = this.queryParams; if (!name) { @@ -31,18 +77,36 @@ API.v1.addRoute( }), ); }, - }, -); - -API.v1.addRoute( - 'livechat/analytics/overview', - { - authRequired: true, - permissionsRequired: ['view-livechat-manager'], - validateParams: isLivechatAnalyticsOverviewProps, - }, - { - async get() { + ) + .get( + 'livechat/analytics/overview', + { + authRequired: true, + permissionsRequired: ['view-livechat-manager'], + query: isLivechatAnalyticsOverviewProps, + response: { + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, + 200: ajv.compile< + { + title: string; + value: string | number; + }[] + >({ + type: 'array', + items: { + type: 'object', + properties: { + title: { type: 'string' }, + value: { anyOf: [{ type: 'string' }, { type: 'number' }] }, + }, + required: ['title', 'value'], + additionalProperties: false, + }, + }), + }, + }, + async function action() { const { name, departmentId, from, to } = this.queryParams; if (!name) { @@ -63,5 +127,11 @@ API.v1.addRoute( }), ); }, - }, -); + ); + +type LivechatAnalyticsEndpoints = ExtractRoutesFromAPI; + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface, @typescript-eslint/no-empty-object-type + interface Endpoints extends LivechatAnalyticsEndpoints {} +} diff --git a/packages/rest-typings/src/v1/omnichannel.ts b/packages/rest-typings/src/v1/omnichannel.ts index fe5988d3583c1..d40748d144ddf 100644 --- a/packages/rest-typings/src/v1/omnichannel.ts +++ b/packages/rest-typings/src/v1/omnichannel.ts @@ -2847,14 +2847,14 @@ type POSTLivechatRoomCloseByUserParams = { generateTranscriptPdf?: boolean; forceClose?: boolean; transcriptEmail?: - | { - // Note: if sendToVisitor is false, then any previously requested transcripts (like via livechat:requestTranscript) will be also cancelled - sendToVisitor: false; - } - | { - sendToVisitor: true; - requestData: Pick, 'email' | 'subject'>; - }; + | { + // Note: if sendToVisitor is false, then any previously requested transcripts (like via livechat:requestTranscript) will be also cancelled + sendToVisitor: false; + } + | { + sendToVisitor: true; + requestData: Pick, 'email' | 'subject'>; + }; }; const POSTLivechatRoomCloseByUserParamsSchema = { @@ -4359,8 +4359,8 @@ export const isLivechatTriggerWebhookCallParams = ajv.compile { filters: IOmnichannelRoom['source'][] }; }; - '/v1/livechat/analytics/agent-overview': { - GET: (params: LivechatAnalyticsAgentOverviewProps) => { - head: { name: string }[]; - data: { name: string; value: number }[]; - }; - }; - '/v1/livechat/analytics/overview': { - GET: (params: LivechatAnalyticsOverviewProps) => { - title: string; - value: string | number; - }[]; - }; '/v1/livechat/sms-incoming/:service': { POST: (params: unknown) => SMSProviderResponse; };