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:
parent
be57e7e1af
commit
4b21b7d16e
8 changed files with 493 additions and 326 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue