2022-10-21 13:14:17 +08:00
|
|
|
import type { GeneratedSchema, SchemaLike } from '@logto/schemas';
|
|
|
|
import type { OmitAutoSetFields } from '@logto/shared';
|
2022-10-10 00:40:04 +08:00
|
|
|
import {
|
|
|
|
convertToIdentifiers,
|
|
|
|
excludeAutoSetFields,
|
|
|
|
convertToPrimitiveOrSql,
|
|
|
|
conditionalSql,
|
|
|
|
} from '@logto/shared';
|
2022-01-25 13:34:20 +08:00
|
|
|
import { has } from '@silverhand/essentials';
|
2023-01-09 15:29:01 +08:00
|
|
|
import type { CommonQueryMethods, IdentifierSqlToken } from 'slonik';
|
2022-10-21 13:14:17 +08:00
|
|
|
import { sql } from 'slonik';
|
2021-08-30 11:30:54 +08:00
|
|
|
|
2022-11-21 16:38:24 +08:00
|
|
|
import { InsertionError } from '#src/errors/SlonikError/index.js';
|
|
|
|
import assertThat from '#src/utils/assert-that.js';
|
2021-08-30 11:30:54 +08:00
|
|
|
|
2022-04-24 10:34:18 +00:00
|
|
|
const setExcluded = (...fields: IdentifierSqlToken[]) =>
|
2021-08-18 00:24:00 +08:00
|
|
|
sql.join(
|
|
|
|
fields.map((field) => sql`${field}=excluded.${field}`),
|
|
|
|
sql`, `
|
|
|
|
);
|
|
|
|
|
|
|
|
type OnConflict = {
|
2022-04-24 10:34:18 +00:00
|
|
|
fields: IdentifierSqlToken[];
|
|
|
|
setExcludedFields: IdentifierSqlToken[];
|
2021-08-18 00:24:00 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
type InsertIntoConfigReturning = {
|
|
|
|
returning: true;
|
|
|
|
onConflict?: OnConflict;
|
|
|
|
};
|
|
|
|
|
|
|
|
type InsertIntoConfig = {
|
|
|
|
returning?: false;
|
|
|
|
onConflict?: OnConflict;
|
|
|
|
};
|
|
|
|
|
2022-08-05 13:58:31 +08:00
|
|
|
type BuildInsertInto = {
|
2023-03-14 12:45:13 +08:00
|
|
|
<CreateSchema extends SchemaLike, Schema extends CreateSchema>(
|
|
|
|
{ fieldKeys, ...rest }: GeneratedSchema<CreateSchema, Schema>,
|
2021-08-18 00:24:00 +08:00
|
|
|
config: InsertIntoConfigReturning
|
2023-03-14 12:45:13 +08:00
|
|
|
): (data: OmitAutoSetFields<CreateSchema>) => Promise<Schema>;
|
|
|
|
<CreateSchema extends SchemaLike, Schema extends CreateSchema>(
|
|
|
|
{ fieldKeys, ...rest }: GeneratedSchema<CreateSchema, Schema>,
|
2021-08-18 00:24:00 +08:00
|
|
|
config?: InsertIntoConfig
|
2023-03-14 12:45:13 +08:00
|
|
|
): (data: OmitAutoSetFields<CreateSchema>) => Promise<void>;
|
2022-08-05 13:58:31 +08:00
|
|
|
};
|
2021-08-18 00:24:00 +08:00
|
|
|
|
2023-01-09 15:29:01 +08:00
|
|
|
export const buildInsertIntoWithPool =
|
|
|
|
(pool: CommonQueryMethods): BuildInsertInto =>
|
2023-03-14 12:45:13 +08:00
|
|
|
<CreateSchema extends SchemaLike, Schema extends CreateSchema>(
|
|
|
|
schema: GeneratedSchema<CreateSchema, Schema>,
|
2023-01-09 15:29:01 +08:00
|
|
|
config?: InsertIntoConfig | InsertIntoConfigReturning
|
|
|
|
) => {
|
|
|
|
const { fieldKeys, ...rest } = schema;
|
|
|
|
const { table, fields } = convertToIdentifiers(rest);
|
|
|
|
const keys = excludeAutoSetFields(fieldKeys);
|
|
|
|
const returning = Boolean(config?.returning);
|
|
|
|
const onConflict = config?.onConflict;
|
2021-08-18 00:24:00 +08:00
|
|
|
|
2023-03-14 12:45:13 +08:00
|
|
|
return async (data: OmitAutoSetFields<CreateSchema>): Promise<Schema | void> => {
|
2023-01-09 15:29:01 +08:00
|
|
|
const insertingKeys = keys.filter((key) => has(data, key));
|
|
|
|
const {
|
|
|
|
rows: [entry],
|
2023-03-14 12:45:13 +08:00
|
|
|
} = await pool.query<Schema>(sql`
|
2023-01-09 15:29:01 +08:00
|
|
|
insert into ${table} (${sql.join(
|
|
|
|
insertingKeys.map((key) => fields[key]),
|
2021-12-20 14:20:23 +08:00
|
|
|
sql`, `
|
|
|
|
)})
|
2023-01-09 15:29:01 +08:00
|
|
|
values (${sql.join(
|
|
|
|
insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)),
|
|
|
|
sql`, `
|
|
|
|
)})
|
|
|
|
${conditionalSql(
|
|
|
|
onConflict,
|
|
|
|
({ fields, setExcludedFields }) => sql`
|
|
|
|
on conflict (${sql.join(fields, sql`, `)}) do update
|
|
|
|
set ${setExcluded(...setExcludedFields)}
|
|
|
|
`
|
|
|
|
)}
|
|
|
|
${conditionalSql(returning, () => sql`returning *`)}
|
|
|
|
`);
|
2021-08-18 00:24:00 +08:00
|
|
|
|
2023-03-14 12:45:13 +08:00
|
|
|
assertThat(!returning || entry, new InsertionError<CreateSchema, Schema>(schema, data));
|
2022-01-27 19:26:34 +08:00
|
|
|
|
2023-01-09 15:29:01 +08:00
|
|
|
return entry;
|
|
|
|
};
|
2021-08-18 00:24:00 +08:00
|
|
|
};
|