Skip to content

Commit abf99ca

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 abf99ca

File tree

2 files changed

+133
-38
lines changed

2 files changed

+133
-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: 128 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,152 @@
1+
import { ajv, validateUnauthorizedErrorResponse } from '@rocket.chat/rest-typings';
2+
13
import { getStatistics, getLastStatistics } from '../../../statistics/server';
24
import telemetryEvent from '../../../statistics/server/lib/telemetryEvents';
35
import { API } from '../api';
46
import { getPaginationItems } from '../helpers/getPaginationItems';
57

6-
API.v1.addRoute(
8+
API.v1.get(
79
'statistics',
8-
{ authRequired: true },
910
{
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-
);
11+
authRequired: true,
12+
query: ajv.compile<{ refresh?: 'true' | 'false' }>({
13+
type: 'object',
14+
properties: {
15+
refresh: {
16+
type: 'string',
17+
nullable: true,
18+
},
19+
},
20+
required: [],
21+
additionalProperties: false,
22+
}),
23+
response: {
24+
200: ajv.compile<Record<string, unknown>>({
25+
type: 'object',
26+
properties: {
27+
success: {
28+
type: 'boolean',
29+
enum: [true],
30+
},
31+
},
32+
required: ['success'],
33+
}),
34+
401: validateUnauthorizedErrorResponse,
1935
},
2036
},
37+
async function action() {
38+
const { refresh = 'false' } = this.queryParams;
39+
40+
return API.v1.success(
41+
await getLastStatistics({
42+
userId: this.userId,
43+
refresh: refresh === 'true',
44+
}),
45+
);
46+
},
2147
);
2248

23-
API.v1.addRoute(
49+
API.v1.get(
2450
'statistics.list',
25-
{ authRequired: true },
2651
{
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,
52+
authRequired: true,
53+
query: ajv.compile<{ fields?: string; count?: number; offset?: number; sort?: string; query?: string }>({
54+
type: 'object',
55+
properties: {
56+
fields: { type: 'string', nullable: true },
57+
count: { type: 'number', nullable: true },
58+
offset: { type: 'number', nullable: true },
59+
sort: { type: 'string', nullable: true },
60+
query: { type: 'string', nullable: true },
61+
},
62+
required: [],
63+
additionalProperties: false,
64+
}),
65+
response: {
66+
200: ajv.compile<{
67+
statistics: unknown[];
68+
count: number;
69+
offset: number;
70+
total: number;
71+
}>({
72+
type: 'object',
73+
properties: {
74+
statistics: { type: 'array' },
75+
count: { type: 'number' },
76+
offset: { type: 'number' },
77+
total: { type: 'number' },
78+
success: {
79+
type: 'boolean',
80+
enum: [true],
4081
},
41-
}),
42-
);
82+
},
83+
required: ['statistics', 'count', 'offset', 'total', 'success'],
84+
}),
85+
401: validateUnauthorizedErrorResponse,
4386
},
4487
},
88+
async function action() {
89+
const { offset, count } = await getPaginationItems(this.queryParams);
90+
const { sort, fields, query } = await this.parseJsonQuery();
91+
92+
return API.v1.success(
93+
await getStatistics({
94+
userId: this.userId,
95+
query,
96+
pagination: {
97+
offset,
98+
count,
99+
sort,
100+
fields,
101+
},
102+
}),
103+
);
104+
},
45105
);
46106

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

54-
events?.params?.forEach((event) => {
55-
const { eventName, ...params } = event;
56-
void telemetryEvent.call(eventName, params);
57-
});
145+
events.params.forEach((event) => {
146+
const { eventName, ...params } = event;
147+
void telemetryEvent.call(eventName, params);
148+
});
58149

59-
return API.v1.success();
60-
},
150+
return API.v1.success();
61151
},
62152
);

0 commit comments

Comments
 (0)