mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
refactor(core): move database pool into env set
This commit is contained in:
parent
b527cb6a83
commit
dff23b57db
35 changed files with 293 additions and 168 deletions
|
@ -2,8 +2,8 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
setupFilesAfterEnv: ['./jest.setup.js', 'jest-matcher-specific-error'],
|
||||
globalSetup: './jest.global-setup.js',
|
||||
setupFilesAfterEnv: ['./jest.setup.ts', 'jest-matcher-specific-error'],
|
||||
globalSetup: './jest.global-setup.ts',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: 'tsconfig.test.json',
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
/* eslint-disable unicorn/prefer-module */
|
||||
/**
|
||||
* Generate private key for tests
|
||||
*/
|
||||
const { generateKeyPairSync } = require('crypto');
|
||||
const { writeFileSync } = require('fs');
|
||||
import { generateKeyPairSync } from 'crypto';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
const privateKeyPath = 'oidc-private-key.test.pem';
|
||||
export const privateKeyPath = 'oidc-private-key.test.pem';
|
||||
|
||||
module.exports = () => {
|
||||
const globalSetup = () => {
|
||||
const { privateKey } = generateKeyPairSync('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
|
@ -23,7 +22,4 @@ module.exports = () => {
|
|||
writeFileSync(privateKeyPath, privateKey);
|
||||
};
|
||||
|
||||
exports = module.exports;
|
||||
exports.privateKeyPath = privateKeyPath;
|
||||
|
||||
/* eslint-enable unicorn/prefer-module */
|
||||
export default globalSetup;
|
|
@ -1,12 +0,0 @@
|
|||
/* eslint-disable unicorn/prefer-module */
|
||||
/**
|
||||
* Setup environment variables for unit test
|
||||
*/
|
||||
|
||||
const { privateKeyPath } = require('./jest.global-setup.js');
|
||||
|
||||
process.env = {
|
||||
...process.env,
|
||||
OIDC_PRIVATE_KEY_PATH: privateKeyPath,
|
||||
};
|
||||
/* eslint-enable unicorn/prefer-module */
|
15
packages/core/jest.setup.ts
Normal file
15
packages/core/jest.setup.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Setup environment variables for unit test
|
||||
*/
|
||||
|
||||
import envSet from '@/env-set';
|
||||
|
||||
import { privateKeyPath } from './jest.global-setup';
|
||||
|
||||
(async () => {
|
||||
process.env = {
|
||||
...process.env,
|
||||
OIDC_PRIVATE_KEY_PATH: privateKeyPath,
|
||||
};
|
||||
await envSet.load();
|
||||
})();
|
|
@ -1,6 +1,7 @@
|
|||
import { CreateUser, Users } from '@logto/schemas';
|
||||
import decamelize from 'decamelize';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import { InsertionError } from '@/errors/SlonikError';
|
||||
import { createTestPool } from '@/utils/test-utils';
|
||||
|
||||
|
@ -18,7 +19,9 @@ describe('buildInsertInto()', () => {
|
|||
const user: CreateUser = { id: 'foo', username: '456' };
|
||||
const expectInsertIntoSql = buildExpectedInsertIntoSql(Object.keys(user));
|
||||
const pool = createTestPool(expectInsertIntoSql.join('\n'));
|
||||
const insertInto = buildInsertInto(pool, Users);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const insertInto = buildInsertInto(Users);
|
||||
await expect(insertInto(user)).resolves.toBe(undefined);
|
||||
});
|
||||
|
||||
|
@ -32,8 +35,10 @@ describe('buildInsertInto()', () => {
|
|||
'set "primary_email"=excluded."primary_email"',
|
||||
].join('\n')
|
||||
);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const { fields } = convertToIdentifiers(Users);
|
||||
const insertInto = buildInsertInto(pool, Users, {
|
||||
const insertInto = buildInsertInto(Users, {
|
||||
onConflict: {
|
||||
fields: [fields.id, fields.username],
|
||||
setExcludedFields: [fields.primaryEmail],
|
||||
|
@ -53,7 +58,9 @@ describe('buildInsertInto()', () => {
|
|||
primaryEmail: String(primaryEmail),
|
||||
})
|
||||
);
|
||||
const insertInto = buildInsertInto(pool, Users, { returning: true });
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const insertInto = buildInsertInto(Users, { returning: true });
|
||||
await expect(
|
||||
insertInto({ id: 'foo', username: '123', primaryEmail: 'foo@bar.com' })
|
||||
).resolves.toStrictEqual(user);
|
||||
|
@ -63,7 +70,9 @@ describe('buildInsertInto()', () => {
|
|||
const user: CreateUser = { id: 'foo', username: '123', primaryEmail: 'foo@bar.com' };
|
||||
const expectInsertIntoSql = buildExpectedInsertIntoSql(Object.keys(user));
|
||||
const pool = createTestPool([...expectInsertIntoSql, 'returning *'].join('\n'));
|
||||
const insertInto = buildInsertInto(pool, Users, { returning: true });
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const insertInto = buildInsertInto(Users, { returning: true });
|
||||
const dataToInsert = { id: 'foo', username: '123', primaryEmail: 'foo@bar.com' };
|
||||
|
||||
await expect(insertInto(dataToInsert)).rejects.toMatchError(
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
|
||||
import { has } from '@silverhand/essentials';
|
||||
import { DatabasePoolType, IdentifierSqlTokenType, sql } from 'slonik';
|
||||
import { IdentifierSqlTokenType, sql } from 'slonik';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import { InsertionError } from '@/errors/SlonikError';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
|
||||
|
@ -36,12 +37,10 @@ type InsertIntoConfig = {
|
|||
|
||||
interface BuildInsertInto {
|
||||
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
|
||||
pool: DatabasePoolType,
|
||||
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
|
||||
config: InsertIntoConfigReturning
|
||||
): (data: OmitAutoSetFields<Schema>) => Promise<ReturnType>;
|
||||
<Schema extends SchemaLike>(
|
||||
pool: DatabasePoolType,
|
||||
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
|
||||
config?: InsertIntoConfig
|
||||
): (data: OmitAutoSetFields<Schema>) => Promise<void>;
|
||||
|
@ -51,7 +50,6 @@ export const buildInsertInto: BuildInsertInto = <
|
|||
Schema extends SchemaLike,
|
||||
ReturnType extends SchemaLike
|
||||
>(
|
||||
pool: DatabasePoolType,
|
||||
schema: GeneratedSchema<Schema>,
|
||||
config?: InsertIntoConfig | InsertIntoConfigReturning
|
||||
) => {
|
||||
|
@ -65,7 +63,7 @@ export const buildInsertInto: BuildInsertInto = <
|
|||
const insertingKeys = keys.filter((key) => has(data, key));
|
||||
const {
|
||||
rows: [entry],
|
||||
} = await pool.query<ReturnType>(sql`
|
||||
} = await envSet.pool.query<ReturnType>(sql`
|
||||
insert into ${table} (${sql.join(
|
||||
insertingKeys.map((key) => fields[key]),
|
||||
sql`, `
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import { createPool } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const interceptors = [...createInterceptors()];
|
||||
|
||||
const pool = createPool(envSet.values.dbUrl, { interceptors });
|
||||
|
||||
export default pool;
|
|
@ -1,5 +1,6 @@
|
|||
import { CreateUser, Users, Applications } from '@logto/schemas';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import { UpdateError } from '@/errors/SlonikError';
|
||||
import { createTestPool } from '@/utils/test-utils';
|
||||
|
||||
|
@ -10,7 +11,9 @@ describe('buildUpdateWhere()', () => {
|
|||
const pool = createTestPool(
|
||||
'update "users"\nset "username"=$1\nwhere "id"=$2 and "username"=$3'
|
||||
);
|
||||
const updateWhere = buildUpdateWhere(pool, Users);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Users);
|
||||
await expect(
|
||||
updateWhere({
|
||||
set: { username: '123' },
|
||||
|
@ -29,7 +32,9 @@ describe('buildUpdateWhere()', () => {
|
|||
primaryEmail: String(primaryEmail),
|
||||
})
|
||||
);
|
||||
const updateWhere = buildUpdateWhere(pool, Users, true);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Users, true);
|
||||
await expect(
|
||||
updateWhere({ set: { username: '123', primaryEmail: 'foo@bar.com' }, where: { id: 'foo' } })
|
||||
).resolves.toStrictEqual(user);
|
||||
|
@ -43,8 +48,9 @@ describe('buildUpdateWhere()', () => {
|
|||
customClientMetadata: String(customClientMetadata),
|
||||
})
|
||||
);
|
||||
const updateWhere = buildUpdateWhere(pool, Applications, true);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Applications, true);
|
||||
await expect(
|
||||
updateWhere({
|
||||
set: { customClientMetadata: { idTokenTtl: 3600 } },
|
||||
|
@ -57,7 +63,9 @@ describe('buildUpdateWhere()', () => {
|
|||
const pool = createTestPool(
|
||||
'update "users"\nset "username"=$1\nwhere "id"=$2 and "username"=$3'
|
||||
);
|
||||
const updateWhere = buildUpdateWhere(pool, Users);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Users);
|
||||
|
||||
await expect(
|
||||
updateWhere({
|
||||
|
@ -69,7 +77,9 @@ describe('buildUpdateWhere()', () => {
|
|||
|
||||
it('throws `entity.not_exists_with_id` error with `undefined` when `returning` is true', async () => {
|
||||
const pool = createTestPool('update "users"\nset "username"=$1\nwhere "id"=$2\nreturning *');
|
||||
const updateWhere = buildUpdateWhere(pool, Users, true);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Users, true);
|
||||
const updateWhereData = { set: { username: '123' }, where: { id: 'foo' } };
|
||||
|
||||
await expect(updateWhere(updateWhereData)).rejects.toMatchError(
|
||||
|
@ -81,7 +91,9 @@ describe('buildUpdateWhere()', () => {
|
|||
const pool = createTestPool(
|
||||
'update "users"\nset "username"=$1\nwhere "username"=$2\nreturning *'
|
||||
);
|
||||
const updateWhere = buildUpdateWhere(pool, Users, true);
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(pool);
|
||||
|
||||
const updateWhere = buildUpdateWhere(Users, true);
|
||||
const updateData = { set: { username: '123' }, where: { username: 'foo' } };
|
||||
|
||||
await expect(updateWhere(updateData)).rejects.toMatchError(new UpdateError(Users, updateData));
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
|
||||
import { notFalsy, Truthy } from '@silverhand/essentials';
|
||||
import { DatabasePoolType, sql } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import { UpdateError } from '@/errors/SlonikError';
|
||||
import assertThat from '@/utils/assert-that';
|
||||
import { isKeyOf } from '@/utils/schema';
|
||||
|
@ -11,22 +12,18 @@ import { conditionalSql, convertToIdentifiers, convertToPrimitiveOrSql } from '.
|
|||
|
||||
interface BuildUpdateWhere {
|
||||
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
|
||||
pool: DatabasePoolType,
|
||||
schema: GeneratedSchema<Schema>,
|
||||
returning: true
|
||||
): (data: UpdateWhereData<Schema>) => Promise<ReturnType>;
|
||||
<Schema extends SchemaLike>(
|
||||
pool: DatabasePoolType,
|
||||
schema: GeneratedSchema<Schema>,
|
||||
returning?: false
|
||||
): (data: UpdateWhereData<Schema>) => Promise<void>;
|
||||
<Schema extends SchemaLike>(schema: GeneratedSchema<Schema>, returning?: false): (
|
||||
data: UpdateWhereData<Schema>
|
||||
) => Promise<void>;
|
||||
}
|
||||
|
||||
export const buildUpdateWhere: BuildUpdateWhere = <
|
||||
Schema extends SchemaLike,
|
||||
ReturnType extends SchemaLike
|
||||
>(
|
||||
pool: DatabasePoolType,
|
||||
schema: GeneratedSchema<Schema>,
|
||||
returning = false
|
||||
) => {
|
||||
|
@ -58,7 +55,7 @@ export const buildUpdateWhere: BuildUpdateWhere = <
|
|||
return async ({ set, where }: UpdateWhereData<Schema>) => {
|
||||
const {
|
||||
rows: [data],
|
||||
} = await pool.query<ReturnType>(sql`
|
||||
} = await envSet.pool.query<ReturnType>(sql`
|
||||
update ${table}
|
||||
set ${sql.join(connectKeyValueWithEqualSign(set), sql`, `)}
|
||||
where ${sql.join(connectKeyValueWithEqualSign(where), sql` and `)}
|
||||
|
|
|
@ -3,7 +3,8 @@ import { Falsy, notFalsy } from '@silverhand/essentials';
|
|||
import dayjs from 'dayjs';
|
||||
import { sql, SqlSqlTokenType, SqlTokenType, IdentifierSqlTokenType } from 'slonik';
|
||||
|
||||
import pool from './pool';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
import { FieldIdentifiers, Table } from './types';
|
||||
|
||||
export const conditionalSql = <T>(
|
||||
|
@ -73,7 +74,7 @@ export const convertToIdentifiers = <T extends Table>(
|
|||
export const convertToTimestamp = (time = dayjs()) => sql`to_timestamp(${time.valueOf() / 1000})`;
|
||||
|
||||
export const getTotalRowCount = async (table: IdentifierSqlTokenType) =>
|
||||
pool.one<{ count: number }>(sql`
|
||||
envSet.pool.one<{ count: number }>(sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
`);
|
||||
|
|
|
@ -3,6 +3,8 @@ import { readFileSync } from 'fs';
|
|||
|
||||
import { assertEnv, getEnv, Optional } from '@silverhand/essentials';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { createPool, DatabasePoolType } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
import { string, number } from 'zod';
|
||||
|
||||
export enum MountedApps {
|
||||
|
@ -33,15 +35,16 @@ const loadOidcValues = (port: number) => {
|
|||
};
|
||||
};
|
||||
|
||||
const loadEnvValues = () => {
|
||||
const loadEnvValues = async () => {
|
||||
const isProduction = getEnv('NODE_ENV') === 'production';
|
||||
const isTest = getEnv('NODE_ENV') === 'test';
|
||||
const port = Number(getEnv('PORT', '3001'));
|
||||
const databaseUrl = isTest ? getEnv('DB_URL') : assertEnv('DB_URL');
|
||||
|
||||
return Object.freeze({
|
||||
isTest,
|
||||
isProduction,
|
||||
dbUrl: isTest ? getEnv('DB_URL') : assertEnv('DB_URL'),
|
||||
databaseUrl,
|
||||
httpsCert: process.env.HTTPS_CERT,
|
||||
httpsKey: process.env.HTTPS_KEY,
|
||||
port,
|
||||
|
@ -57,18 +60,44 @@ const loadEnvValues = () => {
|
|||
});
|
||||
};
|
||||
|
||||
const throwNotLoadedError = () => {
|
||||
throw new Error(
|
||||
'Env set is not loaded. Make sure to call `await envSet.load()` before using it.'
|
||||
);
|
||||
};
|
||||
|
||||
/* eslint-disable @silverhand/fp/no-let, @silverhand/fp/no-mutation */
|
||||
function createEnvSet() {
|
||||
// eslint-disable-next-line @silverhand/fp/no-let
|
||||
let values = loadEnvValues();
|
||||
let values: Optional<Awaited<ReturnType<typeof loadEnvValues>>>;
|
||||
let pool: Optional<DatabasePoolType>;
|
||||
|
||||
return {
|
||||
values,
|
||||
reload: () => {
|
||||
// eslint-disable-next-line @silverhand/fp/no-mutation
|
||||
values = loadEnvValues();
|
||||
get values() {
|
||||
if (!values) {
|
||||
return throwNotLoadedError();
|
||||
}
|
||||
|
||||
return values;
|
||||
},
|
||||
get pool() {
|
||||
if (!pool) {
|
||||
return throwNotLoadedError();
|
||||
}
|
||||
|
||||
return pool;
|
||||
},
|
||||
|
||||
load: async () => {
|
||||
values = await loadEnvValues();
|
||||
|
||||
if (!values.isTest) {
|
||||
const interceptors = [...createInterceptors()];
|
||||
pool = createPool(values.databaseUrl, { interceptors });
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
/* eslint-enable @silverhand/fp/no-let, @silverhand/fp/no-mutation */
|
||||
|
||||
const envSet = createEnvSet();
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import initI18n from './i18n/init';
|
|||
|
||||
(async () => {
|
||||
try {
|
||||
await envSet.load();
|
||||
const app = new Koa({
|
||||
proxy: envSet.values.trustingTlsOffloadingProxies,
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ import { jwtVerify } from 'jose/jwt/verify';
|
|||
import { Context } from 'koa';
|
||||
import { IRouterParamContext } from 'koa-router';
|
||||
|
||||
import envSet from '@/env-set';
|
||||
import RequestError from '@/errors/RequestError';
|
||||
import { createContextWithRouteParameters } from '@/utils/test-utils';
|
||||
|
||||
|
@ -30,19 +31,14 @@ describe('koaAuth middleware', () => {
|
|||
});
|
||||
|
||||
it('should read DEVELOPMENT_USER_ID from env variable first if not production', async () => {
|
||||
// Mock the @/env/consts
|
||||
process.env.DEVELOPMENT_USER_ID = 'foo';
|
||||
const spy = jest
|
||||
.spyOn(envSet, 'values', 'get')
|
||||
.mockReturnValue({ ...envSet.values, developmentUserId: 'foo' });
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable unicorn/prefer-module */
|
||||
const koaAuthModule = require('./koa-auth') as { default: typeof koaAuth };
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
/* eslint-enable @typescript-eslint/no-var-requires */
|
||||
/* eslint-enable unicorn/prefer-module */
|
||||
|
||||
await koaAuthModule.default()(ctx, next);
|
||||
await koaAuth()(ctx, next);
|
||||
expect(ctx.auth).toEqual('foo');
|
||||
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('should set user auth with given sub returned from accessToken', async () => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { MountedApps } from '@/env-set';
|
||||
import envSet, { MountedApps } from '@/env-set';
|
||||
import { createContextWithRouteParameters } from '@/utils/test-utils';
|
||||
|
||||
import koaSpaProxy from './koa-spa-proxy';
|
||||
|
@ -48,32 +48,38 @@ describe('koaSpaProxy middleware', () => {
|
|||
});
|
||||
|
||||
it('production env should overwrite the request path to root if no target ui file are detected', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
process.env.PASSWORD_PEPPERS = JSON.stringify(['foo']);
|
||||
process.env.DB_URL = 'some_db_url';
|
||||
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
|
||||
...envSet.values,
|
||||
isProduction: true,
|
||||
passwordPeppers: ['foo'],
|
||||
databaseUrl: 'some_db_url',
|
||||
});
|
||||
|
||||
const ctx = createContextWithRouteParameters({
|
||||
url: '/foo',
|
||||
});
|
||||
|
||||
const { default: proxy } = await import('./koa-spa-proxy');
|
||||
await proxy()(ctx, next);
|
||||
await koaSpaProxy()(ctx, next);
|
||||
|
||||
expect(mockStaticMiddleware).toBeCalled();
|
||||
expect(ctx.request.path).toEqual('/');
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it('production env should call the static middleware if path hit the ui file directory', async () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
process.env.PASSWORD_PEPPERS = JSON.stringify(['foo']);
|
||||
process.env.DB_URL = 'some_db_url';
|
||||
const spy = jest.spyOn(envSet, 'values', 'get').mockReturnValue({
|
||||
...envSet.values,
|
||||
isProduction: true,
|
||||
passwordPeppers: ['foo'],
|
||||
databaseUrl: 'some_db_url',
|
||||
});
|
||||
|
||||
const { default: proxy } = await import('./koa-spa-proxy');
|
||||
const ctx = createContextWithRouteParameters({
|
||||
url: '/sign-in',
|
||||
});
|
||||
|
||||
await proxy()(ctx, next);
|
||||
await koaSpaProxy()(ctx, next);
|
||||
expect(mockStaticMiddleware).toBeCalled();
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
convertToPrimitiveOrSql,
|
||||
excludeAutoSetFields,
|
||||
} from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
|
@ -22,7 +23,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Application, CreateApplication, Applications } from '@logto/schemas';
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import {
|
||||
convertToIdentifiers,
|
||||
|
@ -10,6 +9,7 @@ import {
|
|||
getTotalRowCount,
|
||||
conditionalSql,
|
||||
} from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Applications);
|
||||
|
@ -17,7 +17,7 @@ const { table, fields } = convertToIdentifiers(Applications);
|
|||
export const findTotalNumberOfApplications = async () => getTotalRowCount(table);
|
||||
|
||||
export const findAllApplications = async (limit: number, offset: number) =>
|
||||
pool.many<Application>(sql`
|
||||
envSet.pool.many<Application>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
order by ${fields.createdAt} desc
|
||||
|
@ -26,25 +26,17 @@ export const findAllApplications = async (limit: number, offset: number) =>
|
|||
`);
|
||||
|
||||
export const findApplicationById = async (id: string) =>
|
||||
pool.one<Application>(sql`
|
||||
envSet.pool.one<Application>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
export const insertApplication = buildInsertInto<CreateApplication, Application>(
|
||||
pool,
|
||||
Applications,
|
||||
{
|
||||
returning: true,
|
||||
}
|
||||
);
|
||||
export const insertApplication = buildInsertInto<CreateApplication, Application>(Applications, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
const updateApplication = buildUpdateWhere<CreateApplication, Application>(
|
||||
pool,
|
||||
Applications,
|
||||
true
|
||||
);
|
||||
const updateApplication = buildUpdateWhere<CreateApplication, Application>(Applications, true);
|
||||
|
||||
export const updateApplicationById = async (
|
||||
id: string,
|
||||
|
@ -52,7 +44,7 @@ export const updateApplicationById = async (
|
|||
) => updateApplication({ set, where: { id } });
|
||||
|
||||
export const deleteApplicationById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
const { rowCount } = await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { Connectors, ConnectorType, CreateConnector } from '@logto/schemas';
|
|||
import { createMockPool, createMockQueryResult, sql, QueryResultRowType } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
|
@ -13,7 +14,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -2,28 +2,28 @@ import { Connector, CreateConnector, Connectors } from '@logto/schemas';
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Connectors);
|
||||
|
||||
export const findAllConnectors = async () =>
|
||||
pool.many<Connector>(sql`
|
||||
envSet.pool.many<Connector>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
order by ${fields.enabled} desc, ${fields.id} asc
|
||||
`);
|
||||
|
||||
export const findConnectorById = async (id: string) =>
|
||||
pool.one<Connector>(sql`
|
||||
envSet.pool.one<Connector>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
export const insertConnector = buildInsertInto<CreateConnector, Connector>(pool, Connectors, {
|
||||
export const insertConnector = buildInsertInto<CreateConnector, Connector>(Connectors, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
export const updateConnector = buildUpdateWhere<CreateConnector, Connector>(pool, Connectors, true);
|
||||
export const updateConnector = buildUpdateWhere<CreateConnector, Connector>(Connectors, true);
|
||||
|
|
|
@ -2,6 +2,7 @@ import { OidcModelInstances, CreateOidcModelInstance } from '@logto/schemas';
|
|||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
|
@ -15,7 +16,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -8,8 +8,8 @@ import { conditional } from '@silverhand/essentials';
|
|||
import { sql, ValueExpressionType } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { convertToIdentifiers, convertToTimestamp } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
export type WithConsumed<T> = T & { consumed?: boolean };
|
||||
export type QueryResult = Pick<OidcModelInstance, 'payload' | 'consumedAt'>;
|
||||
|
@ -26,7 +26,7 @@ const withConsumed = <T>(data: T, consumedAt?: number | null): WithConsumed<T> =
|
|||
const convertResult = (result: QueryResult | null) =>
|
||||
conditional(result && withConsumed(result.payload, result.consumedAt));
|
||||
|
||||
export const upsertInstance = buildInsertInto<CreateOidcModelInstance>(pool, OidcModelInstances, {
|
||||
export const upsertInstance = buildInsertInto<CreateOidcModelInstance>(OidcModelInstances, {
|
||||
onConflict: {
|
||||
fields: [fields.modelName, fields.id],
|
||||
setExcludedFields: [fields.payload, fields.expiresAt],
|
||||
|
@ -40,7 +40,7 @@ const findByModel = (modelName: string) => sql`
|
|||
`;
|
||||
|
||||
export const findPayloadById = async (modelName: string, id: string) => {
|
||||
const result = await pool.maybeOne<QueryResult>(sql`
|
||||
const result = await envSet.pool.maybeOne<QueryResult>(sql`
|
||||
${findByModel(modelName)}
|
||||
and ${fields.id}=${id}
|
||||
`);
|
||||
|
@ -56,7 +56,7 @@ export const findPayloadByPayloadField = async <
|
|||
field: Field,
|
||||
value: T
|
||||
) => {
|
||||
const result = await pool.maybeOne<QueryResult>(sql`
|
||||
const result = await envSet.pool.maybeOne<QueryResult>(sql`
|
||||
${findByModel(modelName)}
|
||||
and ${fields.payload}->>${field}=${value}
|
||||
`);
|
||||
|
@ -65,7 +65,7 @@ export const findPayloadByPayloadField = async <
|
|||
};
|
||||
|
||||
export const consumeInstanceById = async (modelName: string, id: string) => {
|
||||
await pool.query(sql`
|
||||
await envSet.pool.query(sql`
|
||||
update ${table}
|
||||
set ${fields.consumedAt}=${convertToTimestamp()}
|
||||
where ${fields.modelName}=${modelName}
|
||||
|
@ -74,7 +74,7 @@ export const consumeInstanceById = async (modelName: string, id: string) => {
|
|||
};
|
||||
|
||||
export const destroyInstanceById = async (modelName: string, id: string) => {
|
||||
await pool.query(sql`
|
||||
await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.modelName}=${modelName}
|
||||
and ${fields.id}=${id}
|
||||
|
@ -82,7 +82,7 @@ export const destroyInstanceById = async (modelName: string, id: string) => {
|
|||
};
|
||||
|
||||
export const revokeInstanceByGrantId = async (modelName: string, grantId: string) => {
|
||||
await pool.query(sql`
|
||||
await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.modelName}=${modelName}
|
||||
and ${fields.payload}->>'grantId'=${grantId}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
convertToPrimitiveOrSql,
|
||||
excludeAutoSetFields,
|
||||
} from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
|
@ -22,7 +23,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -2,35 +2,35 @@ import { PasscodeType, Passcode, Passcodes, CreatePasscode } from '@logto/schema
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Passcodes);
|
||||
|
||||
export const findUnconsumedPasscodeByJtiAndType = async (jti: string, type: PasscodeType) =>
|
||||
pool.maybeOne<Passcode>(sql`
|
||||
envSet.pool.maybeOne<Passcode>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.interactionJti}=${jti} and ${fields.type}=${type} and ${fields.consumed} = false
|
||||
`);
|
||||
|
||||
export const findUnconsumedPasscodesByJtiAndType = async (jti: string, type: PasscodeType) =>
|
||||
pool.any<Passcode>(sql`
|
||||
envSet.pool.any<Passcode>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.interactionJti}=${jti} and ${fields.type}=${type} and ${fields.consumed} = false
|
||||
`);
|
||||
|
||||
export const insertPasscode = buildInsertInto<CreatePasscode, Passcode>(pool, Passcodes, {
|
||||
export const insertPasscode = buildInsertInto<CreatePasscode, Passcode>(Passcodes, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
export const updatePasscode = buildUpdateWhere<CreatePasscode, Passcode>(pool, Passcodes, true);
|
||||
export const updatePasscode = buildUpdateWhere<CreatePasscode, Passcode>(Passcodes, true);
|
||||
|
||||
export const deletePasscodeById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
const { rowCount } = await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
@ -41,7 +41,7 @@ export const deletePasscodeById = async (id: string) => {
|
|||
};
|
||||
|
||||
export const deletePasscodesByIds = async (ids: string[]) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
const { rowCount } = await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id} in (${ids.join(',')})
|
||||
`);
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
|||
|
||||
import { mockResource } from '@/__mocks__';
|
||||
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
|
@ -18,7 +19,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Resource, CreateResource, Resources } from '@logto/schemas';
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import {
|
||||
convertToIdentifiers,
|
||||
|
@ -10,6 +9,7 @@ import {
|
|||
getTotalRowCount,
|
||||
conditionalSql,
|
||||
} from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Resources);
|
||||
|
@ -17,7 +17,7 @@ const { table, fields } = convertToIdentifiers(Resources);
|
|||
export const findTotalNumberOfResources = async () => getTotalRowCount(table);
|
||||
|
||||
export const findAllResources = async (limit: number, offset: number) =>
|
||||
pool.many<Resource>(sql`
|
||||
envSet.pool.many<Resource>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
${conditionalSql(limit, (limit) => sql`limit ${limit}`)}
|
||||
|
@ -25,24 +25,24 @@ export const findAllResources = async (limit: number, offset: number) =>
|
|||
`);
|
||||
|
||||
export const findResourceByIndicator = async (indicator: string) =>
|
||||
pool.maybeOne<Resource>(sql`
|
||||
envSet.pool.maybeOne<Resource>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.indicator}=${indicator}
|
||||
`);
|
||||
|
||||
export const findResourceById = async (id: string) =>
|
||||
pool.one<Resource>(sql`
|
||||
envSet.pool.one<Resource>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
export const insertResource = buildInsertInto<CreateResource, Resource>(pool, Resources, {
|
||||
export const insertResource = buildInsertInto<CreateResource, Resource>(Resources, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
const updateResource = buildUpdateWhere<CreateResource, Resource>(pool, Resources, true);
|
||||
const updateResource = buildUpdateWhere<CreateResource, Resource>(Resources, true);
|
||||
|
||||
export const updateResourceById = async (
|
||||
id: string,
|
||||
|
@ -50,7 +50,7 @@ export const updateResourceById = async (
|
|||
) => updateResource({ set, where: { id } });
|
||||
|
||||
export const deleteResourceById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
const { rowCount } = await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
|
|
@ -3,13 +3,14 @@ import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
|||
|
||||
import { mockRole } from '@/__mocks__';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { findAllRoles, findRolesByRoleNames } from './roles';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
import { Roles, Role } from '@logto/schemas';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import pool from '@/database/pool';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Roles);
|
||||
|
||||
export const findAllRoles = async () =>
|
||||
pool.any<Role>(sql`
|
||||
envSet.pool.any<Role>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
`);
|
||||
|
||||
export const findRolesByRoleNames = async (roleNames: string[]) =>
|
||||
pool.any<Role>(sql`
|
||||
envSet.pool.any<Role>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.name} in (${sql.join(roleNames, sql`, `)})
|
||||
|
|
|
@ -3,13 +3,14 @@ import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
|||
|
||||
import { mockSetting } from '@/__mocks__';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { defaultSettingId, getSetting, updateSetting } from './setting';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { Setting, CreateSetting, Settings } from '@logto/schemas';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import { convertToIdentifiers, OmitAutoSetFields } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
export const defaultSettingId = 'default';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Settings);
|
||||
|
||||
export const getSetting = async () =>
|
||||
pool.one<Setting>(sql`
|
||||
envSet.pool.one<Setting>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${defaultSettingId}
|
||||
|
@ -18,7 +18,6 @@ export const getSetting = async () =>
|
|||
|
||||
export const updateSetting = async (setting: Partial<OmitAutoSetFields<CreateSetting>>) => {
|
||||
return buildUpdateWhere<CreateSetting, Setting>(
|
||||
pool,
|
||||
Settings,
|
||||
true
|
||||
)({ set: setting, where: { id: defaultSettingId } });
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { createMockPool, createMockQueryResult } from 'slonik';
|
||||
|
||||
import { mockSignInExperience } from '@/__mocks__';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { findDefaultSignInExperience, updateDefaultSignInExperience } from './sign-in-experience';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
import { SignInExperience, CreateSignInExperience, SignInExperiences } from '@logto/schemas';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(SignInExperiences);
|
||||
|
||||
const updateSignInExperience = buildUpdateWhere<CreateSignInExperience, SignInExperience>(
|
||||
pool,
|
||||
SignInExperiences,
|
||||
true
|
||||
);
|
||||
|
@ -19,7 +18,7 @@ export const updateDefaultSignInExperience = async (set: Partial<CreateSignInExp
|
|||
updateSignInExperience({ set, where: { id } });
|
||||
|
||||
export const findDefaultSignInExperience = async () =>
|
||||
pool.one<SignInExperience>(sql`
|
||||
envSet.pool.one<SignInExperience>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id} = ${id}
|
||||
|
|
67
packages/core/src/queries/user-log.test.ts
Normal file
67
packages/core/src/queries/user-log.test.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { UserLogs } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
import { snakeCase } from 'snake-case';
|
||||
|
||||
import { mockUserLog } from '@/__mocks__';
|
||||
import {
|
||||
convertToIdentifiers,
|
||||
excludeAutoSetFields,
|
||||
convertToPrimitiveOrSql,
|
||||
} from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { insertUserLog, findLogsByUserId } from './user-log';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('user-log query', () => {
|
||||
const { table, fields } = convertToIdentifiers(UserLogs);
|
||||
const dbvalue = { ...mockUserLog, payload: JSON.stringify(mockUserLog.payload) };
|
||||
|
||||
it('findLogsByUserId', async () => {
|
||||
const userId = 'foo';
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.userId}=${userId}
|
||||
order by created_at desc
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([userId]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findLogsByUserId(userId)).resolves.toEqual([dbvalue]);
|
||||
});
|
||||
|
||||
it('insertUserLog', async () => {
|
||||
const keys = excludeAutoSetFields(UserLogs.fieldKeys);
|
||||
|
||||
// eslint-disable-next-line sql/no-unsafe-query
|
||||
const expectSql = `
|
||||
insert into "user_logs" (${keys.map((k) => `"${snakeCase(k)}"`).join(', ')})
|
||||
values (${keys.map((_, index) => `$${index + 1}`).join(', ')})
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql);
|
||||
expect(values).toEqual(keys.map((k) => convertToPrimitiveOrSql(k, mockUserLog[k])));
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await insertUserLog(mockUserLog);
|
||||
});
|
||||
});
|
18
packages/core/src/queries/user-log.ts
Normal file
18
packages/core/src/queries/user-log.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { CreateUserLog, UserLogs } from '@logto/schemas';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(UserLogs);
|
||||
|
||||
export const insertUserLog = buildInsertInto<CreateUserLog>(UserLogs);
|
||||
|
||||
export const findLogsByUserId = async (userId: string) =>
|
||||
envSet.pool.many<CreateUserLog>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.userId}=${userId}
|
||||
order by created_at desc
|
||||
`);
|
|
@ -3,6 +3,7 @@ import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
|||
|
||||
import { mockUser } from '@/__mocks__';
|
||||
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
|
@ -28,7 +29,7 @@ import {
|
|||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
jest.spyOn(envSet, 'pool', 'get').mockReturnValue(
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
|
|
|
@ -2,43 +2,43 @@ import { User, CreateUser, Users } from '@logto/schemas';
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
import pool from '@/database/pool';
|
||||
import { buildUpdateWhere } from '@/database/update-where';
|
||||
import { conditionalSql, convertToIdentifiers, OmitAutoSetFields } from '@/database/utils';
|
||||
import envSet from '@/env-set';
|
||||
import { DeletionError, UpdateError } from '@/errors/SlonikError';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(Users);
|
||||
|
||||
export const findUserByUsername = async (username: string) =>
|
||||
pool.one<User>(sql`
|
||||
envSet.pool.one<User>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.username}=${username}
|
||||
`);
|
||||
|
||||
export const findUserByEmail = async (email: string) =>
|
||||
pool.one<User>(sql`
|
||||
envSet.pool.one<User>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.primaryEmail}=${email}
|
||||
`);
|
||||
|
||||
export const findUserByPhone = async (phone: string) =>
|
||||
pool.one<User>(sql`
|
||||
envSet.pool.one<User>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.primaryPhone}=${phone}
|
||||
`);
|
||||
|
||||
export const findUserById = async (id: string) =>
|
||||
pool.one<User>(sql`
|
||||
envSet.pool.one<User>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
export const findUserByIdentity = async (connectorId: string, userId: string) =>
|
||||
pool.one<User>(
|
||||
envSet.pool.one<User>(
|
||||
sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
|
@ -47,35 +47,35 @@ export const findUserByIdentity = async (connectorId: string, userId: string) =>
|
|||
);
|
||||
|
||||
export const hasUser = async (username: string) =>
|
||||
pool.exists(sql`
|
||||
envSet.pool.exists(sql`
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
where ${fields.username}=${username}
|
||||
`);
|
||||
|
||||
export const hasUserWithId = async (id: string) =>
|
||||
pool.exists(sql`
|
||||
envSet.pool.exists(sql`
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
export const hasUserWithEmail = async (email: string) =>
|
||||
pool.exists(sql`
|
||||
envSet.pool.exists(sql`
|
||||
select ${fields.primaryEmail}
|
||||
from ${table}
|
||||
where ${fields.primaryEmail}=${email}
|
||||
`);
|
||||
|
||||
export const hasUserWithPhone = async (phone: string) =>
|
||||
pool.exists(sql`
|
||||
envSet.pool.exists(sql`
|
||||
select ${fields.primaryPhone}
|
||||
from ${table}
|
||||
where ${fields.primaryPhone}=${phone}
|
||||
`);
|
||||
|
||||
export const hasUserWithIdentity = async (connectorId: string, userId: string) =>
|
||||
pool.exists(
|
||||
envSet.pool.exists(
|
||||
sql`
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
|
@ -83,7 +83,9 @@ export const hasUserWithIdentity = async (connectorId: string, userId: string) =
|
|||
`
|
||||
);
|
||||
|
||||
export const insertUser = buildInsertInto<CreateUser, User>(pool, Users, { returning: true });
|
||||
export const insertUser = buildInsertInto<CreateUser, User>(Users, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
const buildUserSearchConditionSql = (search: string) => {
|
||||
const searchFields = [fields.primaryEmail, fields.primaryPhone, fields.username, fields.name];
|
||||
|
@ -93,14 +95,14 @@ const buildUserSearchConditionSql = (search: string) => {
|
|||
};
|
||||
|
||||
export const countUsers = async (search?: string) =>
|
||||
pool.one<{ count: number }>(sql`
|
||||
envSet.pool.one<{ count: number }>(sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
${conditionalSql(search, (search) => sql`where ${buildUserSearchConditionSql(search)}`)}
|
||||
`);
|
||||
|
||||
export const findUsers = async (limit: number, offset: number, search?: string) =>
|
||||
pool.any<User>(
|
||||
envSet.pool.any<User>(
|
||||
sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
|
@ -110,13 +112,13 @@ export const findUsers = async (limit: number, offset: number, search?: string)
|
|||
`
|
||||
);
|
||||
|
||||
const updateUser = buildUpdateWhere<CreateUser, User>(pool, Users, true);
|
||||
const updateUser = buildUpdateWhere<CreateUser, User>(Users, true);
|
||||
|
||||
export const updateUserById = async (id: string, set: Partial<OmitAutoSetFields<CreateUser>>) =>
|
||||
updateUser({ set, where: { id } });
|
||||
|
||||
export const deleteUserById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
const { rowCount } = await envSet.pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
@ -127,7 +129,7 @@ export const deleteUserById = async (id: string) => {
|
|||
};
|
||||
|
||||
export const clearUserCustomDataById = async (id: string) => {
|
||||
const { rowCount } = await pool.query<User>(sql`
|
||||
const { rowCount } = await envSet.pool.query<User>(sql`
|
||||
update ${table}
|
||||
set ${fields.customData}='{}'::jsonb
|
||||
where ${fields.id}=${id}
|
||||
|
@ -139,7 +141,7 @@ export const clearUserCustomDataById = async (id: string) => {
|
|||
};
|
||||
|
||||
export const deleteUserIdentity = async (userId: string, connectorId: string) =>
|
||||
pool.one<User>(sql`
|
||||
envSet.pool.one<User>(sql`
|
||||
update ${table}
|
||||
set ${fields.identities}=${fields.identities}::jsonb-${connectorId}
|
||||
where ${fields.id}=${userId}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
"src",
|
||||
"jest.*.ts"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue