0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-03 22:15:32 -05:00

refactor(core): multiple query factories (#2857)

This commit is contained in:
Gao Sun 2023-01-09 16:32:35 +08:00 committed by GitHub
parent be57e7e1af
commit 4b21b7d16e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 493 additions and 326 deletions

View file

@ -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> = T & { consumed?: boolean };
@ -46,73 +46,99 @@ const withConsumed = <T>(
const convertResult = (result: QueryResult | null, modelName: string) =>
conditional(result && withConsumed(result.payload, modelName, result.consumedAt));
export const upsertInstance = buildInsertInto<CreateOidcModelInstance>(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<QueryResult>(sql`
${findByModel(modelName)}
and ${fields.id}=${id}
`);
export const createOidcModelInstanceQueries = (pool: CommonQueryMethods) => {
const upsertInstance = buildInsertIntoWithPool(pool)<CreateOidcModelInstance>(
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<QueryResult>(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<QueryResult>(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<QueryResult>(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);

View file

@ -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<Passcode>(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<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: VerificationCodeType
) =>
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
`);
const findUnconsumedPasscodesByJtiAndType = async (jti: string, type: VerificationCodeType) =>
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>(Passcodes, {
returning: true,
});
const insertPasscode = buildInsertIntoWithPool(pool)<CreatePasscode, Passcode>(Passcodes, {
returning: true,
});
export const consumePasscode = async (id: string) =>
envSet.pool.query<Passcode>(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<Passcode>(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<Passcode>(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<Passcode>(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);

View file

@ -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<Resource>(sql`
const findAllResources = async (limit?: number, offset?: number) =>
manyRows(
pool.query<Resource>(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<Resource>(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<Resource>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.indicator}=${indicator}
`);
const findResourceById = buildFindEntityByIdWithPool(pool)<CreateResource, Resource>(Resources);
export const findResourcesByIds = async (resourceIds: string[]) =>
resourceIds.length > 0
? envSet.pool.any<Resource>(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<Resource>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.id} in (${sql.join(resourceIds, sql`, `)})
`)
: [];
export const findResourceById = buildFindEntityById<CreateResource, Resource>(Resources);
const insertResource = buildInsertIntoWithPool(pool)<CreateResource, Resource>(Resources, {
returning: true,
});
export const insertResource = buildInsertInto<CreateResource, Resource>(Resources, {
returning: true,
});
const updateResource = buildUpdateWhereWithPool(pool)<CreateResource, Resource>(Resources, true);
const updateResource = buildUpdateWhere<CreateResource, Resource>(Resources, true);
const updateResourceById = async (
id: string,
set: Partial<OmitAutoSetFields<CreateResource>>,
jsonbMode: 'replace' | 'merge' = 'merge'
) => updateResource({ set, where: { id }, jsonbMode });
export const updateResourceById = async (
id: string,
set: Partial<OmitAutoSetFields<CreateResource>>,
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);

View file

@ -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<RolesScope>(sql`
select ${sql.join(Object.values(fields), sql`,`)}
from ${table}
where ${fields.roleId}=${roleId}
`);
const findRolesScopesByRoleId = async (roleId: string) =>
pool.any<RolesScope>(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);

View file

@ -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<Role>(
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<Role>(sql`
const findRoles = async (search: Search, limit?: number, offset?: number) =>
pool.any<Role>(
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<Role>(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<Role>(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<Role>(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<Role>(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<Role>(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)<CreateRole, Role>(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)<CreateRole, Role>(Roles);
export const insertRole = buildInsertInto<CreateRole, Role>(Roles, {
returning: true,
});
const updateRole = buildUpdateWhereWithPool(pool)<CreateRole, Role>(Roles, true);
export const findRoleById = buildFindEntityById<CreateRole, Role>(Roles);
const updateRoleById = async (id: string, set: Partial<OmitAutoSetFields<CreateRole>>) =>
updateRole({ set, where: { id }, jsonbMode: 'merge' });
const updateRole = buildUpdateWhere<CreateRole, Role>(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<OmitAutoSetFields<CreateRole>>) =>
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);

View file

@ -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<Scope>(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<Scope>(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<Scope>(sql`
export const createScopeQueries = (pool: CommonQueryMethods) => {
const findScopes = async (resourceId: string, search: Search, limit?: number, offset?: number) =>
pool.any<Scope>(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<Scope>(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<Scope>(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<CreateScope, Scope>(Scopes, {
returning: true,
});
const findScopesByResourceIds = async (resourceIds: string[]) =>
resourceIds.length > 0
? pool.any<Scope>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.resourceId} in (${sql.join(resourceIds, sql`, `)})
`)
: [];
export const findScopeById = buildFindEntityById<CreateScope, Scope>(Scopes);
const findScopesByIds = async (scopeIds: string[]) =>
scopeIds.length > 0
? pool.any<Scope>(sql`
select ${sql.join(Object.values(fields), sql`, `)}
from ${table}
where ${fields.id} in (${sql.join(scopeIds, sql`, `)})
`)
: [];
const updateScope = buildUpdateWhere<CreateScope, Scope>(Scopes, true);
const insertScope = buildInsertIntoWithPool(pool)<CreateScope, Scope>(Scopes, {
returning: true,
});
export const updateScopeById = async (id: string, set: Partial<OmitAutoSetFields<CreateScope>>) =>
updateScope({ set, where: { id }, jsonbMode: 'merge' });
const findScopeById = buildFindEntityByIdWithPool(pool)<CreateScope, Scope>(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)<CreateScope, Scope>(Scopes, true);
if (rowCount < 1) {
throw new DeletionError(Scopes.table, id);
}
const updateScopeById = async (id: string, set: Partial<OmitAutoSetFields<CreateScope>>) =>
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);

View file

@ -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<CreateSetting, Setting>(Settings)(defaultSettingId);
export const createSettingQueries = (pool: CommonQueryMethods) => {
const getSetting = async () =>
buildFindEntityByIdWithPool(pool)<CreateSetting, Setting>(Settings)(defaultSettingId);
export const updateSetting = async (setting: Partial<OmitAutoSetFields<CreateSetting>>) => {
return buildUpdateWhere<CreateSetting, Setting>(
Settings,
true
)({ set: setting, where: { id: defaultSettingId }, jsonbMode: 'merge' });
const updateSetting = async (setting: Partial<OmitAutoSetFields<CreateSetting>>) => {
return buildUpdateWhereWithPool(pool)<CreateSetting, Setting>(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);

View file

@ -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<CreateSignInExperience, SignInExperience>(
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<CreateSignInExperience>) =>
updateSignInExperience({ set, where: { id }, jsonbMode: 'replace' });
export const createSignInExperienceQueries = (pool: CommonQueryMethods) => {
const updateSignInExperience = buildUpdateWhereWithPool(pool)<
CreateSignInExperience,
SignInExperience
>(SignInExperiences, true);
export const findDefaultSignInExperience = async () =>
buildFindEntityById<CreateSignInExperience, SignInExperience>(SignInExperiences)(id);
const updateDefaultSignInExperience = async (set: Partial<CreateSignInExperience>) =>
updateSignInExperience({ set, where: { id }, jsonbMode: 'replace' });
const findDefaultSignInExperience = async () =>
buildFindEntityByIdWithPool(pool)<CreateSignInExperience, SignInExperience>(SignInExperiences)(
id
);
return { updateDefaultSignInExperience, findDefaultSignInExperience };
};
/** @deprecated Will be removed soon. Use createSignInExperienceQueries() factory instead. */
export const { updateDefaultSignInExperience, findDefaultSignInExperience } =
createSignInExperienceQueries(envSet.pool);