2024-03-15 11:50:10 -05:00
|
|
|
/**
|
|
|
|
* @fileoverview Copied from `@logto/core`. Originally we put them in `@logto/shared` but it
|
|
|
|
* requires `slonik` which makes the package too heavy.
|
|
|
|
*
|
|
|
|
* Since `@logto/cli` only use these functions in a stable manner, we copy them here for now. If
|
|
|
|
* the number of functions grows, we should consider moving them to a separate package. (Actually,
|
|
|
|
* we should remove the dependency on `slonik` at all, and this may not be an issue then.)
|
|
|
|
*/
|
2021-08-17 11:24:00 -05:00
|
|
|
|
2024-03-15 11:50:10 -05:00
|
|
|
import { type SchemaValue, type SchemaValuePrimitive, type Table } from '@logto/shared';
|
2024-03-16 06:04:55 -05:00
|
|
|
import { type IdentifierSqlToken, type SqlToken, sql } from '@silverhand/slonik';
|
2021-08-17 11:24:00 -05:00
|
|
|
|
2021-08-17 11:45:46 -05:00
|
|
|
/**
|
|
|
|
* Note `undefined` is removed from the acceptable list,
|
|
|
|
* since you should NOT call this function if ignoring the field is the desired behavior.
|
|
|
|
* Calling this function with `null` means an explicit `null` setting in database is expected.
|
2021-08-26 00:05:23 -05:00
|
|
|
* @param key The key of value. Will treat as `timestamp` if it ends with `_at` or 'At' AND value is a number;
|
2021-08-17 11:45:46 -05:00
|
|
|
* @param value The value to convert.
|
|
|
|
* @returns A primitive that can be saved into database.
|
|
|
|
*/
|
2021-08-26 00:05:23 -05:00
|
|
|
export const convertToPrimitiveOrSql = (
|
|
|
|
key: string,
|
2023-03-24 11:28:36 -05:00
|
|
|
value: SchemaValue
|
2024-03-15 11:50:10 -05:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
2022-04-24 05:34:18 -05:00
|
|
|
): NonNullable<SchemaValuePrimitive> | SqlToken | null => {
|
2021-08-17 11:24:00 -05:00
|
|
|
if (value === null) {
|
2021-08-17 11:45:46 -05:00
|
|
|
return null;
|
2021-08-17 11:24:00 -05:00
|
|
|
}
|
|
|
|
|
2022-01-28 00:33:57 -05:00
|
|
|
if (typeof value === 'object') {
|
2021-08-17 11:24:00 -05:00
|
|
|
return JSON.stringify(value);
|
|
|
|
}
|
2021-06-23 12:09:42 -05:00
|
|
|
|
2023-07-07 02:14:29 -05:00
|
|
|
if (
|
|
|
|
(['_at', 'At'].some((value) => key.endsWith(value)) || key === 'date') &&
|
|
|
|
typeof value === 'number'
|
|
|
|
) {
|
2022-04-27 04:02:21 -05:00
|
|
|
return sql`to_timestamp(${value}::double precision / 1000)`;
|
2021-08-26 00:05:23 -05:00
|
|
|
}
|
|
|
|
|
2022-09-08 22:15:40 -05:00
|
|
|
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
if (value === '') {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-08-17 11:24:00 -05:00
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2022-01-28 00:33:57 -05:00
|
|
|
throw new Error(`Cannot convert ${key} to primitive`);
|
2021-08-17 11:24:00 -05:00
|
|
|
};
|
2021-07-04 08:52:20 -05:00
|
|
|
|
2024-03-15 11:50:10 -05:00
|
|
|
type FieldIdentifiers<Key extends string> = {
|
|
|
|
[key in Key]: IdentifierSqlToken;
|
|
|
|
};
|
|
|
|
|
2023-10-11 03:41:31 -05:00
|
|
|
export const convertToIdentifiers = <Key extends string>(
|
|
|
|
{ table, fields }: Table<Key>,
|
|
|
|
withPrefix = false
|
|
|
|
) => {
|
|
|
|
const fieldsIdentifiers = Object.entries<string>(fields).map<[Key, IdentifierSqlToken]>(
|
|
|
|
// eslint-disable-next-line no-restricted-syntax -- Object.entries can only return string keys
|
|
|
|
([key, value]) => [key as Key, sql.identifier(withPrefix ? [table, value] : [value])]
|
|
|
|
);
|
2022-07-20 05:28:48 -05:00
|
|
|
|
|
|
|
return {
|
|
|
|
table: sql.identifier([table]),
|
|
|
|
// Key value inferred from the original fields directly
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
2023-10-11 03:41:31 -05:00
|
|
|
fields: Object.fromEntries(fieldsIdentifiers) as FieldIdentifiers<Key>,
|
2022-07-20 05:28:48 -05:00
|
|
|
};
|
|
|
|
};
|