diff --git a/packages/core/src/database/find-entity-by-id.ts b/packages/core/src/database/find-entity-by-id.ts new file mode 100644 index 000000000..dec6074c5 --- /dev/null +++ b/packages/core/src/database/find-entity-by-id.ts @@ -0,0 +1,38 @@ +import { SchemaLike, GeneratedSchema } from '@logto/schemas'; +import { sql, NotFoundError } from 'slonik'; + +import { convertToIdentifiers } from '@/database/utils'; +import envSet from '@/env-set'; +import RequestError from '@/errors/RequestError'; +import assertThat from '@/utils/assert-that'; +import { isKeyOf } from '@/utils/schema'; + +export const buildFindEntityById = ( + schema: GeneratedSchema +) => { + const { table, fields } = convertToIdentifiers(schema); + const isKeyOfSchema = isKeyOf(schema); + + // Make sure id is key of the schema + assertThat(isKeyOfSchema('id'), 'entity.not_exists'); + + return async (id: string) => { + try { + return await envSet.pool.one(sql` + select ${sql.join(Object.values(fields), sql`, `)} + from ${table} + where ${fields.id}=${id} + `); + } catch (error: unknown) { + if (error instanceof NotFoundError) { + throw new RequestError({ + code: 'entity.not_exists_with_id', + name: schema.table, + id, + status: 404, + }); + } + throw error; + } + }; +}; diff --git a/packages/core/src/queries/application.ts b/packages/core/src/queries/application.ts index c141717e5..497ccbe18 100644 --- a/packages/core/src/queries/application.ts +++ b/packages/core/src/queries/application.ts @@ -1,6 +1,7 @@ import { Application, CreateApplication, Applications } from '@logto/schemas'; import { sql } from 'slonik'; +import { buildFindEntityById } from '@/database/find-entity-by-id'; import { buildInsertInto } from '@/database/insert-into'; import { getTotalRowCount } from '@/database/row-count'; import { buildUpdateWhere } from '@/database/update-where'; @@ -28,12 +29,9 @@ export const findAllApplications = async (limit: number, offset: number) => `) ); -export const findApplicationById = async (id: string) => - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id}=${id} - `); +export const findApplicationById = buildFindEntityById( + Applications +); export const insertApplication = buildInsertInto(Applications, { returning: true, diff --git a/packages/core/src/queries/log.ts b/packages/core/src/queries/log.ts index e6eacb700..d96c62c5b 100644 --- a/packages/core/src/queries/log.ts +++ b/packages/core/src/queries/log.ts @@ -1,6 +1,7 @@ import { CreateLog, Log, Logs, LogType } from '@logto/schemas'; import { sql } from 'slonik'; +import { buildFindEntityById } from '@/database/find-entity-by-id'; import { buildInsertInto } from '@/database/insert-into'; import { conditionalSql, convertToIdentifiers } from '@/database/utils'; import envSet from '@/env-set'; @@ -46,12 +47,7 @@ export const findLogs = async (limit: number, offset: number, logCondition: LogC offset ${offset} `); -export const findLogById = async (id: string) => - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id}=${id} - `); +export const findLogById = buildFindEntityById(Logs); const registerLogTypes: LogType[] = [ 'RegisterUsernamePassword', diff --git a/packages/core/src/queries/resource.ts b/packages/core/src/queries/resource.ts index 4eb0195d8..2ea998a0c 100644 --- a/packages/core/src/queries/resource.ts +++ b/packages/core/src/queries/resource.ts @@ -1,6 +1,7 @@ import { Resource, CreateResource, Resources } from '@logto/schemas'; import { sql } from 'slonik'; +import { buildFindEntityById } from '@/database/find-entity-by-id'; import { buildInsertInto } from '@/database/insert-into'; import { getTotalRowCount } from '@/database/row-count'; import { buildUpdateWhere } from '@/database/update-where'; @@ -34,12 +35,7 @@ export const findResourceByIndicator = async (indicator: string) => where ${fields.indicator}=${indicator} `); -export const findResourceById = async (id: string) => - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id}=${id} - `); +export const findResourceById = buildFindEntityById(Resources); export const insertResource = buildInsertInto(Resources, { returning: true, diff --git a/packages/core/src/queries/setting.ts b/packages/core/src/queries/setting.ts index fc17bcd39..c7b7474b7 100644 --- a/packages/core/src/queries/setting.ts +++ b/packages/core/src/queries/setting.ts @@ -1,20 +1,13 @@ import { Setting, CreateSetting, Settings } from '@logto/schemas'; -import { sql } from 'slonik'; +import { buildFindEntityById } from '@/database/find-entity-by-id'; import { buildUpdateWhere } from '@/database/update-where'; -import { convertToIdentifiers, OmitAutoSetFields } from '@/database/utils'; -import envSet from '@/env-set'; +import { OmitAutoSetFields } from '@/database/utils'; export const defaultSettingId = 'default'; -const { table, fields } = convertToIdentifiers(Settings); - export const getSetting = async () => - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id}=${defaultSettingId} - `); + buildFindEntityById(Settings)(defaultSettingId); export const updateSetting = async (setting: Partial>) => { return buildUpdateWhere( diff --git a/packages/core/src/queries/sign-in-experience.test.ts b/packages/core/src/queries/sign-in-experience.test.ts index 2be2a7ab9..5573c704c 100644 --- a/packages/core/src/queries/sign-in-experience.test.ts +++ b/packages/core/src/queries/sign-in-experience.test.ts @@ -34,7 +34,7 @@ describe('sign-in-experience query', () => { const expectSql = ` select "id", "color", "branding", "language_info", "terms_of_use", "sign_in_methods", "social_sign_in_connector_targets", "sign_in_mode" from "sign_in_experiences" - where "id" = $1 + where "id"=$1 `; /* eslint-enable sql/no-unsafe-query */ diff --git a/packages/core/src/queries/sign-in-experience.ts b/packages/core/src/queries/sign-in-experience.ts index 792c1733c..6650fd7dd 100644 --- a/packages/core/src/queries/sign-in-experience.ts +++ b/packages/core/src/queries/sign-in-experience.ts @@ -1,11 +1,7 @@ import { SignInExperience, CreateSignInExperience, SignInExperiences } from '@logto/schemas'; -import { sql } from 'slonik'; +import { buildFindEntityById } from '@/database/find-entity-by-id'; import { buildUpdateWhere } from '@/database/update-where'; -import { convertToIdentifiers } from '@/database/utils'; -import envSet from '@/env-set'; - -const { table, fields } = convertToIdentifiers(SignInExperiences); const updateSignInExperience = buildUpdateWhere( SignInExperiences, @@ -18,8 +14,4 @@ export const updateDefaultSignInExperience = async (set: Partial - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.id} = ${id} - `); + buildFindEntityById(SignInExperiences)(id);