0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-24 22:05:56 -05:00
logto/packages/core/src/database/insert-into.ts

88 lines
2.8 KiB
TypeScript
Raw Normal View History

2022-10-21 13:14:17 +08:00
import type { GeneratedSchema, SchemaLike } from '@logto/schemas';
import type { OmitAutoSetFields } from '@logto/shared';
import {
convertToIdentifiers,
excludeAutoSetFields,
convertToPrimitiveOrSql,
conditionalSql,
} from '@logto/shared';
import { has } from '@silverhand/essentials';
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
const setExcluded = (...fields: IdentifierSqlToken[]) =>
2021-08-18 00:24:00 +08:00
sql.join(
fields.map((field) => sql`${field}=excluded.${field}`),
sql`, `
);
type OnConflict = {
fields: IdentifierSqlToken[];
setExcludedFields: IdentifierSqlToken[];
2021-08-18 00:24:00 +08:00
};
type InsertIntoConfigReturning = {
returning: true;
onConflict?: OnConflict;
};
type InsertIntoConfig = {
returning?: false;
onConflict?: OnConflict;
};
type BuildInsertInto = {
<CreateSchema extends SchemaLike, Schema extends CreateSchema>(
{ fieldKeys, ...rest }: GeneratedSchema<CreateSchema, Schema>,
2021-08-18 00:24:00 +08:00
config: InsertIntoConfigReturning
): (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
): (data: OmitAutoSetFields<CreateSchema>) => Promise<void>;
};
2021-08-18 00:24:00 +08:00
export const buildInsertIntoWithPool =
(pool: CommonQueryMethods): BuildInsertInto =>
<CreateSchema extends SchemaLike, Schema extends CreateSchema>(
schema: GeneratedSchema<CreateSchema, Schema>,
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
return async (data: OmitAutoSetFields<CreateSchema>): Promise<Schema | void> => {
const insertingKeys = keys.filter((key) => has(data, key));
const {
rows: [entry],
} = await pool.query<Schema>(sql`
insert into ${table} (${sql.join(
insertingKeys.map((key) => fields[key]),
sql`, `
)})
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
assertThat(!returning || entry, new InsertionError<CreateSchema, Schema>(schema, data));
2022-01-27 19:26:34 +08:00
return entry;
};
2021-08-18 00:24:00 +08:00
};