0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-13 21:30:30 -05:00
logto/packages/core/src/database/update-where.ts
2022-10-21 14:03:35 +08:00

69 lines
2.5 KiB
TypeScript

import type { SchemaLike, GeneratedSchema } from '@logto/schemas';
import type { UpdateWhereData } from '@logto/shared';
import { convertToIdentifiers, convertToPrimitiveOrSql, conditionalSql } from '@logto/shared';
import type { Truthy } from '@silverhand/essentials';
import { notFalsy } from '@silverhand/essentials';
import { sql } from 'slonik';
import envSet from '@/env-set';
import { UpdateError } from '@/errors/SlonikError';
import assertThat from '@/utils/assert-that';
import { isKeyOf } from '@/utils/schema';
type BuildUpdateWhere = {
<Schema extends SchemaLike, ReturnType extends SchemaLike>(
schema: GeneratedSchema<Schema>,
returning: true
): (data: UpdateWhereData<Schema>) => Promise<ReturnType>;
<Schema extends SchemaLike>(schema: GeneratedSchema<Schema>, returning?: false): (
data: UpdateWhereData<Schema>
) => Promise<void>;
};
export const buildUpdateWhere: BuildUpdateWhere = <
Schema extends SchemaLike,
ReturnType extends SchemaLike
>(
schema: GeneratedSchema<Schema>,
returning = false
) => {
const { table, fields } = convertToIdentifiers(schema);
const isKeyOfSchema = isKeyOf(schema);
const connectKeyValueWithEqualSign = (data: Partial<Schema>, jsonbMode: 'replace' | 'merge') =>
Object.entries(data)
.map(([key, value]) => {
if (!isKeyOfSchema(key)) {
return;
}
if (jsonbMode === 'merge' && value && typeof value === 'object' && !Array.isArray(value)) {
/**
* Jsonb || operator is used to shallow merge two jsonb types of data
* all jsonb data field must be non-nullable
* https://www.postgresql.org/docs/current/functions-json.html
*/
return sql`
${fields[key]}=
coalesce(${fields[key]},'{}'::jsonb)|| ${convertToPrimitiveOrSql(key, value)}
`;
}
return sql`${fields[key]}=${convertToPrimitiveOrSql(key, value)}`;
})
.filter((value): value is Truthy<typeof value> => notFalsy(value));
return async ({ set, where, jsonbMode }: UpdateWhereData<Schema>) => {
const {
rows: [data],
} = await envSet.pool.query<ReturnType>(sql`
update ${table}
set ${sql.join(connectKeyValueWithEqualSign(set, jsonbMode), sql`, `)}
where ${sql.join(connectKeyValueWithEqualSign(where, jsonbMode), sql` and `)}
${conditionalSql(returning, () => sql`returning *`)}
`);
assertThat(!returning || data, new UpdateError(schema, { set, where, jsonbMode }));
return data;
};
};