From 8f809da308da432b3c8633b19970d7c46ab85764 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 9 Jan 2023 15:29:01 +0800 Subject: [PATCH] refactor(core,cli): application query factory (#2853) --- packages/cli/src/queries/logto-config.ts | 13 +-- .../core/src/database/find-entity-by-id.ts | 56 ++++++----- .../core/src/database/insert-into.test.ts | 19 +--- packages/core/src/database/insert-into.ts | 74 +++++++------- packages/core/src/database/row-count.ts | 16 +-- .../core/src/database/update-where.test.ts | 25 ++--- packages/core/src/database/update-where.ts | 90 +++++++++-------- packages/core/src/libraries/logto-config.ts | 6 +- packages/core/src/queries/application.test.ts | 18 ++-- packages/core/src/queries/application.ts | 99 ++++++++++++------- packages/core/src/queries/connector.test.ts | 20 ++-- packages/core/src/queries/passcode.test.ts | 16 +-- packages/core/src/queries/resource.test.ts | 24 +++-- packages/core/src/queries/roles.test.ts | 22 ++--- packages/core/src/queries/setting.test.ts | 4 +- .../src/queries/sign-in-experience.test.ts | 9 +- packages/core/src/queries/user.test.ts | 30 +++--- 17 files changed, 282 insertions(+), 259 deletions(-) diff --git a/packages/cli/src/queries/logto-config.ts b/packages/cli/src/queries/logto-config.ts index b4d50d716..d207b690e 100644 --- a/packages/cli/src/queries/logto-config.ts +++ b/packages/cli/src/queries/logto-config.ts @@ -2,13 +2,13 @@ import type { AlterationState, LogtoConfig, LogtoConfigKey } from '@logto/schema import { logtoConfigGuards, LogtoConfigs, AlterationStateKey } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; import type { Nullable } from '@silverhand/essentials'; -import type { DatabasePool, DatabaseTransactionConnection } from 'slonik'; +import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik'; import { sql } from 'slonik'; import { z } from 'zod'; const { table, fields } = convertToIdentifiers(LogtoConfigs); -export const doesConfigsTableExist = async (pool: DatabasePool) => { +export const doesConfigsTableExist = async (pool: CommonQueryMethods) => { const { rows } = await pool.query<{ regclass: Nullable }>( sql`select to_regclass(${LogtoConfigs.table}) as regclass` ); @@ -16,17 +16,14 @@ export const doesConfigsTableExist = async (pool: DatabasePool) => { return Boolean(rows[0]?.regclass); }; -export const getRowsByKeys = async ( - pool: DatabasePool | DatabaseTransactionConnection, - keys: LogtoConfigKey[] -) => +export const getRowsByKeys = async (pool: CommonQueryMethods, keys: LogtoConfigKey[]) => pool.query(sql` select ${sql.join([fields.key, fields.value], sql`,`)} from ${table} where ${fields.key} in (${sql.join(keys, sql`,`)}) `); export const updateValueByKey = async ( - pool: DatabasePool | DatabaseTransactionConnection, + pool: CommonQueryMethods, key: T, value: z.infer ) => @@ -38,7 +35,7 @@ export const updateValueByKey = async ( ` ); -export const getCurrentDatabaseAlterationTimestamp = async (pool: DatabasePool) => { +export const getCurrentDatabaseAlterationTimestamp = async (pool: CommonQueryMethods) => { try { const result = await pool.maybeOne( sql`select * from ${table} where ${fields.key}=${AlterationStateKey.AlterationState}` diff --git a/packages/core/src/database/find-entity-by-id.ts b/packages/core/src/database/find-entity-by-id.ts index f65e45d8d..ddb7f6c37 100644 --- a/packages/core/src/database/find-entity-by-id.ts +++ b/packages/core/src/database/find-entity-by-id.ts @@ -1,5 +1,6 @@ import type { SchemaLike, GeneratedSchema } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; +import type { CommonQueryMethods } from 'slonik'; import { sql, NotFoundError } from 'slonik'; import envSet from '#src/env-set/index.js'; @@ -7,32 +8,37 @@ import RequestError from '#src/errors/RequestError/index.js'; import assertThat from '#src/utils/assert-that.js'; import { isKeyOf } from '#src/utils/schema.js'; -export const buildFindEntityById = ( - schema: GeneratedSchema -) => { - const { table, fields } = convertToIdentifiers(schema); - const isKeyOfSchema = isKeyOf(schema); +export const buildFindEntityByIdWithPool = + (pool: CommonQueryMethods) => + ( + schema: GeneratedSchema + ) => { + const { table, fields } = convertToIdentifiers(schema); + const isKeyOfSchema = isKeyOf(schema); - // Make sure id is key of the schema - assertThat(isKeyOfSchema('id'), 'entity.not_exists'); + // Make sure id is key of the schema + assertThat(isKeyOfSchema('id'), 'entity.not_exists'); - return async (id: string) => { - try { - return await envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id}=${id} - `); - } catch (error: unknown) { - if (error instanceof NotFoundError) { - throw new RequestError({ - code: 'entity.not_exists_with_id', - name: schema.table, - id, - status: 404, - }); + return async (id: string) => { + try { + return await pool.one(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.id}=${id} + `); + } catch (error: unknown) { + if (error instanceof NotFoundError) { + throw new RequestError({ + code: 'entity.not_exists_with_id', + name: schema.table, + id, + status: 404, + }); + } + throw error; } - throw error; - } + }; }; -}; + +/** @deprecated Will be removed soon. Use buildFindEntityByIdWithPool() factory instead. */ +export const buildFindEntityById = buildFindEntityByIdWithPool(envSet.pool); diff --git a/packages/core/src/database/insert-into.test.ts b/packages/core/src/database/insert-into.test.ts index 4fdb7f954..79e270c6a 100644 --- a/packages/core/src/database/insert-into.test.ts +++ b/packages/core/src/database/insert-into.test.ts @@ -3,15 +3,10 @@ import { Users } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; import decamelize from 'decamelize'; -import envSet from '#src/env-set/index.js'; import { InsertionError } from '#src/errors/SlonikError/index.js'; import { createTestPool } from '#src/utils/test-utils.js'; -import { buildInsertInto } from './insert-into.js'; - -const { jest } = import.meta; - -const poolSpy = jest.spyOn(envSet, 'pool', 'get'); +const { buildInsertIntoWithPool } = await import('./insert-into.js'); const buildExpectedInsertIntoSql = (keys: string[]) => [ // eslint-disable-next-line sql/no-unsafe-query @@ -24,9 +19,8 @@ describe('buildInsertInto()', () => { const user: CreateUser = { id: 'foo', username: '456', applicationId: 'bar' }; const expectInsertIntoSql = buildExpectedInsertIntoSql(Object.keys(user)); const pool = createTestPool(expectInsertIntoSql.join('\n')); - poolSpy.mockReturnValue(pool); - const insertInto = buildInsertInto(Users); + const insertInto = buildInsertIntoWithPool(pool)(Users); await expect(insertInto(user)).resolves.toBe(undefined); }); @@ -45,10 +39,9 @@ describe('buildInsertInto()', () => { 'set "primary_email"=excluded."primary_email"', ].join('\n') ); - poolSpy.mockReturnValue(pool); const { fields } = convertToIdentifiers(Users); - const insertInto = buildInsertInto(Users, { + const insertInto = buildInsertIntoWithPool(pool)(Users, { onConflict: { fields: [fields.id, fields.username], setExcludedFields: [fields.primaryEmail], @@ -74,9 +67,8 @@ describe('buildInsertInto()', () => { applicationId: String(applicationId), }) ); - poolSpy.mockReturnValue(pool); - const insertInto = buildInsertInto(Users, { returning: true }); + const insertInto = buildInsertIntoWithPool(pool)(Users, { returning: true }); await expect( insertInto({ id: 'foo', username: '123', primaryEmail: 'foo@bar.com', applicationId: 'bar' }) ).resolves.toStrictEqual(user); @@ -91,9 +83,8 @@ describe('buildInsertInto()', () => { }; const expectInsertIntoSql = buildExpectedInsertIntoSql(Object.keys(user)); const pool = createTestPool([...expectInsertIntoSql, 'returning *'].join('\n')); - poolSpy.mockReturnValue(pool); - const insertInto = buildInsertInto(Users, { returning: true }); + const insertInto = buildInsertIntoWithPool(pool)(Users, { returning: true }); const dataToInsert = { id: 'foo', username: '123', diff --git a/packages/core/src/database/insert-into.ts b/packages/core/src/database/insert-into.ts index 404a7b378..fe7c5ea3a 100644 --- a/packages/core/src/database/insert-into.ts +++ b/packages/core/src/database/insert-into.ts @@ -7,7 +7,7 @@ import { conditionalSql, } from '@logto/shared'; import { has } from '@silverhand/essentials'; -import type { IdentifierSqlToken } from 'slonik'; +import type { CommonQueryMethods, IdentifierSqlToken } from 'slonik'; import { sql } from 'slonik'; import envSet from '#src/env-set/index.js'; @@ -46,44 +46,46 @@ type BuildInsertInto = { ): (data: OmitAutoSetFields) => Promise; }; -export const buildInsertInto: BuildInsertInto = < - Schema extends SchemaLike, - ReturnType extends SchemaLike ->( - schema: GeneratedSchema, - config?: InsertIntoConfig | InsertIntoConfigReturning -) => { - const { fieldKeys, ...rest } = schema; - const { table, fields } = convertToIdentifiers(rest); - const keys = excludeAutoSetFields(fieldKeys); - const returning = Boolean(config?.returning); - const onConflict = config?.onConflict; +export const buildInsertIntoWithPool = + (pool: CommonQueryMethods): BuildInsertInto => + ( + schema: GeneratedSchema, + config?: InsertIntoConfig | InsertIntoConfigReturning + ) => { + const { fieldKeys, ...rest } = schema; + const { table, fields } = convertToIdentifiers(rest); + const keys = excludeAutoSetFields(fieldKeys); + const returning = Boolean(config?.returning); + const onConflict = config?.onConflict; - return async (data: OmitAutoSetFields): Promise => { - const insertingKeys = keys.filter((key) => has(data, key)); - const { - rows: [entry], - } = await envSet.pool.query(sql` - insert into ${table} (${sql.join( - insertingKeys.map((key) => fields[key]), - sql`, ` - )}) - values (${sql.join( - insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)), + return async (data: OmitAutoSetFields): Promise => { + const insertingKeys = keys.filter((key) => has(data, key)); + const { + rows: [entry], + } = await pool.query(sql` + insert into ${table} (${sql.join( + insertingKeys.map((key) => fields[key]), sql`, ` )}) - ${conditionalSql( - onConflict, - ({ fields, setExcludedFields }) => sql` - on conflict (${sql.join(fields, sql`, `)}) do update - set ${setExcluded(...setExcludedFields)} - ` - )} - ${conditionalSql(returning, () => sql`returning *`)} - `); + values (${sql.join( + insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)), + sql`, ` + )}) + ${conditionalSql( + onConflict, + ({ fields, setExcludedFields }) => sql` + on conflict (${sql.join(fields, sql`, `)}) do update + set ${setExcluded(...setExcludedFields)} + ` + )} + ${conditionalSql(returning, () => sql`returning *`)} + `); - assertThat(!returning || entry, new InsertionError(schema, data)); + assertThat(!returning || entry, new InsertionError(schema, data)); - return entry; + return entry; + }; }; -}; + +/** @deprecated Will be removed soon. Use buildInsertIntoWithPool() factory instead. */ +export const buildInsertInto = buildInsertIntoWithPool(envSet.pool); diff --git a/packages/core/src/database/row-count.ts b/packages/core/src/database/row-count.ts index 64d3e04a7..3a4f52573 100644 --- a/packages/core/src/database/row-count.ts +++ b/packages/core/src/database/row-count.ts @@ -1,10 +1,14 @@ -import type { IdentifierSqlToken } from 'slonik'; +import type { CommonQueryMethods, IdentifierSqlToken } from 'slonik'; import { sql } from 'slonik'; import envSet from '#src/env-set/index.js'; -export const getTotalRowCount = async (table: IdentifierSqlToken) => - envSet.pool.one<{ count: number }>(sql` - select count(*) - from ${table} - `); +export const getTotalRowCountWithPool = + (pool: CommonQueryMethods) => async (table: IdentifierSqlToken) => + pool.one<{ count: number }>(sql` + select count(*) + from ${table} + `); + +/** @deprecated Will be removed soon. Use getTotalRowCountWithPool() factory instead. */ +export const getTotalRowCount = getTotalRowCountWithPool(envSet.pool); diff --git a/packages/core/src/database/update-where.test.ts b/packages/core/src/database/update-where.test.ts index f22cbbd91..e9397ed2d 100644 --- a/packages/core/src/database/update-where.test.ts +++ b/packages/core/src/database/update-where.test.ts @@ -2,24 +2,18 @@ import type { CreateUser, User } from '@logto/schemas'; import { Users, Applications } from '@logto/schemas'; import type { UpdateWhereData } from '@logto/shared'; -import envSet from '#src/env-set/index.js'; import { UpdateError } from '#src/errors/SlonikError/index.js'; import { createTestPool } from '#src/utils/test-utils.js'; -import { buildUpdateWhere } from './update-where.js'; - -const { jest } = import.meta; - -const poolSpy = jest.spyOn(envSet, 'pool', 'get'); +const { buildUpdateWhereWithPool } = await import('./update-where.js'); describe('buildUpdateWhere()', () => { it('resolves a promise with `undefined` when `returning` is false', async () => { const pool = createTestPool( 'update "users"\nset "username"=$1\nwhere "id"=$2 and "username"=$3' ); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Users); + const updateWhere = buildUpdateWhereWithPool(pool)(Users); await expect( updateWhere({ set: { username: '123' }, @@ -45,9 +39,8 @@ describe('buildUpdateWhere()', () => { applicationId: String(applicationId), }) ); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Users, true); + const updateWhere = buildUpdateWhereWithPool(pool)(Users, true); await expect( updateWhere({ set: { username: '123', primaryEmail: 'foo@bar.com', applicationId: 'bar' }, @@ -65,9 +58,8 @@ describe('buildUpdateWhere()', () => { customClientMetadata: String(customClientMetadata), }) ); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Applications, true); + const updateWhere = buildUpdateWhereWithPool(pool)(Applications, true); await expect( updateWhere({ set: { customClientMetadata: { idTokenTtl: 3600 } }, @@ -81,9 +73,8 @@ describe('buildUpdateWhere()', () => { const pool = createTestPool( 'update "users"\nset "username"=$1\nwhere "id"=$2 and "username"=$3' ); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Users); + const updateWhere = buildUpdateWhereWithPool(pool)(Users); await expect( updateWhere({ @@ -96,9 +87,8 @@ 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 *'); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Users, true); + const updateWhere = buildUpdateWhereWithPool(pool)(Users, true); const updateWhereData: UpdateWhereData = { set: { username: '123' }, where: { id: 'foo' }, @@ -114,9 +104,8 @@ describe('buildUpdateWhere()', () => { const pool = createTestPool( 'update "users"\nset "username"=$1\nwhere "username"=$2\nreturning *' ); - poolSpy.mockReturnValue(pool); - const updateWhere = buildUpdateWhere(Users, true); + const updateWhere = buildUpdateWhereWithPool(pool)(Users, true); const updateWhereData: UpdateWhereData = { set: { username: '123' }, where: { username: 'foo' }, diff --git a/packages/core/src/database/update-where.ts b/packages/core/src/database/update-where.ts index 99d3e74b4..57bc5db22 100644 --- a/packages/core/src/database/update-where.ts +++ b/packages/core/src/database/update-where.ts @@ -3,6 +3,7 @@ import { convertToIdentifiers, convertToPrimitiveOrSql, conditionalSql } from '@ import type { UpdateWhereData } from '@logto/shared'; import type { Truthy } from '@silverhand/essentials'; import { notFalsy } from '@silverhand/essentials'; +import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; import envSet from '#src/env-set/index.js'; @@ -20,50 +21,57 @@ type BuildUpdateWhere = { ) => Promise; }; -export const buildUpdateWhere: BuildUpdateWhere = < - Schema extends SchemaLike, - ReturnType extends SchemaLike ->( - schema: GeneratedSchema, - returning = false -) => { - const { table, fields } = convertToIdentifiers(schema); - const isKeyOfSchema = isKeyOf(schema); - const connectKeyValueWithEqualSign = (data: Partial, jsonbMode: 'replace' | 'merge') => - Object.entries(data) - .map(([key, value]) => { - if (!isKeyOfSchema(key)) { - return; - } +export const buildUpdateWhereWithPool = + (pool: CommonQueryMethods): BuildUpdateWhere => + ( + schema: GeneratedSchema, + returning = false + ) => { + const { table, fields } = convertToIdentifiers(schema); + const isKeyOfSchema = isKeyOf(schema); + const connectKeyValueWithEqualSign = (data: Partial, jsonbMode: 'replace' | 'merge') => + Object.entries(data) + .map(([key, value]) => { + if (!isKeyOfSchema(key)) { + return; + } - if (jsonbMode === 'merge' && value && typeof value === 'object' && !Array.isArray(value)) { - /** - * Jsonb || operator is used to shallow merge two jsonb types of data - * all jsonb data field must be non-nullable - * https://www.postgresql.org/docs/current/functions-json.html - */ - return sql` - ${fields[key]}= - coalesce(${fields[key]},'{}'::jsonb)|| ${convertToPrimitiveOrSql(key, value)} - `; - } + if ( + jsonbMode === 'merge' && + value && + typeof value === 'object' && + !Array.isArray(value) + ) { + /** + * Jsonb || operator is used to shallow merge two jsonb types of data + * all jsonb data field must be non-nullable + * https://www.postgresql.org/docs/current/functions-json.html + */ + return sql` + ${fields[key]}= + coalesce(${fields[key]},'{}'::jsonb)|| ${convertToPrimitiveOrSql(key, value)} + `; + } - return sql`${fields[key]}=${convertToPrimitiveOrSql(key, value)}`; - }) - .filter((value): value is Truthy => notFalsy(value)); + return sql`${fields[key]}=${convertToPrimitiveOrSql(key, value)}`; + }) + .filter((value): value is Truthy => notFalsy(value)); - return async ({ set, where, jsonbMode }: UpdateWhereData) => { - const { - rows: [data], - } = await envSet.pool.query(sql` - update ${table} - set ${sql.join(connectKeyValueWithEqualSign(set, jsonbMode), sql`, `)} - where ${sql.join(connectKeyValueWithEqualSign(where, jsonbMode), sql` and `)} - ${conditionalSql(returning, () => sql`returning *`)} - `); + return async ({ set, where, jsonbMode }: UpdateWhereData) => { + const { + rows: [data], + } = await pool.query(sql` + update ${table} + set ${sql.join(connectKeyValueWithEqualSign(set, jsonbMode), sql`, `)} + where ${sql.join(connectKeyValueWithEqualSign(where, jsonbMode), sql` and `)} + ${conditionalSql(returning, () => sql`returning *`)} + `); - assertThat(!returning || data, new UpdateError(schema, { set, where, jsonbMode })); + assertThat(!returning || data, new UpdateError(schema, { set, where, jsonbMode })); - return data; + return data; + }; }; -}; + +/** @deprecated Will be removed soon. Use buildUpdateWhereWithPool() factory instead. */ +export const buildUpdateWhere = buildUpdateWhereWithPool(envSet.pool); diff --git a/packages/core/src/libraries/logto-config.ts b/packages/core/src/libraries/logto-config.ts index dfc0361fb..70dcf56b9 100644 --- a/packages/core/src/libraries/logto-config.ts +++ b/packages/core/src/libraries/logto-config.ts @@ -2,12 +2,10 @@ import { getRowsByKeys } from '@logto/cli/lib/queries/logto-config.js'; import type { LogtoOidcConfigType } from '@logto/schemas'; import { logtoOidcConfigGuard, LogtoOidcConfigKey } from '@logto/schemas'; import chalk from 'chalk'; -import type { DatabasePool, DatabaseTransactionConnection } from 'slonik'; +import type { CommonQueryMethods } from 'slonik'; import { z, ZodError } from 'zod'; -export const getOidcConfigs = async ( - pool: DatabasePool | DatabaseTransactionConnection -): Promise => { +export const getOidcConfigs = async (pool: CommonQueryMethods): Promise => { try { const { rows } = await getRowsByKeys(pool, Object.values(LogtoOidcConfigKey)); diff --git a/packages/core/src/queries/application.test.ts b/packages/core/src/queries/application.test.ts index 9758fb402..30bfab90e 100644 --- a/packages/core/src/queries/application.test.ts +++ b/packages/core/src/queries/application.test.ts @@ -9,15 +9,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findTotalNumberOfApplications, - findAllApplications, - findApplicationById, - insertApplication, - updateApplicationById, - deleteApplicationById, -} from './application.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -30,6 +21,15 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + findTotalNumberOfApplications, + findAllApplications, + findApplicationById, + insertApplication, + updateApplicationById, + deleteApplicationById, +} = await import('./application.js'); + describe('application query', () => { const { table, fields } = convertToIdentifiers(Applications); diff --git a/packages/core/src/queries/application.ts b/packages/core/src/queries/application.ts index 6956ebdb7..69c36a336 100644 --- a/packages/core/src/queries/application.ts +++ b/packages/core/src/queries/application.ts @@ -2,52 +2,77 @@ import type { Application, CreateApplication } from '@logto/schemas'; import { Applications } from '@logto/schemas'; import type { OmitAutoSetFields } from '@logto/shared'; import { convertToIdentifiers, conditionalSql, manyRows } from '@logto/shared'; +import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; -import { buildFindEntityById } from '#src/database/find-entity-by-id.js'; -import { buildInsertInto } from '#src/database/insert-into.js'; -import { getTotalRowCount } from '#src/database/row-count.js'; -import { buildUpdateWhere } from '#src/database/update-where.js'; +import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'; +import { buildInsertIntoWithPool } from '#src/database/insert-into.js'; +import { getTotalRowCountWithPool } from '#src/database/row-count.js'; +import { buildUpdateWhereWithPool } from '#src/database/update-where.js'; import envSet from '#src/env-set/index.js'; import { DeletionError } from '#src/errors/SlonikError/index.js'; const { table, fields } = convertToIdentifiers(Applications); -export const findTotalNumberOfApplications = async () => getTotalRowCount(table); - -export const findAllApplications = async (limit: number, offset: number) => - manyRows( - envSet.pool.query(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - order by ${fields.createdAt} desc - ${conditionalSql(limit, (limit) => sql`limit ${limit}`)} - ${conditionalSql(offset, (offset) => sql`offset ${offset}`)} - `) +export const createApplicationQueries = (pool: CommonQueryMethods) => { + const findTotalNumberOfApplications = async () => getTotalRowCountWithPool(pool)(table); + const findAllApplications = async (limit: number, offset: number) => + manyRows( + pool.query(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + order by ${fields.createdAt} desc + ${conditionalSql(limit, (limit) => sql`limit ${limit}`)} + ${conditionalSql(offset, (offset) => sql`offset ${offset}`)} + `) + ); + const findApplicationById = buildFindEntityByIdWithPool(pool)( + Applications ); + const insertApplication = buildInsertIntoWithPool(pool)( + Applications, + { + returning: true, + } + ); + const updateApplication = buildUpdateWhereWithPool(pool)( + Applications, + true + ); + const updateApplicationById = async ( + id: string, + set: Partial> + ) => updateApplication({ set, where: { id }, jsonbMode: 'merge' }); -export const findApplicationById = buildFindEntityById( - Applications -); + const deleteApplicationById = async (id: string) => { + const { rowCount } = await envSet.pool.query(sql` + delete from ${table} + where ${fields.id}=${id} + `); -export const insertApplication = buildInsertInto(Applications, { - returning: true, -}); + if (rowCount < 1) { + throw new DeletionError(Applications.table, id); + } + }; -const updateApplication = buildUpdateWhere(Applications, true); - -export const updateApplicationById = async ( - id: string, - set: Partial> -) => updateApplication({ set, where: { id }, jsonbMode: 'merge' }); - -export const deleteApplicationById = async (id: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id}=${id} - `); - - if (rowCount < 1) { - throw new DeletionError(Applications.table, id); - } + return { + findTotalNumberOfApplications, + findAllApplications, + findApplicationById, + insertApplication, + updateApplication, + updateApplicationById, + deleteApplicationById, + }; }; + +/** @deprecated Will be removed soon. Use createApplicationQueries() factory instead. */ +export const { + findTotalNumberOfApplications, + findAllApplications, + findApplicationById, + insertApplication, + updateApplication, + updateApplicationById, + deleteApplicationById, +} = createApplicationQueries(envSet.pool); diff --git a/packages/core/src/queries/connector.test.ts b/packages/core/src/queries/connector.test.ts index 14ece2af8..dc5b29723 100644 --- a/packages/core/src/queries/connector.test.ts +++ b/packages/core/src/queries/connector.test.ts @@ -8,16 +8,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findAllConnectors, - findConnectorById, - countConnectorByConnectorId, - deleteConnectorById, - deleteConnectorByIds, - insertConnector, - updateConnector, -} from './connector.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -30,6 +20,16 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + findAllConnectors, + findConnectorById, + countConnectorByConnectorId, + deleteConnectorById, + deleteConnectorByIds, + insertConnector, + updateConnector, +} = await import('./connector.js'); + describe('connector queries', () => { const { table, fields } = convertToIdentifiers(Connectors); diff --git a/packages/core/src/queries/passcode.test.ts b/packages/core/src/queries/passcode.test.ts index c6f912831..a3f259117 100644 --- a/packages/core/src/queries/passcode.test.ts +++ b/packages/core/src/queries/passcode.test.ts @@ -10,14 +10,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findUnconsumedPasscodeByJtiAndType, - findUnconsumedPasscodesByJtiAndType, - insertPasscode, - deletePasscodeById, - deletePasscodesByIds, -} from './passcode.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -30,6 +22,14 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + findUnconsumedPasscodeByJtiAndType, + findUnconsumedPasscodesByJtiAndType, + insertPasscode, + deletePasscodeById, + deletePasscodesByIds, +} = await import('./passcode.js'); + describe('passcode query', () => { const { table, fields } = convertToIdentifiers(Passcodes); diff --git a/packages/core/src/queries/resource.test.ts b/packages/core/src/queries/resource.test.ts index da5b75483..955baa923 100644 --- a/packages/core/src/queries/resource.test.ts +++ b/packages/core/src/queries/resource.test.ts @@ -8,16 +8,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findTotalNumberOfResources, - findAllResources, - findResourceById, - findResourceByIndicator, - insertResource, - updateResourceById, - deleteResourceById, -} from './resource.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -30,9 +20,23 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + findTotalNumberOfResources, + findAllResources, + findResourceById, + findResourceByIndicator, + insertResource, + updateResourceById, + deleteResourceById, +} = await import('./resource.js'); + describe('resource query', () => { const { table, fields } = convertToIdentifiers(Resources); + afterEach(() => { + mockQuery.mockClear(); + }); + it('findTotalNumberOfResources', async () => { const expectSql = sql` select count(*) diff --git a/packages/core/src/queries/roles.test.ts b/packages/core/src/queries/roles.test.ts index ffb0a34f6..d3f797c83 100644 --- a/packages/core/src/queries/roles.test.ts +++ b/packages/core/src/queries/roles.test.ts @@ -8,17 +8,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - deleteRoleById, - findRoleById, - findRoleByRoleName, - findRolesByRoleIds, - findRolesByRoleNames, - insertRole, - insertRoles, - updateRoleById, -} from './roles.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -31,6 +20,17 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + deleteRoleById, + findRoleById, + findRoleByRoleName, + findRolesByRoleIds, + findRolesByRoleNames, + insertRole, + insertRoles, + updateRoleById, +} = await import('./roles.js'); + describe('roles query', () => { const { table, fields } = convertToIdentifiers(Roles); diff --git a/packages/core/src/queries/setting.test.ts b/packages/core/src/queries/setting.test.ts index 75b89ea21..664999f4e 100644 --- a/packages/core/src/queries/setting.test.ts +++ b/packages/core/src/queries/setting.test.ts @@ -7,8 +7,6 @@ import envSet from '#src/env-set/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { defaultSettingId, getSetting, updateSetting } from './setting.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -21,6 +19,8 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { defaultSettingId, getSetting, updateSetting } = await import('./setting.js'); + describe('setting query', () => { const { table, fields } = convertToIdentifiers(Settings); const dbvalue = { ...mockSetting, adminConsole: JSON.stringify(mockSetting.adminConsole) }; diff --git a/packages/core/src/queries/sign-in-experience.test.ts b/packages/core/src/queries/sign-in-experience.test.ts index c2ed989ed..e22b07e8b 100644 --- a/packages/core/src/queries/sign-in-experience.test.ts +++ b/packages/core/src/queries/sign-in-experience.test.ts @@ -5,11 +5,6 @@ import envSet from '#src/env-set/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findDefaultSignInExperience, - updateDefaultSignInExperience, -} from './sign-in-experience.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -22,6 +17,10 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { findDefaultSignInExperience, updateDefaultSignInExperience } = await import( + './sign-in-experience.js' +); + describe('sign-in-experience query', () => { const id = 'default'; diff --git a/packages/core/src/queries/user.test.ts b/packages/core/src/queries/user.test.ts index b7035964d..900e022db 100644 --- a/packages/core/src/queries/user.test.ts +++ b/packages/core/src/queries/user.test.ts @@ -8,21 +8,6 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { QueryType } from '#src/utils/test-utils.js'; import { expectSqlAssert } from '#src/utils/test-utils.js'; -import { - findUserByUsername, - findUserByEmail, - findUserByPhone, - findUserByIdentity, - hasUser, - hasUserWithId, - hasUserWithEmail, - hasUserWithIdentity, - hasUserWithPhone, - updateUserById, - deleteUserById, - deleteUserIdentity, -} from './user.js'; - const { jest } = import.meta; const mockQuery: jest.MockedFunction = jest.fn(); @@ -35,6 +20,21 @@ jest.spyOn(envSet, 'pool', 'get').mockReturnValue( }) ); +const { + findUserByUsername, + findUserByEmail, + findUserByPhone, + findUserByIdentity, + hasUser, + hasUserWithId, + hasUserWithEmail, + hasUserWithIdentity, + hasUserWithPhone, + updateUserById, + deleteUserById, + deleteUserIdentity, +} = await import('./user.js'); + describe('user query', () => { const { table, fields } = convertToIdentifiers(Users); const { fields: rolesFields, table: rolesTable } = convertToIdentifiers(Roles);