Skip to content

Commit 403f88f

Browse files
committed
Add analytics to web endpoints
1 parent ac63621 commit 403f88f

File tree

8 files changed

+152
-4
lines changed

8 files changed

+152
-4
lines changed

common/src/constants/analytics-events.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ export enum AnalyticsEvent {
8585

8686
// Web - API
8787
SSE_ENDPOINT_REQUEST = 'api.sse_endpoint_request',
88+
AGENT_RUN_API_REQUEST = 'api.agent_run_request',
89+
AGENT_RUN_CREATED = 'api.agent_run_created',
90+
AGENT_RUN_VALIDATION_ERROR = 'api.agent_run_validation_error',
91+
AGENT_RUN_CREATION_ERROR = 'api.agent_run_creation_error',
92+
ME_API_REQUEST = 'api.me_request',
93+
ME_VALIDATION_ERROR = 'api.me_validation_error',
8894

8995
// Common
9096
FLUSH_FAILED = 'common.flush_failed',
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { AnalyticsEvent } from '../../constants/analytics-events'
2+
import type { Logger } from './logger'
3+
4+
export type TrackEventFn = (params: {
5+
event: AnalyticsEvent
6+
userId: string
7+
properties?: Record<string, any>
8+
logger: Logger
9+
}) => void

web/src/api/v1/__tests__/agent-runs.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { NextRequest } from 'next/server'
1212

1313
import { agentRunsPost } from '../agent-runs'
1414

15+
import type { TrackEventFn } from '@codebuff/common/types/contracts/analytics'
1516
import type {
1617
GetUserInfoFromApiKeyFn,
1718
GetUserInfoFromApiKeyOutput,
@@ -51,6 +52,8 @@ describe('/api/v1/agent-runs POST endpoint', () => {
5152
debug: () => {},
5253
}
5354

55+
const mockTrackEvent: TrackEventFn = () => {}
56+
5457
let mockDbInsert: any
5558

5659
beforeEach(async () => {
@@ -84,6 +87,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
8487
req,
8588
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
8689
logger: mockLogger,
90+
trackEvent: mockTrackEvent,
8791
})
8892

8993
expect(response.status).toBe(401)
@@ -101,6 +105,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
101105
req,
102106
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
103107
logger: mockLogger,
108+
trackEvent: mockTrackEvent,
104109
})
105110

106111
expect(response.status).toBe(401)
@@ -120,6 +125,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
120125
req,
121126
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
122127
logger: mockLogger,
128+
trackEvent: mockTrackEvent,
123129
})
124130
expect(response.status).toBe(200)
125131
const body = await response.json()
@@ -138,6 +144,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
138144
req,
139145
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
140146
logger: mockLogger,
147+
trackEvent: mockTrackEvent,
141148
})
142149
expect(response.status).toBe(200)
143150
const body = await response.json()
@@ -155,6 +162,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
155162
req,
156163
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
157164
logger: mockLogger,
165+
trackEvent: mockTrackEvent,
158166
})
159167
expect(response.status).toBe(404)
160168
const body = await response.json()
@@ -174,6 +182,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
174182
req,
175183
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
176184
logger: mockLogger,
185+
trackEvent: mockTrackEvent,
177186
})
178187
expect(response.status).toBe(400)
179188
const body = await response.json()
@@ -191,6 +200,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
191200
req,
192201
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
193202
logger: mockLogger,
203+
trackEvent: mockTrackEvent,
194204
})
195205
expect(response.status).toBe(400)
196206
const body = await response.json()
@@ -209,6 +219,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
209219
req,
210220
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
211221
logger: mockLogger,
222+
trackEvent: mockTrackEvent,
212223
})
213224
expect(response.status).toBe(400)
214225
const body = await response.json()
@@ -227,6 +238,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
227238
req,
228239
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
229240
logger: mockLogger,
241+
trackEvent: mockTrackEvent,
230242
})
231243
expect(response.status).toBe(400)
232244
const body = await response.json()
@@ -249,6 +261,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
249261
req,
250262
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
251263
logger: mockLogger,
264+
trackEvent: mockTrackEvent,
252265
})
253266
expect(response.status).toBe(400)
254267
const body = await response.json()
@@ -269,6 +282,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
269282
req,
270283
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
271284
logger: mockLogger,
285+
trackEvent: mockTrackEvent,
272286
})
273287
expect(response.status).toBe(200)
274288
const body = await response.json()
@@ -293,6 +307,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
293307
req,
294308
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
295309
logger: mockLogger,
310+
trackEvent: mockTrackEvent,
296311
})
297312
expect(response.status).toBe(200)
298313
const body = await response.json()
@@ -314,6 +329,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
314329
req,
315330
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
316331
logger: mockLogger,
332+
trackEvent: mockTrackEvent,
317333
})
318334
expect(response.status).toBe(200)
319335
const body = await response.json()
@@ -335,6 +351,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
335351
req,
336352
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
337353
logger: mockLogger,
354+
trackEvent: mockTrackEvent,
338355
})
339356
expect(response.status).toBe(200)
340357
const body = await response.json()
@@ -353,6 +370,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
353370
req,
354371
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
355372
logger: mockLogger,
373+
trackEvent: mockTrackEvent,
356374
})
357375
expect(response.status).toBe(200)
358376
const body = await response.json()
@@ -384,6 +402,7 @@ describe('/api/v1/agent-runs POST endpoint', () => {
384402
req,
385403
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
386404
logger: mockLogger,
405+
trackEvent: mockTrackEvent,
387406
})
388407

