diff --git a/packages/core/src/queries/daily-active-user.ts b/packages/core/src/queries/daily-active-user.ts index 547f6aa91..00ebdcc19 100644 --- a/packages/core/src/queries/daily-active-user.ts +++ b/packages/core/src/queries/daily-active-user.ts @@ -1,14 +1,42 @@ import { DailyActiveUsers } from '@logto/schemas'; -import type { CommonQueryMethods } from 'slonik'; +import { convertToIdentifiers } from '@logto/shared'; +import { sql, type CommonQueryMethods } from 'slonik'; import { buildInsertIntoWithPool } from '#src/database/insert-into.js'; +const { table, fields } = convertToIdentifiers(DailyActiveUsers); + export const createDailyActiveUsersQueries = (pool: CommonQueryMethods) => { const insertActiveUser = buildInsertIntoWithPool(pool)(DailyActiveUsers, { onConflict: { ignore: true }, }); + const countActiveUsersByTimeInterval = async ( + startTimeExclusive: number, + endTimeInclusive: number + ) => + pool.one<{ count: number }>(sql` + select count(distinct(${fields.userId})) + from ${table} + where ${fields.date} > to_timestamp(${startTimeExclusive}::double precision / 1000) + and ${fields.date} <= to_timestamp(${endTimeInclusive}::double precision / 1000) + `); + + const getDailyActiveUserCountsByTimeInterval = async ( + startTimeExclusive: number, + endTimeInclusive: number + ) => + pool.any<{ date: string; count: number }>(sql` + select date(${fields.date}), count(distinct(${fields.userId})) + from ${table} + where ${fields.date} > to_timestamp(${startTimeExclusive}::double precision / 1000) + and ${fields.date} <= to_timestamp(${endTimeInclusive}::double precision / 1000) + group by date(${fields.date}) + `); + return { insertActiveUser, + countActiveUsersByTimeInterval, + getDailyActiveUserCountsByTimeInterval, }; }; diff --git a/packages/core/src/routes/dashboard.test.ts b/packages/core/src/routes/dashboard.test.ts index ae7ef9ae1..04995739b 100644 --- a/packages/core/src/routes/dashboard.test.ts +++ b/packages/core/src/routes/dashboard.test.ts @@ -41,13 +41,13 @@ const users = { }; const { countUsers, getDailyNewUserCountsByTimeInterval } = users; -const logs = { +const dailyActiveUsers = { getDailyActiveUserCountsByTimeInterval: jest.fn().mockResolvedValue(mockDailyActiveUserCounts), countActiveUsersByTimeInterval: jest.fn().mockResolvedValue({ count: mockActiveUserCount }), }; -const { getDailyActiveUserCountsByTimeInterval, countActiveUsersByTimeInterval } = logs; +const { getDailyActiveUserCountsByTimeInterval, countActiveUsersByTimeInterval } = dailyActiveUsers; -const tenantContext = new MockTenant(undefined, { logs, users }); +const tenantContext = new MockTenant(undefined, { dailyActiveUsers, users }); const dashboardRoutes = await pickDefault(import('./dashboard.js')); describe('dashboardRoutes', () => { diff --git a/packages/core/src/routes/dashboard.ts b/packages/core/src/routes/dashboard.ts index b5c29d5ff..f5f7b7deb 100644 --- a/packages/core/src/routes/dashboard.ts +++ b/packages/core/src/routes/dashboard.ts @@ -17,7 +17,7 @@ export default function dashboardRoutes( ...[router, { queries }]: RouterInitArgs ) { const { - logs: { countActiveUsersByTimeInterval, getDailyActiveUserCountsByTimeInterval }, + dailyActiveUsers: { countActiveUsersByTimeInterval, getDailyActiveUserCountsByTimeInterval }, users: { countUsers, getDailyNewUserCountsByTimeInterval }, } = queries;