From 4b21b7d16e283cfeff1aff42641c6ac57003f65d Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Mon, 9 Jan 2023 16:32:35 +0800 Subject: [PATCH] refactor(core): multiple query factories (#2857) --- .../core/src/queries/oidc-model-instance.ts | 152 +++++++++------- packages/core/src/queries/passcode.ts | 127 ++++++++------ packages/core/src/queries/resource.ts | 116 ++++++++----- packages/core/src/queries/roles-scopes.ts | 53 +++--- packages/core/src/queries/roles.ts | 164 +++++++++++------- packages/core/src/queries/scope.ts | 145 +++++++++------- packages/core/src/queries/setting.ts | 28 ++- .../core/src/queries/sign-in-experience.ts | 34 ++-- 8 files changed, 493 insertions(+), 326 deletions(-) diff --git a/packages/core/src/queries/oidc-model-instance.ts b/packages/core/src/queries/oidc-model-instance.ts index d903ff603..47e91e720 100644 --- a/packages/core/src/queries/oidc-model-instance.ts +++ b/packages/core/src/queries/oidc-model-instance.ts @@ -8,10 +8,10 @@ import { convertToIdentifiers, convertToTimestamp } from '@logto/shared'; import type { Nullable } from '@silverhand/essentials'; import { conditional } from '@silverhand/essentials'; import { addSeconds, isBefore } from 'date-fns'; -import type { ValueExpression } from 'slonik'; +import type { CommonQueryMethods, ValueExpression } from 'slonik'; import { sql } from 'slonik'; -import { buildInsertInto } from '#src/database/insert-into.js'; +import { buildInsertIntoWithPool } from '#src/database/insert-into.js'; import envSet from '#src/env-set/index.js'; export type WithConsumed = T & { consumed?: boolean }; @@ -46,73 +46,99 @@ const withConsumed = ( const convertResult = (result: QueryResult | null, modelName: string) => conditional(result && withConsumed(result.payload, modelName, result.consumedAt)); -export const upsertInstance = buildInsertInto(OidcModelInstances, { - onConflict: { - fields: [fields.modelName, fields.id], - setExcludedFields: [fields.payload, fields.expiresAt], - }, -}); - const findByModel = (modelName: string) => sql` select ${fields.payload}, ${fields.consumedAt} from ${table} where ${fields.modelName}=${modelName} `; -export const findPayloadById = async (modelName: string, id: string) => { - const result = await envSet.pool.maybeOne(sql` - ${findByModel(modelName)} - and ${fields.id}=${id} - `); +export const createOidcModelInstanceQueries = (pool: CommonQueryMethods) => { + const upsertInstance = buildInsertIntoWithPool(pool)( + OidcModelInstances, + { + onConflict: { + fields: [fields.modelName, fields.id], + setExcludedFields: [fields.payload, fields.expiresAt], + }, + } + ); - return convertResult(result, modelName); + const findPayloadById = async (modelName: string, id: string) => { + const result = await envSet.pool.maybeOne(sql` + ${findByModel(modelName)} + and ${fields.id}=${id} + `); + + return convertResult(result, modelName); + }; + + const findPayloadByPayloadField = async < + T extends ValueExpression, + Field extends keyof OidcModelInstancePayload + >( + modelName: string, + field: Field, + value: T + ) => { + const result = await envSet.pool.maybeOne(sql` + ${findByModel(modelName)} + and ${fields.payload}->>${field}=${value} + `); + + return convertResult(result, modelName); + }; + + const consumeInstanceById = async (modelName: string, id: string) => { + await envSet.pool.query(sql` + update ${table} + set ${fields.consumedAt}=${convertToTimestamp()} + where ${fields.modelName}=${modelName} + and ${fields.id}=${id} + `); + }; + + const destroyInstanceById = async (modelName: string, id: string) => { + await envSet.pool.query(sql` + delete from ${table} + where ${fields.modelName}=${modelName} + and ${fields.id}=${id} + `); + }; + + const revokeInstanceByGrantId = async (modelName: string, grantId: string) => { + await envSet.pool.query(sql` + delete from ${table} + where ${fields.modelName}=${modelName} + and ${fields.payload}->>'grantId'=${grantId} + `); + }; + + const revokeInstanceByUserId = async (modelName: string, userId: string) => { + await envSet.pool.query(sql` + delete from ${table} + where ${fields.modelName}=${modelName} + and ${fields.payload}->>'accountId'=${userId} + `); + }; + + return { + upsertInstance, + findPayloadById, + findPayloadByPayloadField, + consumeInstanceById, + destroyInstanceById, + revokeInstanceByGrantId, + revokeInstanceByUserId, + }; }; -export const findPayloadByPayloadField = async < - T extends ValueExpression, - Field extends keyof OidcModelInstancePayload ->( - modelName: string, - field: Field, - value: T -) => { - const result = await envSet.pool.maybeOne(sql` - ${findByModel(modelName)} - and ${fields.payload}->>${field}=${value} - `); - - return convertResult(result, modelName); -}; - -export const consumeInstanceById = async (modelName: string, id: string) => { - await envSet.pool.query(sql` - update ${table} - set ${fields.consumedAt}=${convertToTimestamp()} - where ${fields.modelName}=${modelName} - and ${fields.id}=${id} - `); -}; - -export const destroyInstanceById = async (modelName: string, id: string) => { - await envSet.pool.query(sql` - delete from ${table} - where ${fields.modelName}=${modelName} - and ${fields.id}=${id} - `); -}; - -export const revokeInstanceByGrantId = async (modelName: string, grantId: string) => { - await envSet.pool.query(sql` - delete from ${table} - where ${fields.modelName}=${modelName} - and ${fields.payload}->>'grantId'=${grantId} - `); -}; - -export const revokeInstanceByUserId = async (modelName: string, userId: string) => { - await envSet.pool.query(sql` - delete from ${table} - where ${fields.modelName}=${modelName} - and ${fields.payload}->>'accountId'=${userId} - `); -}; +/** @deprecated Will be removed soon. Use createOidcModelInstanceQueries() factory instead. */ +export const { + upsertInstance, + findPayloadById, + findPayloadByPayloadField, + consumeInstanceById, + destroyInstanceById, + revokeInstanceByGrantId, + revokeInstanceByUserId, +} = createOidcModelInstanceQueries(envSet.pool); diff --git a/packages/core/src/queries/passcode.ts b/packages/core/src/queries/passcode.ts index 763f1efd1..b336a374a 100644 --- a/packages/core/src/queries/passcode.ts +++ b/packages/core/src/queries/passcode.ts @@ -2,69 +2,94 @@ import type { VerificationCodeType } from '@logto/connector-kit'; import type { Passcode, CreatePasscode } from '@logto/schemas'; import { Passcodes } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; +import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; -import { buildInsertInto } from '#src/database/insert-into.js'; +import { buildInsertIntoWithPool } from '#src/database/insert-into.js'; import envSet from '#src/env-set/index.js'; import { DeletionError } from '#src/errors/SlonikError/index.js'; const { table, fields } = convertToIdentifiers(Passcodes); -export const findUnconsumedPasscodeByJtiAndType = async (jti: string, type: VerificationCodeType) => - envSet.pool.maybeOne(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.interactionJti}=${jti} and ${fields.type}=${type} and ${fields.consumed} = false - `); +const createPasscodeQueries = (pool: CommonQueryMethods) => { + const findUnconsumedPasscodeByJtiAndType = async (jti: string, type: VerificationCodeType) => + pool.maybeOne(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: VerificationCodeType -) => - envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.interactionJti}=${jti} and ${fields.type}=${type} and ${fields.consumed} = false - `); + const findUnconsumedPasscodesByJtiAndType = async (jti: string, type: VerificationCodeType) => + pool.any(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(Passcodes, { - returning: true, -}); + const insertPasscode = buildInsertIntoWithPool(pool)(Passcodes, { + returning: true, + }); -export const consumePasscode = async (id: string) => - envSet.pool.query(sql` - update ${table} - set ${fields.consumed}=true - where ${fields.id}=${id} - returning ${sql.join(Object.values(fields), sql`, `)} - `); + const consumePasscode = async (id: string) => + pool.query(sql` + update ${table} + set ${fields.consumed}=true + where ${fields.id}=${id} + returning ${sql.join(Object.values(fields), sql`, `)} + `); -export const increasePasscodeTryCount = async (id: string) => - envSet.pool.query(sql` - update ${table} - set ${fields.tryCount}=${fields.tryCount}+1 - where ${fields.id}=${id} - returning ${sql.join(Object.values(fields), sql`, `)} - `); + const increasePasscodeTryCount = async (id: string) => + pool.query(sql` + update ${table} + set ${fields.tryCount}=${fields.tryCount}+1 + where ${fields.id}=${id} + returning ${sql.join(Object.values(fields), sql`, `)} + `); -export const deletePasscodeById = async (id: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id}=${id} - `); + const deletePasscodeById = async (id: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.id}=${id} + `); - if (rowCount < 1) { - throw new DeletionError(Passcodes.table, id); - } + if (rowCount < 1) { + throw new DeletionError(Passcodes.table, id); + } + }; + + const deletePasscodesByIds = async (ids: string[]) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.id} in (${sql.join(ids, sql`,`)}) + `); + + if (rowCount !== ids.length) { + throw new DeletionError(Passcodes.table, `${ids.join(',')}`); + } + }; + + return { + findUnconsumedPasscodeByJtiAndType, + findUnconsumedPasscodesByJtiAndType, + insertPasscode, + consumePasscode, + increasePasscodeTryCount, + deletePasscodeById, + deletePasscodesByIds, + }; }; -export const deletePasscodesByIds = async (ids: string[]) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id} in (${sql.join(ids, sql`,`)}) - `); - - if (rowCount !== ids.length) { - throw new DeletionError(Passcodes.table, `${ids.join(',')}`); - } -}; +/** @deprecated Will be removed soon. Use createPasscodeQueries() factory instead. */ +export const { + findUnconsumedPasscodeByJtiAndType, + findUnconsumedPasscodesByJtiAndType, + insertPasscode, + consumePasscode, + increasePasscodeTryCount, + deletePasscodeById, + deletePasscodesByIds, +} = createPasscodeQueries(envSet.pool); diff --git a/packages/core/src/queries/resource.ts b/packages/core/src/queries/resource.ts index 71b36e3fa..075ee9e24 100644 --- a/packages/core/src/queries/resource.ts +++ b/packages/core/src/queries/resource.ts @@ -2,66 +2,94 @@ import type { Resource, CreateResource } from '@logto/schemas'; import { Resources } 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(Resources); -export const findTotalNumberOfResources = async () => getTotalRowCount(table); +export const createResourceQueries = (pool: CommonQueryMethods) => { + const findTotalNumberOfResources = async () => getTotalRowCountWithPool(pool)(table); -export const findAllResources = async (limit?: number, offset?: number) => - manyRows( - envSet.pool.query(sql` + const findAllResources = async (limit?: number, offset?: number) => + manyRows( + pool.query(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + ${conditionalSql(limit, (limit) => sql`limit ${limit}`)} + ${conditionalSql(offset, (offset) => sql`offset ${offset}`)} + `) + ); + + const findResourceByIndicator = async (indicator: string) => + pool.maybeOne(sql` select ${sql.join(Object.values(fields), sql`, `)} from ${table} - ${conditionalSql(limit, (limit) => sql`limit ${limit}`)} - ${conditionalSql(offset, (offset) => sql`offset ${offset}`)} - `) - ); + where ${fields.indicator}=${indicator} + `); -export const findResourceByIndicator = async (indicator: string) => - envSet.pool.maybeOne(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.indicator}=${indicator} - `); + const findResourceById = buildFindEntityByIdWithPool(pool)(Resources); -export const findResourcesByIds = async (resourceIds: string[]) => - resourceIds.length > 0 - ? envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id} in (${sql.join(resourceIds, sql`, `)}) - `) - : []; + const findResourcesByIds = async (resourceIds: string[]) => + resourceIds.length > 0 + ? pool.any(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.id} in (${sql.join(resourceIds, sql`, `)}) + `) + : []; -export const findResourceById = buildFindEntityById(Resources); + const insertResource = buildInsertIntoWithPool(pool)(Resources, { + returning: true, + }); -export const insertResource = buildInsertInto(Resources, { - returning: true, -}); + const updateResource = buildUpdateWhereWithPool(pool)(Resources, true); -const updateResource = buildUpdateWhere(Resources, true); + const updateResourceById = async ( + id: string, + set: Partial>, + jsonbMode: 'replace' | 'merge' = 'merge' + ) => updateResource({ set, where: { id }, jsonbMode }); -export const updateResourceById = async ( - id: string, - set: Partial>, - jsonbMode: 'replace' | 'merge' = 'merge' -) => updateResource({ set, where: { id }, jsonbMode }); + const deleteResourceById = async (id: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.id}=${id} + `); -export const deleteResourceById = async (id: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id}=${id} - `); + if (rowCount < 1) { + throw new DeletionError(Resources.table, id); + } + }; - if (rowCount < 1) { - throw new DeletionError(Resources.table, id); - } + return { + findTotalNumberOfResources, + findAllResources, + findResourceByIndicator, + findResourceById, + findResourcesByIds, + insertResource, + updateResource, + updateResourceById, + deleteResourceById, + }; }; + +/** @deprecated Will be removed soon. Use createResourceQueries() factory instead. */ +export const { + findTotalNumberOfResources, + findAllResources, + findResourceByIndicator, + findResourceById, + findResourcesByIds, + insertResource, + updateResource, + updateResourceById, + deleteResourceById, +} = createResourceQueries(envSet.pool); diff --git a/packages/core/src/queries/roles-scopes.ts b/packages/core/src/queries/roles-scopes.ts index 8193a0568..a2ae2b080 100644 --- a/packages/core/src/queries/roles-scopes.ts +++ b/packages/core/src/queries/roles-scopes.ts @@ -1,6 +1,7 @@ import type { RolesScope } from '@logto/schemas'; import { RolesScopes } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; +import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; import envSet from '#src/env-set/index.js'; @@ -8,29 +9,37 @@ import { DeletionError } from '#src/errors/SlonikError/index.js'; const { table, fields } = convertToIdentifiers(RolesScopes); -export const insertRolesScopes = async (rolesScopes: RolesScope[]) => - envSet.pool.query(sql` - insert into ${table} (${fields.scopeId}, ${fields.roleId}) values - ${sql.join( - rolesScopes.map(({ scopeId, roleId }) => sql`(${scopeId}, ${roleId})`), - sql`, ` - )} - `); +export const createRolesScopesQueries = (pool: CommonQueryMethods) => { + const insertRolesScopes = async (rolesScopes: RolesScope[]) => + pool.query(sql` + insert into ${table} (${fields.scopeId}, ${fields.roleId}) values + ${sql.join( + rolesScopes.map(({ scopeId, roleId }) => sql`(${scopeId}, ${roleId})`), + sql`, ` + )} + `); -export const findRolesScopesByRoleId = async (roleId: string) => - envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`,`)} - from ${table} - where ${fields.roleId}=${roleId} - `); + const findRolesScopesByRoleId = async (roleId: string) => + pool.any(sql` + select ${sql.join(Object.values(fields), sql`,`)} + from ${table} + where ${fields.roleId}=${roleId} + `); -export const deleteRolesScope = async (roleId: string, scopeId: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.scopeId} = ${scopeId} and ${fields.roleId} = ${roleId} - `); + const deleteRolesScope = async (roleId: string, scopeId: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.scopeId} = ${scopeId} and ${fields.roleId} = ${roleId} + `); - if (rowCount < 1) { - throw new DeletionError(RolesScopes.table); - } + if (rowCount < 1) { + throw new DeletionError(RolesScopes.table); + } + }; + + return { insertRolesScopes, findRolesScopesByRoleId, deleteRolesScope }; }; + +/** @deprecated Will be removed soon. Use createRolesScopesQueries() factory instead. */ +export const { insertRolesScopes, findRolesScopesByRoleId, deleteRolesScope } = + createRolesScopesQueries(envSet.pool); diff --git a/packages/core/src/queries/roles.ts b/packages/core/src/queries/roles.ts index a9e77fbc6..9f909932d 100644 --- a/packages/core/src/queries/roles.ts +++ b/packages/core/src/queries/roles.ts @@ -2,11 +2,12 @@ import type { CreateRole, Role } from '@logto/schemas'; import { SearchJointMode, Roles } from '@logto/schemas'; import type { OmitAutoSetFields } from '@logto/shared'; import { conditionalSql, convertToIdentifiers } 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 { 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 { buildUpdateWhereWithPool } from '#src/database/update-where.js'; import envSet from '#src/env-set/index.js'; import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { Search } from '#src/utils/search.js'; @@ -26,80 +27,111 @@ const buildRoleConditions = (search: Search) => { export const defaultUserSearch = { matches: [], isCaseSensitive: false, joint: SearchJointMode.Or }; -export const countRoles = async (search: Search = defaultUserSearch) => - envSet.pool.one<{ count: number }>(sql` - select count(*) - from ${table} - ${buildRoleConditions(search)} - `); - -export const findRoles = async (search: Search, limit?: number, offset?: number) => - envSet.pool.any( - sql` - select ${sql.join( - Object.values(fields).map((field) => sql`${table}.${field}`), - sql`,` - )} +export const createRolesQueries = (pool: CommonQueryMethods) => { + const countRoles = async (search: Search = defaultUserSearch) => + pool.one<{ count: number }>(sql` + select count(*) from ${table} ${buildRoleConditions(search)} - ${conditionalSql(limit, (value) => sql`limit ${value}`)} - ${conditionalSql(offset, (value) => sql`offset ${value}`)} - ` - ); + `); -export const findRolesByRoleIds = async (roleIds: string[]) => - roleIds.length > 0 - ? envSet.pool.any(sql` + const findRoles = async (search: Search, limit?: number, offset?: number) => + pool.any( + sql` + select ${sql.join( + Object.values(fields).map((field) => sql`${table}.${field}`), + sql`,` + )} + from ${table} + ${buildRoleConditions(search)} + ${conditionalSql(limit, (value) => sql`limit ${value}`)} + ${conditionalSql(offset, (value) => sql`offset ${value}`)} + ` + ); + + const findRolesByRoleIds = async (roleIds: string[]) => + roleIds.length > 0 + ? pool.any(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.id} in (${sql.join(roleIds, sql`, `)}) + `) + : []; + + const findRolesByRoleNames = async (roleNames: string[]) => + roleNames.length > 0 + ? pool.any(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.name} in (${sql.join(roleNames, sql`, `)}) + `) + : []; + + const findRoleByRoleName = async (roleName: string, excludeRoleId?: string) => + pool.maybeOne(sql` select ${sql.join(Object.values(fields), sql`, `)} from ${table} - where ${fields.id} in (${sql.join(roleIds, sql`, `)}) - `) - : []; + where ${fields.name} = ${roleName} + ${conditionalSql(excludeRoleId, (id) => sql`and ${fields.id}<>${id}`)} + `); -export const findRolesByRoleNames = async (roleNames: string[]) => - roleNames.length > 0 - ? envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.name} in (${sql.join(roleNames, sql`, `)}) - `) - : []; + const insertRoles = async (roles: Role[]) => + pool.query(sql` + insert into ${table} (${fields.id}, ${fields.name}, ${fields.description}) values + ${sql.join( + roles.map(({ id, name, description }) => sql`(${id}, ${name}, ${description})`), + sql`, ` + )} + `); -export const findRoleByRoleName = async (roleName: string, excludeRoleId?: string) => - envSet.pool.maybeOne(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.name} = ${roleName} - ${conditionalSql(excludeRoleId, (id) => sql`and ${fields.id}<>${id}`)} - `); + const insertRole = buildInsertIntoWithPool(pool)(Roles, { + returning: true, + }); -export const insertRoles = async (roles: Role[]) => - envSet.pool.query(sql` - insert into ${table} (${fields.id}, ${fields.name}, ${fields.description}) values - ${sql.join( - roles.map(({ id, name, description }) => sql`(${id}, ${name}, ${description})`), - sql`, ` - )} - `); + const findRoleById = buildFindEntityByIdWithPool(pool)(Roles); -export const insertRole = buildInsertInto(Roles, { - returning: true, -}); + const updateRole = buildUpdateWhereWithPool(pool)(Roles, true); -export const findRoleById = buildFindEntityById(Roles); + const updateRoleById = async (id: string, set: Partial>) => + updateRole({ set, where: { id }, jsonbMode: 'merge' }); -const updateRole = buildUpdateWhere(Roles, true); + const deleteRoleById = async (id: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.id}=${id} + `); -export const updateRoleById = async (id: string, set: Partial>) => - updateRole({ set, where: { id }, jsonbMode: 'merge' }); + if (rowCount < 1) { + throw new DeletionError(Roles.table, id); + } + }; -export const deleteRoleById = async (id: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id}=${id} - `); - - if (rowCount < 1) { - throw new DeletionError(Roles.table, id); - } + return { + countRoles, + findRoles, + findRolesByRoleIds, + findRolesByRoleNames, + findRoleByRoleName, + insertRoles, + insertRole, + findRoleById, + updateRole, + updateRoleById, + deleteRoleById, + }; }; + +/** @deprecated Will be removed soon. Use createRolesQueries() factory instead. */ +export const { + countRoles, + findRoles, + findRolesByRoleIds, + findRolesByRoleNames, + findRoleByRoleName, + insertRoles, + insertRole, + findRoleById, + updateRole, + updateRoleById, + deleteRoleById, +} = createRolesQueries(envSet.pool); diff --git a/packages/core/src/queries/scope.ts b/packages/core/src/queries/scope.ts index 0f257faa4..08f80dada 100644 --- a/packages/core/src/queries/scope.ts +++ b/packages/core/src/queries/scope.ts @@ -2,11 +2,12 @@ import type { CreateScope, Scope } from '@logto/schemas'; import { Scopes } from '@logto/schemas'; import type { OmitAutoSetFields } from '@logto/shared'; import { conditionalSql, convertToIdentifiers } 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 { 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 { buildUpdateWhereWithPool } from '#src/database/update-where.js'; import envSet from '#src/env-set/index.js'; import { DeletionError } from '#src/errors/SlonikError/index.js'; import type { Search } from '#src/utils/search.js'; @@ -14,13 +15,6 @@ import { buildConditionsFromSearch } from '#src/utils/search.js'; const { table, fields } = convertToIdentifiers(Scopes); -export const findScopesByResourceId = async (resourceId: string) => - envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.resourceId}=${resourceId} - `); - const buildResourceConditions = (search: Search) => { const hasSearch = search.matches.length > 0; const searchFields = [Scopes.fields.id, Scopes.fields.name, Scopes.fields.description]; @@ -31,65 +25,96 @@ const buildResourceConditions = (search: Search) => { ); }; -export const findScopes = async ( - resourceId: string, - search: Search, - limit?: number, - offset?: number -) => - envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.resourceId}=${resourceId} - ${buildResourceConditions(search)} - ${conditionalSql(limit, (value) => sql`limit ${value}`)} - ${conditionalSql(offset, (value) => sql`offset ${value}`)} - `); - -export const countScopes = async (resourceId: string, search: Search) => - envSet.pool.one<{ count: number }>(sql` - select count(*) - from ${table} - where ${fields.resourceId}=${resourceId} - ${buildResourceConditions(search)} - `); - -export const findScopesByResourceIds = async (resourceIds: string[]) => - resourceIds.length > 0 - ? envSet.pool.any(sql` +export const createScopeQueries = (pool: CommonQueryMethods) => { + const findScopes = async (resourceId: string, search: Search, limit?: number, offset?: number) => + pool.any(sql` select ${sql.join(Object.values(fields), sql`, `)} from ${table} - where ${fields.resourceId} in (${sql.join(resourceIds, sql`, `)}) - `) - : []; + where ${fields.resourceId}=${resourceId} + ${buildResourceConditions(search)} + ${conditionalSql(limit, (value) => sql`limit ${value}`)} + ${conditionalSql(offset, (value) => sql`offset ${value}`)} + `); -export const findScopesByIds = async (scopeIds: string[]) => - scopeIds.length > 0 - ? envSet.pool.any(sql` + const countScopes = async (resourceId: string, search: Search) => + pool.one<{ count: number }>(sql` + select count(*) + from ${table} + where ${fields.resourceId}=${resourceId} + ${buildResourceConditions(search)} + `); + + const findScopesByResourceId = async (resourceId: string) => + pool.any(sql` select ${sql.join(Object.values(fields), sql`, `)} from ${table} - where ${fields.id} in (${sql.join(scopeIds, sql`, `)}) - `) - : []; + where ${fields.resourceId}=${resourceId} + `); -export const insertScope = buildInsertInto(Scopes, { - returning: true, -}); + const findScopesByResourceIds = async (resourceIds: string[]) => + resourceIds.length > 0 + ? pool.any(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.resourceId} in (${sql.join(resourceIds, sql`, `)}) + `) + : []; -export const findScopeById = buildFindEntityById(Scopes); + const findScopesByIds = async (scopeIds: string[]) => + scopeIds.length > 0 + ? pool.any(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.id} in (${sql.join(scopeIds, sql`, `)}) + `) + : []; -const updateScope = buildUpdateWhere(Scopes, true); + const insertScope = buildInsertIntoWithPool(pool)(Scopes, { + returning: true, + }); -export const updateScopeById = async (id: string, set: Partial>) => - updateScope({ set, where: { id }, jsonbMode: 'merge' }); + const findScopeById = buildFindEntityByIdWithPool(pool)(Scopes); -export const deleteScopeById = async (id: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.id}=${id} - `); + const updateScope = buildUpdateWhereWithPool(pool)(Scopes, true); - if (rowCount < 1) { - throw new DeletionError(Scopes.table, id); - } + const updateScopeById = async (id: string, set: Partial>) => + updateScope({ set, where: { id }, jsonbMode: 'merge' }); + + const deleteScopeById = async (id: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.id}=${id} + `); + + if (rowCount < 1) { + throw new DeletionError(Scopes.table, id); + } + }; + + return { + findScopes, + countScopes, + findScopesByResourceId, + findScopesByResourceIds, + findScopesByIds, + insertScope, + findScopeById, + updateScope, + updateScopeById, + deleteScopeById, + }; }; + +/** @deprecated Will be removed soon. Use createScopeQueries() factory instead. */ +export const { + findScopes, + countScopes, + findScopesByResourceId, + findScopesByResourceIds, + findScopesByIds, + insertScope, + findScopeById, + updateScope, + updateScopeById, + deleteScopeById, +} = createScopeQueries(envSet.pool); diff --git a/packages/core/src/queries/setting.ts b/packages/core/src/queries/setting.ts index 08d4a4ba4..e93daad9a 100644 --- a/packages/core/src/queries/setting.ts +++ b/packages/core/src/queries/setting.ts @@ -1,18 +1,28 @@ import type { Setting, CreateSetting } from '@logto/schemas'; import { Settings } from '@logto/schemas'; import type { OmitAutoSetFields } from '@logto/shared'; +import type { CommonQueryMethods } from 'slonik'; -import { buildFindEntityById } from '#src/database/find-entity-by-id.js'; -import { buildUpdateWhere } from '#src/database/update-where.js'; +import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'; +import { buildUpdateWhereWithPool } from '#src/database/update-where.js'; +import envSet from '#src/env-set/index.js'; export const defaultSettingId = 'default'; -export const getSetting = async () => - buildFindEntityById(Settings)(defaultSettingId); +export const createSettingQueries = (pool: CommonQueryMethods) => { + const getSetting = async () => + buildFindEntityByIdWithPool(pool)(Settings)(defaultSettingId); -export const updateSetting = async (setting: Partial>) => { - return buildUpdateWhere( - Settings, - true - )({ set: setting, where: { id: defaultSettingId }, jsonbMode: 'merge' }); + const updateSetting = async (setting: Partial>) => { + return buildUpdateWhereWithPool(pool)(Settings, true)({ + set: setting, + where: { id: defaultSettingId }, + jsonbMode: 'merge', + }); + }; + + return { getSetting, updateSetting }; }; + +/** @deprecated Will be removed soon. Use createSettingQueries() factory instead. */ +export const { getSetting, updateSetting } = createSettingQueries(envSet.pool); diff --git a/packages/core/src/queries/sign-in-experience.ts b/packages/core/src/queries/sign-in-experience.ts index b57c2d69d..6dc71db23 100644 --- a/packages/core/src/queries/sign-in-experience.ts +++ b/packages/core/src/queries/sign-in-experience.ts @@ -1,18 +1,30 @@ import type { SignInExperience, CreateSignInExperience } from '@logto/schemas'; import { SignInExperiences } from '@logto/schemas'; +import type { CommonQueryMethods } from 'slonik'; -import { buildFindEntityById } from '#src/database/find-entity-by-id.js'; -import { buildUpdateWhere } from '#src/database/update-where.js'; - -const updateSignInExperience = buildUpdateWhere( - SignInExperiences, - true -); +import { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'; +import { buildUpdateWhereWithPool } from '#src/database/update-where.js'; +import envSet from '#src/env-set/index.js'; const id = 'default'; -export const updateDefaultSignInExperience = async (set: Partial) => - updateSignInExperience({ set, where: { id }, jsonbMode: 'replace' }); +export const createSignInExperienceQueries = (pool: CommonQueryMethods) => { + const updateSignInExperience = buildUpdateWhereWithPool(pool)< + CreateSignInExperience, + SignInExperience + >(SignInExperiences, true); -export const findDefaultSignInExperience = async () => - buildFindEntityById(SignInExperiences)(id); + const updateDefaultSignInExperience = async (set: Partial) => + updateSignInExperience({ set, where: { id }, jsonbMode: 'replace' }); + + const findDefaultSignInExperience = async () => + buildFindEntityByIdWithPool(pool)(SignInExperiences)( + id + ); + + return { updateDefaultSignInExperience, findDefaultSignInExperience }; +}; + +/** @deprecated Will be removed soon. Use createSignInExperienceQueries() factory instead. */ +export const { updateDefaultSignInExperience, findDefaultSignInExperience } = + createSignInExperienceQueries(envSet.pool);