389408
expect(response.status).toBe(500)

web/src/api/v1/__tests__/me.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import { NextRequest } from 'next/server'
44
import { VALID_USER_INFO_FIELDS } from '../../../db/user'
55
import { meGet } from '../me'
66

7+
import type { TrackEventFn } from '@codebuff/common/types/contracts/analytics'
78
import type {
89
GetUserInfoFromApiKeyFn,
910
GetUserInfoFromApiKeyOutput,
1011
} from '@codebuff/common/types/contracts/database'
12+
import type { Logger } from '@codebuff/common/types/contracts/logger'
1113

1214
describe('/api/v1/me route', () => {
1315
const mockUserData: Record<
@@ -43,12 +45,23 @@ describe('/api/v1/me route', () => {
4345
) as any
4446
}
4547

48+
const mockLogger: Logger = {
49+
error: () => {},
50+
warn: () => {},
51+
info: () => {},
52+
debug: () => {},
53+
}
54+
55+
const mockTrackEvent: TrackEventFn = () => {}
56+
4657
describe('Authentication', () => {
4758
test('returns 401 when Authorization header is missing', async () => {
4859
const req = new NextRequest('http://localhost:3000/api/v1/me')
4960
const response = await meGet({
5061
req,
5162
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
63+
logger: mockLogger,
64+
trackEvent: mockTrackEvent,
5265
})
5366

5467
expect(response.status).toBe(401)
@@ -63,6 +76,8 @@ describe('/api/v1/me route', () => {
6376
const response = await meGet({
6477
req,
6578
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
79+
logger: mockLogger,
80+
trackEvent: mockTrackEvent,
6681
})
6782

6883
expect(response.status).toBe(401)
@@ -79,6 +94,8 @@ describe('/api/v1/me route', () => {
7994
const response = await meGet({
8095
req,
8196
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
97+
logger: mockLogger,
98+
trackEvent: mockTrackEvent,
8299
})
83100
expect(response.status).toBe(200)
84101
const body = await response.json()
@@ -94,6 +111,8 @@ describe('/api/v1/me route', () => {
94111
const response = await meGet({
95112
req,
96113
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
114+
logger: mockLogger,
115+
trackEvent: mockTrackEvent,
97116
})
98117
expect(response.status).toBe(200)
99118
const body = await response.json()
@@ -108,6 +127,8 @@ describe('/api/v1/me route', () => {
108127
const response = await meGet({
109128
req,
110129
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
130+
logger: mockLogger,
131+
trackEvent: mockTrackEvent,
111132
})
112133
expect(response.status).toBe(404)
113134
const body = await response.json()
@@ -124,6 +145,8 @@ describe('/api/v1/me route', () => {
124145
const response = await meGet({
125146
req,
126147
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
148+
logger: mockLogger,
149+
trackEvent: mockTrackEvent,
127150
})
128151
expect(response.status).toBe(200)
129152
const body = await response.json()
@@ -141,6 +164,8 @@ describe('/api/v1/me route', () => {
141164
const response = await meGet({
142165
req,
143166
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
167+
logger: mockLogger,
168+
trackEvent: mockTrackEvent,
144169
})
145170
expect(response.status).toBe(200)
146171
const body = await response.json()
@@ -158,6 +183,8 @@ describe('/api/v1/me route', () => {
158183
const response = await meGet({
159184
req,
160185
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
186+
logger: mockLogger,
187+
trackEvent: mockTrackEvent,
161188
})
162189
expect(response.status).toBe(200)
163190
const body = await response.json()
@@ -179,6 +206,8 @@ describe('/api/v1/me route', () => {
179206
const response = await meGet({
180207
req,
181208
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
209+
logger: mockLogger,
210+
trackEvent: mockTrackEvent,
182211
})
183212
expect(response.status).toBe(200)
184213
const body = await response.json()
@@ -200,6 +229,8 @@ describe('/api/v1/me route', () => {
200229
const response = await meGet({
201230
req,
202231
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
232+
logger: mockLogger,
233+
trackEvent: mockTrackEvent,
203234
})
204235
expect(response.status).toBe(400)
205236
const body = await response.json()
@@ -220,6 +251,8 @@ describe('/api/v1/me route', () => {
220251
const response = await meGet({
221252
req,
222253
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
254+
logger: mockLogger,
255+
trackEvent: mockTrackEvent,
223256
})
224257
expect(response.status).toBe(400)
225258
const body = await response.json()
@@ -237,6 +270,8 @@ describe('/api/v1/me route', () => {
237270
const response = await meGet({
238271
req,
239272
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
273+
logger: mockLogger,
274+
trackEvent: mockTrackEvent,
240275
})
241276
expect(response.status).toBe(400)
242277
})
@@ -251,6 +286,8 @@ describe('/api/v1/me route', () => {
251286
const response = await meGet({
252287
req,
253288
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
289+
logger: mockLogger,
290+
trackEvent: mockTrackEvent,
254291
})
255292
expect(response.status).toBe(200)
256293
const body = await response.json()
@@ -268,6 +305,8 @@ describe('/api/v1/me route', () => {
268305
const response = await meGet({
269306
req,
270307
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
308+
logger: mockLogger,
309+
trackEvent: mockTrackEvent,
271310
})
272311
expect(response.status).toBe(200)
273312
const body = await response.json()
@@ -285,6 +324,8 @@ describe('/api/v1/me route', () => {
285324
const response = await meGet({
286325
req,
287326
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
327+
logger: mockLogger,
328+
trackEvent: mockTrackEvent,
288329
})
289330
expect(response.status).toBe(200)
290331
const body = await response.json()
@@ -306,6 +347,8 @@ describe('/api/v1/me route', () => {
306347
const response = await meGet({
307348
req,
308349
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
350+
logger: mockLogger,
351+
trackEvent: mockTrackEvent,
309352
})
310353
expect(response.status).toBe(200)
311354
const body = await response.json()
@@ -322,6 +365,8 @@ describe('/api/v1/me route', () => {
322365
const response = await meGet({
323366
req,
324367
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
368+
logger: mockLogger,
369+
trackEvent: mockTrackEvent,
325370
})
326371
expect(response.status).toBe(400)
327372
const body = await response.json()
@@ -339,6 +384,8 @@ describe('/api/v1/me route', () => {
339384
const response = await meGet({
340385
req,
341386
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
387+
logger: mockLogger,
388+
trackEvent: mockTrackEvent,
342389
})
343390
expect(response.status).toBe(400)
344391
})
@@ -354,6 +401,8 @@ describe('/api/v1/me route', () => {
354401
const response = await meGet({
355402
req,
356403
getUserInfoFromApiKey: mockGetUserInfoFromApiKey,
404+
logger: mockLogger,
405+
trackEvent: mockTrackEvent,
357406
})
358407
expect(response.status).toBe(400)
359408
const body = await response.json()

0 commit comments

Comments
 (0)