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

90 lines
2.5 KiB
TypeScript
Raw Normal View History

2021-08-18 00:24:00 +08:00
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
import { has } from '@silverhand/essentials';
import { IdentifierSqlTokenType, sql } from 'slonik';
2021-08-30 11:30:54 +08:00
import envSet from '@/env-set';
import { InsertionError } from '@/errors/SlonikError';
import assertThat from '@/utils/assert-that';
2021-08-30 11:30:54 +08:00
2021-08-18 00:24:00 +08:00
import {
conditionalSql,
convertToIdentifiers,
convertToPrimitiveOrSql,
2021-08-18 00:24:00 +08:00
excludeAutoSetFields,
OmitAutoSetFields,
} from './utils';
const setExcluded = (...fields: IdentifierSqlTokenType[]) =>
sql.join(
fields.map((field) => sql`${field}=excluded.${field}`),
sql`, `
);
type OnConflict = {
fields: IdentifierSqlTokenType[];
setExcludedFields: IdentifierSqlTokenType[];
};
type InsertIntoConfigReturning = {
returning: true;
onConflict?: OnConflict;
};
type InsertIntoConfig = {
returning?: false;
onConflict?: OnConflict;
};
interface BuildInsertInto {
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
2021-08-18 00:24:00 +08:00
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
config: InsertIntoConfigReturning
): (data: OmitAutoSetFields<Schema>) => Promise<ReturnType>;
2021-08-27 00:33:13 +08:00
<Schema extends SchemaLike>(
2021-08-18 00:24:00 +08:00
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
config?: InsertIntoConfig
): (data: OmitAutoSetFields<Schema>) => Promise<void>;
}
export const buildInsertInto: BuildInsertInto = <
Schema extends SchemaLike,
ReturnType extends SchemaLike
>(
schema: GeneratedSchema<Schema>,
2021-08-18 00:24:00 +08:00
config?: InsertIntoConfig | InsertIntoConfigReturning
) => {
const { fieldKeys, ...rest } = schema;
2021-08-18 00:24:00 +08:00
const { table, fields } = convertToIdentifiers(rest);
const keys = excludeAutoSetFields(fieldKeys);
const returning = Boolean(config?.returning);
const onConflict = config?.onConflict;
return async (data: OmitAutoSetFields<Schema>): Promise<ReturnType | void> => {
const insertingKeys = keys.filter((key) => has(data, key));
2021-08-27 00:33:13 +08:00
const {
rows: [entry],
} = await envSet.pool.query<ReturnType>(sql`
insert into ${table} (${sql.join(
insertingKeys.map((key) => fields[key]),
2021-08-18 00:24:00 +08:00
sql`, `
)})
values (${sql.join(
insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)),
sql`, `
)})
${conditionalSql(returning, () => sql`returning *`)}
${conditionalSql(
onConflict,
({ fields, setExcludedFields }) => sql`
on conflict (${sql.join(fields, sql`, `)}) do update
set ${setExcluded(...setExcludedFields)}
`
)}
2021-08-18 00:24:00 +08:00
`);
assertThat(!returning || entry, new InsertionError(schema, data));
2022-01-27 19:26:34 +08:00
2021-08-18 00:24:00 +08:00
return entry;
};
};