Skip to content

Commit 40b9909

Browse files
committed
refactor: migrate statistics endpoints to new chained API pattern
Migrates statistics (GET), statistics.list (GET), and statistics.telemetry (POST) from the legacy addRoute() pattern to the new chained .get()/.post() API pattern with typed AJV response schemas and query parameter validation using existing isStatisticsProps and isStatisticsListProps validators. Part of #38876
1 parent 3145c41 commit 40b9909

File tree

2 files changed

+138
-38
lines changed

2 files changed

+138
-38
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rocket.chat/meteor': patch
3+
---
4+
5+
Migrated `statistics`, `statistics.list`, and `statistics.telemetry` REST API endpoints from legacy `addRoute` pattern to the new chained `.get()`/`.post()` API pattern with typed response schemas and AJV query parameter validation.
Lines changed: 133 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,157 @@
1+
import type { TelemetryEvents } from '@rocket.chat/core-services';
2+
import { ajv, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
3+
14
import { getStatistics, getLastStatistics } from '../../../statistics/server';
25
import telemetryEvent from '../../../statistics/server/lib/telemetryEvents';
36
import { API } from '../api';
47
import { getPaginationItems } from '../helpers/getPaginationItems';
58

6-
API.v1.addRoute(
9+
API.v1.get(
710
'statistics',
8-
{ authRequired: true },
911
{
10-
async get() {
11-
const { refresh = 'false' } = this.queryParams;
12-
13-
return API.v1.success(
14-
await getLastStatistics({
15-
userId: this.userId,
16-
refresh: refresh === 'true',
17-
}),
18-
);
12+
authRequired: true,
13+
query: ajv.compile<{ refresh?: 'true' | 'false' }>({
14+
type: 'object',
15+
properties: {
16+
refresh: {
17+
type: 'string',
18+
nullable: true,
19+
},
20+
},
21+
required: [],
22+
additionalProperties: false,
23+
}),
24+
response: {
25+
200: ajv.compile<Record<string, unknown>>({
26+
type: 'object',
27+
properties: {
28+
success: {
29+
type: 'boolean',
30+
enum: [true],
31+
},
32+
},
33+
required: ['success'],
34+
}),
35+
401: validateUnauthorizedErrorResponse,
1936
},
2037
},
38+
async function action() {
39+
const { refresh = 'false' } = this.queryParams;
40+
41+
const stats = await getLastStatistics({
42+
userId: this.userId,
43+
refresh: refresh === 'true',
44+
});
45+
46+
if (!stats) {
47+
throw new Error('No statistics found');
48+
}
49+
50+
return API.v1.success(stats);
51+
},
2152
);
2253

23-
API.v1.addRoute(
54+
API.v1.get(
2455
'statistics.list',
25-
{ authRequired: true },
2656
{
27-
async get() {
28-
const { offset, count } = await getPaginationItems(this.queryParams);
29-
const { sort, fields, query } = await this.parseJsonQuery();
30-
31-
return API.v1.success(
32-
await getStatistics({
33-
userId: this.userId,
34-
query,
35-
pagination: {
36-
offset,
37-
count,
38-
sort,
39-
fields,
57+
authRequired: true,
58+
query: ajv.compile<{ fields?: string; count?: number; offset?: number; sort?: string; query?: string }>({
59+
type: 'object',
60+
properties: {
61+
fields: { type: 'string', nullable: true },
62+
count: { type: 'number', nullable: true },
63+
offset: { type: 'number', nullable: true },
64+
sort: { type: 'string', nullable: true },
65+
query: { type: 'string', nullable: true },
66+
},
67+
required: [],
68+
additionalProperties: false,
69+
}),
70+
response: {
71+
200: ajv.compile<{
72+
statistics: unknown[];
73+
count: number;
74+
offset: number;
75+
total: number;
76+
}>({
77+
type: 'object',
78+
properties: {
79+
statistics: { type: 'array' },
80+
count: { type: 'number' },
81+
offset: { type: 'number' },
82+
total: { type: 'number' },
83+
success: {
84+
type: 'boolean',
85+
enum: [true],
4086
},
41-
}),
42-
);
87+
},
88+
required: ['statistics', 'count', 'offset', 'total', 'success'],
89+
}),
90+
401: validateUnauthorizedErrorResponse,
4391
},
4492
},
93+
async function action() {
94+
const { offset, count } = await getPaginationItems(this.queryParams);
95+
const { sort, fields, query } = await this.parseJsonQuery();
96+
97+
return API.v1.success(
98+
await getStatistics({
99+
userId: this.userId,
100+
query,
101+
pagination: {
102+
offset,
103+
count,
104+
sort,
105+
fields,
106+
},
107+
}),
108+
);
109+
},
45110
);
46111

47-
API.v1.addRoute(
112+
API.v1.post(
48113
'statistics.telemetry',
49-
{ authRequired: true },
50114
{
51-
post() {
52-
const events = this.bodyParams;
115+
authRequired: true,
116+
body: ajv.compile<{ params: { eventName: string; [key: string]: unknown }[] }>({
117+
type: 'object',
118+
properties: {
119+
params: {
120+
type: 'array',
121+
items: {
122+
type: 'object',
123+
properties: {
124+
eventName: { type: 'string' },
125+
},
126+
required: ['eventName'],
127+
},
128+
},
129+
},
130+
required: ['params'],
131+
}),
132+
response: {
133+
200: ajv.compile<void>({
134+
type: 'object',
135+
properties: {
136+
success: {
137+
type: 'boolean',
138+
enum: [true],
139+
},
140+
},
141+
required: ['success'],
142+
additionalProperties: false,
143+
}),
144+
401: validateUnauthorizedErrorResponse,
145+
},
146+
},
147+
function action() {
148+
const events = this.bodyParams;
53149

54-
events?.params?.forEach((event) => {
55-
const { eventName, ...params } = event;
56-
void telemetryEvent.call(eventName, params);
57-
});
150+
events.params.forEach((event) => {
151+
const { eventName, ...params } = event;
152+
void telemetryEvent.call(eventName as TelemetryEvents, params);
153+
});
58154

59-
return API.v1.success();
60-
},
155+
return API.v1.success();
61156
},
62157
);

0 commit comments

Comments
 (0)