mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
test(core): add api response guard and error case tests to dashboard api (#3789)
* test(core): add dashboard api response guard add dashboard api response guard * test(core): add dashboard integration tests add dashboard integration tests
This commit is contained in:
parent
f4f8224ff9
commit
5c0ed8e79e
4 changed files with 94 additions and 40 deletions
|
@ -1,6 +1,7 @@
|
|||
import { dateRegex } from '@logto/core-kit';
|
||||
import { getNewUsersResponseGuard, getActiveUsersResponseGuard } from '@logto/schemas';
|
||||
import { endOfDay, format, subDays } from 'date-fns';
|
||||
import { object, string } from 'zod';
|
||||
import { number, object, string } from 'zod';
|
||||
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
|
||||
|
@ -20,56 +21,74 @@ export default function dashboardRoutes<T extends AuthedRouter>(
|
|||
users: { countUsers, getDailyNewUserCountsByTimeInterval },
|
||||
} = queries;
|
||||
|
||||
router.get('/dashboard/users/total', async (ctx, next) => {
|
||||
const { count: totalUserCount } = await countUsers();
|
||||
ctx.body = { totalUserCount };
|
||||
router.get(
|
||||
'/dashboard/users/total',
|
||||
koaGuard({
|
||||
response: object({
|
||||
totalUserCount: number(),
|
||||
}),
|
||||
status: [200, 401, 403],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { count: totalUserCount } = await countUsers();
|
||||
ctx.body = { totalUserCount };
|
||||
|
||||
return next();
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get('/dashboard/users/new', async (ctx, next) => {
|
||||
const today = Date.now();
|
||||
const dailyNewUserCounts = await getDailyNewUserCountsByTimeInterval(
|
||||
// (14 days ago 23:59:59.999, today 23:59:59.999]
|
||||
getEndOfDayTimestamp(subDays(today, 14)),
|
||||
getEndOfDayTimestamp(today)
|
||||
);
|
||||
router.get(
|
||||
'/dashboard/users/new',
|
||||
koaGuard({
|
||||
response: getNewUsersResponseGuard,
|
||||
status: [200, 401, 403],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const today = Date.now();
|
||||
const dailyNewUserCounts = await getDailyNewUserCountsByTimeInterval(
|
||||
// (14 days ago 23:59:59.999, today 23:59:59.999]
|
||||
getEndOfDayTimestamp(subDays(today, 14)),
|
||||
getEndOfDayTimestamp(today)
|
||||
);
|
||||
|
||||
const last14DaysNewUserCounts = new Map(
|
||||
dailyNewUserCounts.map(({ date, count }) => [date, count])
|
||||
);
|
||||
const last14DaysNewUserCounts = new Map(
|
||||
dailyNewUserCounts.map(({ date, count }) => [date, count])
|
||||
);
|
||||
|
||||
const todayNewUserCount = last14DaysNewUserCounts.get(getDateString(today)) ?? 0;
|
||||
const yesterday = subDays(today, 1);
|
||||
const yesterdayNewUserCount = last14DaysNewUserCounts.get(getDateString(yesterday)) ?? 0;
|
||||
const todayDelta = todayNewUserCount - yesterdayNewUserCount;
|
||||
const todayNewUserCount = last14DaysNewUserCounts.get(getDateString(today)) ?? 0;
|
||||
const yesterday = subDays(today, 1);
|
||||
const yesterdayNewUserCount = last14DaysNewUserCounts.get(getDateString(yesterday)) ?? 0;
|
||||
const todayDelta = todayNewUserCount - yesterdayNewUserCount;
|
||||
|
||||
const last7DaysNewUserCount = indices(7)
|
||||
.map((index) => getDateString(subDays(today, index)))
|
||||
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
|
||||
const newUserCountFrom13DaysAgoTo7DaysAgo = indices(7)
|
||||
.map((index) => getDateString(subDays(today, index + 7)))
|
||||
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
|
||||
const last7DaysDelta = last7DaysNewUserCount - newUserCountFrom13DaysAgoTo7DaysAgo;
|
||||
const last7DaysNewUserCount = indices(7)
|
||||
.map((index) => getDateString(subDays(today, index)))
|
||||
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
|
||||
const newUserCountFrom13DaysAgoTo7DaysAgo = indices(7)
|
||||
.map((index) => getDateString(subDays(today, index + 7)))
|
||||
.reduce((sum, date) => sum + (last14DaysNewUserCounts.get(date) ?? 0), 0);
|
||||
const last7DaysDelta = last7DaysNewUserCount - newUserCountFrom13DaysAgoTo7DaysAgo;
|
||||
|
||||
ctx.body = {
|
||||
today: {
|
||||
count: todayNewUserCount,
|
||||
delta: todayDelta,
|
||||
},
|
||||
last7Days: {
|
||||
count: last7DaysNewUserCount,
|
||||
delta: last7DaysDelta,
|
||||
},
|
||||
};
|
||||
ctx.body = {
|
||||
today: {
|
||||
count: todayNewUserCount,
|
||||
delta: todayDelta,
|
||||
},
|
||||
last7Days: {
|
||||
count: last7DaysNewUserCount,
|
||||
delta: last7DaysDelta,
|
||||
},
|
||||
};
|
||||
|
||||
return next();
|
||||
});
|
||||
return next();
|
||||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/dashboard/users/active',
|
||||
koaGuard({
|
||||
query: object({ date: string().regex(dateRegex).optional() }),
|
||||
response: getActiveUsersResponseGuard,
|
||||
status: [200, 401, 403],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import type { StatisticsData } from '#src/api/index.js';
|
||||
import { getTotalUsersCount, getNewUsersData, getActiveUsersData } from '#src/api/index.js';
|
||||
import { api, getTotalUsersCount, getNewUsersData, getActiveUsersData } from '#src/api/index.js';
|
||||
import { createResponseWithCode } from '#src/helpers/admin-tenant.js';
|
||||
import { createUserByAdmin } from '#src/helpers/index.js';
|
||||
import { registerNewUser, signInWithPassword } from '#src/helpers/interactions.js';
|
||||
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
|
||||
|
@ -16,6 +17,16 @@ describe('admin console dashboard', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('non authorized request should return 401', async () => {
|
||||
await expect(api.get('dashboard/users/total')).rejects.toMatchObject(
|
||||
createResponseWithCode(401)
|
||||
);
|
||||
await expect(api.get('dashboard/users/new')).rejects.toMatchObject(createResponseWithCode(401));
|
||||
await expect(api.get('dashboard/users/active')).rejects.toMatchObject(
|
||||
createResponseWithCode(401)
|
||||
);
|
||||
});
|
||||
|
||||
it('should get total user count successfully', async () => {
|
||||
const { totalUserCount: originTotalUserCount } = await getTotalUsersCount();
|
||||
|
||||
|
|
23
packages/schemas/src/types/dashboard.ts
Normal file
23
packages/schemas/src/types/dashboard.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { number, object, array, string } from 'zod';
|
||||
|
||||
const dashboardUsersDataGuard = object({
|
||||
count: number(),
|
||||
delta: number(),
|
||||
});
|
||||
|
||||
export const getNewUsersResponseGuard = object({
|
||||
today: dashboardUsersDataGuard,
|
||||
last7Days: dashboardUsersDataGuard,
|
||||
});
|
||||
|
||||
export const getActiveUsersResponseGuard = object({
|
||||
dauCurve: array(
|
||||
object({
|
||||
date: string(),
|
||||
count: number(),
|
||||
})
|
||||
),
|
||||
dau: dashboardUsersDataGuard,
|
||||
wau: dashboardUsersDataGuard,
|
||||
mau: dashboardUsersDataGuard,
|
||||
});
|
|
@ -17,3 +17,4 @@ export * from './hook.js';
|
|||
export * from './service-log.js';
|
||||
export * from './theme.js';
|
||||
export * from './cookie.js';
|
||||
export * from './dashboard.js';
|
||||
|
|
Loading…
Reference in a new issue