diff --git a/apps/meteor/app/api/server/v1/stats.ts b/apps/meteor/app/api/server/v1/stats.ts index 27cea2c310574..ec47993d0eff6 100644 --- a/apps/meteor/app/api/server/v1/stats.ts +++ b/apps/meteor/app/api/server/v1/stats.ts @@ -1,13 +1,64 @@ +import type { IStats } from '@rocket.chat/core-typings'; +import type { PaginatedResult } from '@rocket.chat/rest-typings'; +import { + ajv, + isStatisticsProps, + isStatisticsListProps, + validateUnauthorizedErrorResponse, + validateBadRequestErrorResponse, +} from '@rocket.chat/rest-typings'; + import { getStatistics, getLastStatistics } from '../../../statistics/server'; import telemetryEvent from '../../../statistics/server/lib/telemetryEvents'; +import type { ExtractRoutesFromAPI } from '../ApiClass'; import { API } from '../api'; import { getPaginationItems } from '../helpers/getPaginationItems'; -API.v1.addRoute( - 'statistics', - { authRequired: true }, - { - async get() { +type TelemetryBody = { + params?: { eventName: string; [key: string]: unknown }[]; +}; + +const TelemetryBodySchema = { + type: 'object', + properties: { + params: { + type: 'array', + items: { + type: 'object', + properties: { + eventName: { type: 'string' }, + }, + required: ['eventName'], + additionalProperties: true, + }, + nullable: true, + }, + }, + additionalProperties: true, +}; + +const isTelemetryBody = ajv.compile(TelemetryBodySchema); + +const statsEndpoints = API.v1 + .get( + 'statistics', + { + authRequired: true, + query: isStatisticsProps, + response: { + 200: ajv.compile({ + type: 'object', + additionalProperties: true, + properties: { + success: { type: 'boolean', enum: [true] }, + }, + required: ['success'], + }), + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + }, + }, + async function action() { const { refresh = 'false' } = this.queryParams; return API.v1.success( @@ -17,14 +68,30 @@ API.v1.addRoute( }), ); }, - }, -); - -API.v1.addRoute( - 'statistics.list', - { authRequired: true }, - { - async get() { + ) + .get( + 'statistics.list', + { + authRequired: true, + query: isStatisticsListProps, + response: { + 200: ajv.compile>({ + type: 'object', + properties: { + statistics: { type: 'array', items: { type: 'object', additionalProperties: true } }, + count: { type: 'number' }, + offset: { type: 'number' }, + total: { type: 'number' }, + success: { type: 'boolean', enum: [true] }, + }, + required: ['statistics', 'count', 'offset', 'total', 'success'], + additionalProperties: false, + }), + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + }, + }, + async function action() { const { offset, count } = await getPaginationItems(this.queryParams); const { sort, fields, query } = await this.parseJsonQuery(); @@ -41,22 +108,40 @@ API.v1.addRoute( }), ); }, - }, -); - -API.v1.addRoute( - 'statistics.telemetry', - { authRequired: true }, - { - post() { + ) + .post( + 'statistics.telemetry', + { + authRequired: true, + body: isTelemetryBody, + response: { + 200: ajv.compile({ + type: 'object', + properties: { + success: { type: 'boolean', enum: [true] }, + }, + required: ['success'], + additionalProperties: false, + }), + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + }, + }, + function action() { const events = this.bodyParams; events?.params?.forEach((event) => { const { eventName, ...params } = event; - void telemetryEvent.call(eventName, params); + void telemetryEvent.call(eventName as import('@rocket.chat/core-services').TelemetryEvents, params as any); }); return API.v1.success(); }, - }, -); + ); + +export type StatsEndpoints = ExtractRoutesFromAPI; + +declare module '@rocket.chat/rest-typings' { + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface + interface Endpoints extends StatsEndpoints {} +} diff --git a/packages/rest-typings/src/index.ts b/packages/rest-typings/src/index.ts index 76a36bffc4b45..518eff0a16109 100644 --- a/packages/rest-typings/src/index.ts +++ b/packages/rest-typings/src/index.ts @@ -257,5 +257,7 @@ export * from './v1/cloud'; export * from './v1/banners'; export * from './default'; +export * from './v1/statistics'; + // Export the ajv instance for use in other packages export * from './v1/Ajv';