2021-08-18 00:24:00 +08:00
|
|
|
import { SchemaLike, GeneratedSchema } from '@logto/schemas';
|
2022-01-25 13:34:20 +08:00
|
|
|
import { has } from '@silverhand/essentials';
|
2021-08-18 00:24:00 +08:00
|
|
|
import { DatabasePoolType, IdentifierSqlTokenType, sql } from 'slonik';
|
2021-08-30 11:30:54 +08:00
|
|
|
|
2021-09-16 23:48:06 +08:00
|
|
|
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,
|
2021-08-26 13:05:23 +08:00
|
|
|
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 {
|
2021-12-08 11:11:27 +08:00
|
|
|
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
|
2021-08-18 00:24:00 +08:00
|
|
|
pool: DatabasePoolType,
|
|
|
|
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
|
|
|
|
config: InsertIntoConfigReturning
|
2021-12-08 11:11:27 +08:00
|
|
|
): (data: OmitAutoSetFields<Schema>) => Promise<ReturnType>;
|
2021-08-27 00:33:13 +08:00
|
|
|
<Schema extends SchemaLike>(
|
2021-08-18 00:24:00 +08:00
|
|
|
pool: DatabasePoolType,
|
|
|
|
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
|
|
|
|
config?: InsertIntoConfig
|
|
|
|
): (data: OmitAutoSetFields<Schema>) => Promise<void>;
|
|
|
|
}
|
|
|
|
|
2021-12-08 11:11:27 +08:00
|
|
|
export const buildInsertInto: BuildInsertInto = <
|
|
|
|
Schema extends SchemaLike,
|
|
|
|
ReturnType extends SchemaLike
|
|
|
|
>(
|
2021-08-18 00:24:00 +08:00
|
|
|
pool: DatabasePoolType,
|
|
|
|
{ fieldKeys, ...rest }: GeneratedSchema<Schema>,
|
|
|
|
config?: InsertIntoConfig | InsertIntoConfigReturning
|
|
|
|
) => {
|
|
|
|
const { table, fields } = convertToIdentifiers(rest);
|
|
|
|
const keys = excludeAutoSetFields(fieldKeys);
|
|
|
|
const returning = Boolean(config?.returning);
|
|
|
|
const onConflict = config?.onConflict;
|
|
|
|
|
2021-12-08 11:11:27 +08:00
|
|
|
return async (data: OmitAutoSetFields<Schema>): Promise<ReturnType | void> => {
|
2022-01-25 13:34:20 +08:00
|
|
|
const insertingKeys = keys.filter((key) => has(data, key));
|
2021-08-27 00:33:13 +08:00
|
|
|
const {
|
|
|
|
rows: [entry],
|
2021-12-08 11:11:27 +08:00
|
|
|
} = await pool.query<ReturnType>(sql`
|
2021-12-20 14:20:23 +08:00
|
|
|
insert into ${table} (${sql.join(
|
2022-01-25 13:34:20 +08:00
|
|
|
insertingKeys.map((key) => fields[key]),
|
2021-08-18 00:24:00 +08:00
|
|
|
sql`, `
|
|
|
|
)})
|
2021-12-20 14:20:23 +08:00
|
|
|
values (${sql.join(
|
2022-01-25 13:34:20 +08:00
|
|
|
insertingKeys.map((key) => convertToPrimitiveOrSql(key, data[key] ?? null)),
|
2021-12-20 14:20:23 +08:00
|
|
|
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
|
|
|
`);
|
|
|
|
|
2021-09-16 23:48:06 +08:00
|
|
|
assertThat(!returning || entry, 'entity.create_failed', { name: rest.tableSingular });
|
2021-08-18 00:24:00 +08:00
|
|
|
return entry;
|
|
|
|
};
|
|
|
|
};
|