diff --git a/packages/core/src/queries/custom-phrase.ts b/packages/core/src/queries/custom-phrase.ts index 2abb510cd..9eb30af1c 100644 --- a/packages/core/src/queries/custom-phrase.ts +++ b/packages/core/src/queries/custom-phrase.ts @@ -1,57 +1,80 @@ import type { CreateCustomPhrase, CustomPhrase } from '@logto/schemas'; import { CustomPhrases } from '@logto/schemas'; import { convertToIdentifiers, manyRows } 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(CustomPhrases); -export const findAllCustomLanguageTags = async () => { - const rows = await manyRows<{ languageTag: string }>( - envSet.pool.query(sql` - select ${fields.languageTag} +export const createCustomPhraseQueries = (pool: CommonQueryMethods) => { + const findAllCustomLanguageTags = async () => { + const rows = await manyRows<{ languageTag: string }>( + pool.query(sql` + select ${fields.languageTag} + from ${table} + order by ${fields.languageTag} + `) + ); + + return rows.map((row) => row.languageTag); + }; + + const findAllCustomPhrases = async () => + manyRows( + pool.query(sql` + select ${sql.join(Object.values(fields), sql`,`)} + from ${table} + order by ${fields.languageTag} + `) + ); + + const findCustomPhraseByLanguageTag = async (languageTag: string): Promise => + pool.one(sql` + select ${sql.join(Object.values(fields), sql`, `)} from ${table} - order by ${fields.languageTag} - `) + where ${fields.languageTag} = ${languageTag} + `); + + const upsertCustomPhrase = buildInsertIntoWithPool(pool)( + CustomPhrases, + { + returning: true, + onConflict: { + fields: [fields.languageTag], + setExcludedFields: [fields.translation], + }, + } ); - return rows.map((row) => row.languageTag); + const deleteCustomPhraseByLanguageTag = async (languageTag: string) => { + const { rowCount } = await pool.query(sql` + delete from ${table} + where ${fields.languageTag}=${languageTag} + `); + + if (rowCount < 1) { + throw new DeletionError(CustomPhrases.table, languageTag); + } + }; + + return { + findAllCustomLanguageTags, + findAllCustomPhrases, + findCustomPhraseByLanguageTag, + upsertCustomPhrase, + deleteCustomPhraseByLanguageTag, + }; }; -export const findAllCustomPhrases = async () => - manyRows( - envSet.pool.query(sql` - select ${sql.join(Object.values(fields), sql`,`)} - from ${table} - order by ${fields.languageTag} - `) - ); - -export const findCustomPhraseByLanguageTag = async (languageTag: string): Promise => - envSet.pool.one(sql` - select ${sql.join(Object.values(fields), sql`, `)} - from ${table} - where ${fields.languageTag} = ${languageTag} - `); - -export const upsertCustomPhrase = buildInsertInto(CustomPhrases, { - returning: true, - onConflict: { - fields: [fields.languageTag], - setExcludedFields: [fields.translation], - }, -}); - -export const deleteCustomPhraseByLanguageTag = async (languageTag: string) => { - const { rowCount } = await envSet.pool.query(sql` - delete from ${table} - where ${fields.languageTag}=${languageTag} - `); - - if (rowCount < 1) { - throw new DeletionError(CustomPhrases.table, languageTag); - } -}; +/** @deprecated Will be removed soon. Use createCustomPhraseQueries() factory instead. */ +export const { + findAllCustomLanguageTags, + findAllCustomPhrases, + findCustomPhraseByLanguageTag, + upsertCustomPhrase, + deleteCustomPhraseByLanguageTag, +} = createCustomPhraseQueries(envSet.pool); diff --git a/packages/core/src/queries/log.ts b/packages/core/src/queries/log.ts index 70d0e3693..d5b83e262 100644 --- a/packages/core/src/queries/log.ts +++ b/packages/core/src/queries/log.ts @@ -1,16 +1,15 @@ import type { CreateLog, Log } from '@logto/schemas'; import { token, Logs } from '@logto/schemas'; 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 { buildFindEntityByIdWithPool } from '#src/database/find-entity-by-id.js'; +import { buildInsertIntoWithPool } from '#src/database/insert-into.js'; import envSet from '#src/env-set/index.js'; const { table, fields } = convertToIdentifiers(Logs); -export const insertLog = buildInsertInto(Logs); - export type LogCondition = { logKey?: string; applicationId?: string; @@ -31,48 +30,71 @@ const buildLogConditionSql = (logCondition: LogCondition) => return subConditions.length > 0 ? sql`where ${sql.join(subConditions, sql` and `)}` : sql``; }); -export const countLogs = async (condition: LogCondition) => - envSet.pool.one<{ count: number }>(sql` - select count(*) - from ${table} - ${buildLogConditionSql(condition)} - `); +export const createLogQueries = (pool: CommonQueryMethods) => { + const insertLog = buildInsertIntoWithPool(pool)(Logs); -export const findLogs = async (limit: number, offset: number, logCondition: LogCondition) => - envSet.pool.any(sql` - select ${sql.join(Object.values(fields), sql`,`)} - from ${table} - ${buildLogConditionSql(logCondition)} - order by ${fields.createdAt} desc - limit ${limit} - offset ${offset} - `); + const countLogs = async (condition: LogCondition) => + pool.one<{ count: number }>(sql` + select count(*) + from ${table} + ${buildLogConditionSql(condition)} + `); -export const findLogById = buildFindEntityById(Logs); + const findLogs = async (limit: number, offset: number, logCondition: LogCondition) => + pool.any(sql` + select ${sql.join(Object.values(fields), sql`,`)} + from ${table} + ${buildLogConditionSql(logCondition)} + order by ${fields.createdAt} desc + limit ${limit} + offset ${offset} + `); -export const getDailyActiveUserCountsByTimeInterval = async ( - startTimeExclusive: number, - endTimeInclusive: number -) => - envSet.pool.any<{ date: string; count: number }>(sql` - select date(${fields.createdAt}), count(distinct(${fields.payload}->>'userId')) - from ${table} - where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000) - and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000) - and ${fields.key} like ${`${token.Type.ExchangeTokenBy}.%`} - and ${fields.payload}->>'result' = 'Success' - group by date(${fields.createdAt}) - `); + const findLogById = buildFindEntityByIdWithPool(pool)(Logs); -export const countActiveUsersByTimeInterval = async ( - startTimeExclusive: number, - endTimeInclusive: number -) => - envSet.pool.one<{ count: number }>(sql` - select count(distinct(${fields.payload}->>'userId')) - from ${table} - where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000) - and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000) - and ${fields.key} like ${`${token.Type.ExchangeTokenBy}.%`} - and ${fields.payload}->>'result' = 'Success' - `); + const getDailyActiveUserCountsByTimeInterval = async ( + startTimeExclusive: number, + endTimeInclusive: number + ) => + pool.any<{ date: string; count: number }>(sql` + select date(${fields.createdAt}), count(distinct(${fields.payload}->>'userId')) + from ${table} + where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000) + and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000) + and ${fields.key} like ${`${token.Type.ExchangeTokenBy}.%`} + and ${fields.payload}->>'result' = 'Success' + group by date(${fields.createdAt}) + `); + + const countActiveUsersByTimeInterval = async ( + startTimeExclusive: number, + endTimeInclusive: number + ) => + pool.one<{ count: number }>(sql` + select count(distinct(${fields.payload}->>'userId')) + from ${table} + where ${fields.createdAt} > to_timestamp(${startTimeExclusive}::double precision / 1000) + and ${fields.createdAt} <= to_timestamp(${endTimeInclusive}::double precision / 1000) + and ${fields.key} like ${`${token.Type.ExchangeTokenBy}.%`} + and ${fields.payload}->>'result' = 'Success' + `); + + return { + insertLog, + countLogs, + findLogs, + findLogById, + getDailyActiveUserCountsByTimeInterval, + countActiveUsersByTimeInterval, + }; +}; + +/** @deprecated Will be removed soon. Use createLogQueries() factory instead. */ +export const { + insertLog, + countLogs, + findLogs, + findLogById, + getDailyActiveUserCountsByTimeInterval, + countActiveUsersByTimeInterval, +} = createLogQueries(envSet.pool);