mirror of
https://github.com/logto-io/logto.git
synced 2025-03-17 22:31:28 -05:00
feat(core): get /logs (#823)
* feat(core): get /logs * chore(core): rename userRequest to logRequest in UTs
This commit is contained in:
parent
82c7138683
commit
4ffd4c0480
4 changed files with 133 additions and 1 deletions
|
@ -1,5 +1,46 @@
|
|||
import { CreateLog, Logs } from '@logto/schemas';
|
||||
import { CreateLog, Log, Logs } from '@logto/schemas';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import { conditionalSql, convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Logs);
|
||||
|
||||
export const insertLog = buildInsertInto<CreateLog>(Logs);
|
||||
|
||||
export interface LogCondition {
|
||||
logType?: string;
|
||||
applicationId?: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
const buildLogConditionSql = (logCondition: LogCondition) =>
|
||||
conditionalSql(logCondition, ({ logType, applicationId, userId }) => {
|
||||
const subConditions = [
|
||||
conditionalSql(logType, (logType) => sql`${fields.type}=${logType}`),
|
||||
conditionalSql(userId, (userId) => sql`${fields.payload}->>'userId'=${userId}`),
|
||||
conditionalSql(
|
||||
applicationId,
|
||||
(applicationId) => sql`${fields.payload}->>'applicationId'=${applicationId}`
|
||||
),
|
||||
].filter(({ sql }) => sql);
|
||||
|
||||
return sql`where ${sql.join(subConditions, sql` and `)}`;
|
||||
});
|
||||
|
||||
export const countLogs = async (condition: LogCondition) =>
|
||||
envSet.pool.one<{ count: number }>(sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
${buildLogConditionSql(condition)}
|
||||
`);
|
||||
|
||||
export const findLogs = async (limit: number, offset: number, logCondition: LogCondition) =>
|
||||
envSet.pool.any<Log>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
${buildLogConditionSql(logCondition)}
|
||||
limit ${limit}
|
||||
offset ${offset}
|
||||
`);
|
||||
|
|
|
@ -15,6 +15,7 @@ import statusRoutes from '@/routes/status';
|
|||
import swaggerRoutes from '@/routes/swagger';
|
||||
|
||||
import adminUserRoutes from './admin-user';
|
||||
import logRoutes from './log';
|
||||
import roleRoutes from './role';
|
||||
import { AnonymousRouter, AuthedRouter } from './types';
|
||||
|
||||
|
@ -35,6 +36,7 @@ const createRouters = (provider: Provider) => {
|
|||
resourceRoutes(authedRouter);
|
||||
signInExperiencesRoutes(authedRouter);
|
||||
adminUserRoutes(authedRouter);
|
||||
logRoutes(authedRouter);
|
||||
roleRoutes(authedRouter);
|
||||
|
||||
return [sessionRouter, anonymousRouter, authedRouter];
|
||||
|
|
51
packages/core/src/routes/log.test.ts
Normal file
51
packages/core/src/routes/log.test.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
import { LogCondition } from '@/queries/log';
|
||||
import logRoutes from '@/routes/log';
|
||||
import { createRequester } from '@/utils/test-utils';
|
||||
|
||||
const mockLogs = [{ id: 1 }, { id: 2 }];
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
const countLogs = jest.fn(async (condition: LogCondition) => ({
|
||||
count: mockLogs.length,
|
||||
}));
|
||||
const findLogs = jest.fn(
|
||||
async (limit: number, offset: number, condition: LogCondition) => mockLogs
|
||||
);
|
||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||
|
||||
jest.mock('@/queries/log', () => ({
|
||||
countLogs: async (condition: LogCondition) => countLogs(condition),
|
||||
findLogs: async (limit: number, offset: number, condition: LogCondition) =>
|
||||
findLogs(limit, offset, condition),
|
||||
}));
|
||||
|
||||
describe('logRoutes', () => {
|
||||
const logRequest = createRequester({ authedRoutes: logRoutes });
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /logs', () => {
|
||||
it('should call countLogs and findLogs with correct parameters', async () => {
|
||||
const userId = 'userIdValue';
|
||||
const applicationId = 'foo';
|
||||
const logType = 'SignInUsernamePassword';
|
||||
const page = 1;
|
||||
const pageSize = 5;
|
||||
|
||||
await logRequest.get(
|
||||
`/logs?userId=${userId}&applicationId=${applicationId}&logType=${logType}&page=${page}&page_size=${pageSize}`
|
||||
);
|
||||
expect(countLogs).toHaveBeenCalledWith({ userId, applicationId, logType });
|
||||
expect(findLogs).toHaveBeenCalledWith(5, 0, { userId, applicationId, logType });
|
||||
});
|
||||
|
||||
it('should return correct response', async () => {
|
||||
const response = await logRequest.get(`/logs`);
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual(mockLogs);
|
||||
expect(response.header).toHaveProperty('total-number', `${mockLogs.length}`);
|
||||
});
|
||||
});
|
||||
});
|
38
packages/core/src/routes/log.ts
Normal file
38
packages/core/src/routes/log.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { object, string } from 'zod';
|
||||
|
||||
import koaGuard from '@/middleware/koa-guard';
|
||||
import koaPagination from '@/middleware/koa-pagination';
|
||||
import { countLogs, findLogs } from '@/queries/log';
|
||||
|
||||
import { AuthedRouter } from './types';
|
||||
|
||||
export default function logRoutes<T extends AuthedRouter>(router: T) {
|
||||
router.get(
|
||||
'/logs',
|
||||
koaPagination(),
|
||||
koaGuard({
|
||||
query: object({
|
||||
userId: string().optional(),
|
||||
applicationId: string().optional(),
|
||||
logType: string().optional(),
|
||||
}),
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
const { limit, offset } = ctx.pagination;
|
||||
const {
|
||||
query: { userId, applicationId, logType },
|
||||
} = ctx.guard;
|
||||
|
||||
const [{ count }, logs] = await Promise.all([
|
||||
countLogs({ logType, applicationId, userId }),
|
||||
findLogs(limit, offset, { logType, userId, applicationId }),
|
||||
]);
|
||||
|
||||
// Return totalCount to pagination middleware
|
||||
ctx.pagination.totalCount = count;
|
||||
ctx.body = logs;
|
||||
|
||||
return next();
|
||||
}
|
||||
);
|
||||
}
|
Loading…
Add table
Reference in a new issue