0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

refactor(core): count new users by created_at (#2027)

This commit is contained in:
IceHe 2022-09-29 15:32:43 +08:00 committed by GitHub
parent 11381afdc5
commit cce2d40160
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 51 additions and 30 deletions

View file

@ -17,6 +17,7 @@ export const mockUser: User = {
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_789,
createdAt: 1_650_969_000_000,
};
export const mockUserResponse = pick(mockUser, ...userInfoSelectFields);
@ -38,6 +39,7 @@ export const mockUserWithPassword: User = {
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_789,
createdAt: 1_650_969_000_000,
};
export const mockUserList: User[] = [
@ -55,6 +57,7 @@ export const mockUserList: User[] = [
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_000,
createdAt: 1_650_969_000_000,
},
{
id: '2',
@ -70,6 +73,7 @@ export const mockUserList: User[] = [
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_000,
createdAt: 1_650_969_000_000,
},
{
id: '3',
@ -85,6 +89,7 @@ export const mockUserList: User[] = [
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_000,
createdAt: 1_650_969_000_000,
},
{
id: '4',
@ -100,6 +105,7 @@ export const mockUserList: User[] = [
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_000,
createdAt: 1_650_969_000_000,
},
{
id: '5',
@ -115,6 +121,7 @@ export const mockUserList: User[] = [
customData: {},
applicationId: 'bar',
lastSignInAt: 1_650_969_465_000,
createdAt: 1_650_969_000_000,
},
];

View file

@ -49,27 +49,6 @@ export const findLogs = async (limit: number, offset: number, logCondition: LogC
export const findLogById = buildFindEntityById<CreateLog, Log>(Logs);
const registerLogTypes: LogType[] = [
'RegisterUsernamePassword',
'RegisterEmail',
'RegisterSms',
'RegisterSocial',
];
export const getDailyNewUserCountsByTimeInterval = async (
startTimeExclusive: number,
endTimeInclusive: number
) =>
envSet.pool.any<{ date: string; count: number }>(sql`
select date(${fields.createdAt}), count(*)
from ${table}
where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000)
and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000)
and ${fields.type} in (${sql.join(registerLogTypes, sql`, `)})
and ${fields.payload}->>'result' = 'Success'
group by date(${fields.createdAt})
`);
// The active user should exchange the tokens by the authorization code (i.e. sign-in)
// or exchange the access token, which will expire in 2 hours, by the refresh token.
const activeUserLogTypes: LogType[] = ['CodeExchangeToken', 'RefreshTokenExchangeToken'];

View file

@ -158,3 +158,15 @@ export const hasActiveUsers = async () =>
from ${table}
limit 1
`);
export const getDailyNewUserCountsByTimeInterval = async (
startTimeExclusive: number,
endTimeInclusive: number
) =>
envSet.pool.any<{ date: string; count: number }>(sql`
select date(${fields.createdAt}), count(*)
from ${table}
where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000)
and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000)
group by date(${fields.createdAt})
`);

View file

@ -5,9 +5,16 @@ import { createRequester } from '@/utils/test-utils';
const totalUserCount = 1000;
const countUsers = jest.fn(async () => ({ count: totalUserCount }));
const getDailyNewUserCountsByTimeInterval = jest.fn(
async (startTimeExclusive: number, endTimeInclusive: number) => mockDailyNewUserCounts
);
jest.mock('@/queries/user', () => ({
countUsers: async () => countUsers(),
getDailyNewUserCountsByTimeInterval: async (
startTimeExclusive: number,
endTimeInclusive: number
) => getDailyNewUserCountsByTimeInterval(startTimeExclusive, endTimeInclusive),
}));
const mockDailyNewUserCounts = [
@ -32,9 +39,6 @@ const mockDailyActiveUserCounts = [
const mockActiveUserCount = 1000;
const getDailyNewUserCountsByTimeInterval = jest.fn(
async (startTimeExclusive: number, endTimeInclusive: number) => mockDailyNewUserCounts
);
const getDailyActiveUserCountsByTimeInterval = jest.fn(
async (startTimeExclusive: number, endTimeInclusive: number) => mockDailyActiveUserCounts
);
@ -43,10 +47,6 @@ const countActiveUsersByTimeInterval = jest.fn(
);
jest.mock('@/queries/log', () => ({
getDailyNewUserCountsByTimeInterval: async (
startTimeExclusive: number,
endTimeInclusive: number
) => getDailyNewUserCountsByTimeInterval(startTimeExclusive, endTimeInclusive),
getDailyActiveUserCountsByTimeInterval: async (
startTimeExclusive: number,
endTimeInclusive: number

View file

@ -6,9 +6,8 @@ import koaGuard from '@/middleware/koa-guard';
import {
countActiveUsersByTimeInterval,
getDailyActiveUserCountsByTimeInterval,
getDailyNewUserCountsByTimeInterval,
} from '@/queries/log';
import { countUsers } from '@/queries/user';
import { countUsers, getDailyNewUserCountsByTimeInterval } from '@/queries/user';
import { AuthedRouter } from './types';

View file

@ -0,0 +1,20 @@
import { sql } from 'slonik';
import { AlterationScript } from '../lib/types/alteration';
const alteration: AlterationScript = {
up: async (pool) => {
await pool.query(sql`
alter table users add column created_at timestamptz not null default (now());
create index users__created_at on users (created_at);
`);
},
down: async (pool) => {
await pool.query(sql`
drop index users__created_at;
alter table users drop column created_at;
`);
},
};
export default alteration;

View file

@ -11,6 +11,7 @@ export const userInfoSelectFields = Object.freeze([
'customData',
'identities',
'lastSignInAt',
'createdAt',
'applicationId',
] as const);

View file

@ -14,5 +14,8 @@ create table users (
identities jsonb /* @use Identities */ not null default '{}'::jsonb,
custom_data jsonb /* @use ArbitraryObject */ not null default '{}'::jsonb,
last_sign_in_at timestamptz,
created_at timestamptz not null default (now()),
primary key (id)
);
create index users__created_at on users (created_at);