mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor: remove slonik from shared
This commit is contained in:
parent
2908c816e7
commit
e22eb3a9d2
12 changed files with 93 additions and 33 deletions
|
@ -28,7 +28,7 @@ import {
|
|||
} from '@logto/schemas';
|
||||
import { getTenantRole } from '@logto/schemas';
|
||||
import { Tenants } from '@logto/schemas/models';
|
||||
import { convertToIdentifiers, generateStandardId } from '@logto/shared';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import type { DatabaseTransactionConnection } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
import { raw } from 'slonik-sql-tag-raw';
|
||||
|
@ -36,6 +36,7 @@ import { raw } from 'slonik-sql-tag-raw';
|
|||
import { insertInto } from '../../../database.js';
|
||||
import { getDatabaseName } from '../../../queries/database.js';
|
||||
import { updateDatabaseTimestamp } from '../../../queries/system.js';
|
||||
import { convertToIdentifiers } from '../../../sql.js';
|
||||
import { consoleLog, getPathInModule } from '../../../utils.js';
|
||||
|
||||
import { appendAdminConsoleRedirectUris, seedTenantCloudServiceApplication } from './cloud.js';
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type { SchemaLike } from '@logto/schemas';
|
||||
import { convertToPrimitiveOrSql } from '@logto/shared';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
import decamelize from 'decamelize';
|
||||
import { DatabaseError } from 'pg-protocol';
|
||||
import { createPool, parseDsn, sql, stringifyDsn } from 'slonik';
|
||||
import { createInterceptors } from 'slonik-interceptor-preset';
|
||||
|
||||
import { convertToPrimitiveOrSql } from './sql.js';
|
||||
import { ConfigKey, consoleLog, getCliConfigWithPrompt } from './utils.js';
|
||||
|
||||
export const defaultDatabaseUrl = 'postgresql://localhost:5432/logto';
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import type { LogtoConfig, LogtoConfigKey, logtoConfigGuards } from '@logto/schemas';
|
||||
import { LogtoConfigs } from '@logto/schemas';
|
||||
import { convertToIdentifiers } from '@logto/shared';
|
||||
import type { Nullable } from '@silverhand/essentials';
|
||||
import type { CommonQueryMethods } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { convertToIdentifiers } from '../sql.js';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(LogtoConfigs);
|
||||
|
||||
export const doesConfigsTableExist = async (pool: CommonQueryMethods) => {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { AlterationStateKey, Systems } from '@logto/schemas';
|
||||
import { convertToIdentifiers } from '@logto/shared';
|
||||
import { DatabaseError } from 'pg-protocol';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '../sql.js';
|
||||
import type { QueryType } from '../test-utils.js';
|
||||
import { expectSqlAssert } from '../test-utils.js';
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import type { AlterationState, System, SystemKey } from '@logto/schemas';
|
||||
import { systemGuards, Systems, AlterationStateKey } from '@logto/schemas';
|
||||
import { convertToIdentifiers } from '@logto/shared';
|
||||
import type { Nullable } from '@silverhand/essentials';
|
||||
import { DatabaseError } from 'pg-protocol';
|
||||
import type { CommonQueryMethods, DatabaseTransactionConnection } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
import type { z } from 'zod';
|
||||
|
||||
import { convertToIdentifiers } from '../sql.js';
|
||||
|
||||
const { fields, table } = convertToIdentifiers(Systems);
|
||||
|
||||
const doesTableExist = async (pool: CommonQueryMethods, table: string) => {
|
||||
|
|
75
packages/cli/src/sql.ts
Normal file
75
packages/cli/src/sql.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
/**
|
||||
* @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.)
|
||||
*/
|
||||
|
||||
import { type SchemaValue, type SchemaValuePrimitive, type Table } from '@logto/shared';
|
||||
import { type IdentifierSqlToken, type SqlToken, sql } from 'slonik';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param key The key of value. Will treat as `timestamp` if it ends with `_at` or 'At' AND value is a number;
|
||||
* @param value The value to convert.
|
||||
* @returns A primitive that can be saved into database.
|
||||
*/
|
||||
export const convertToPrimitiveOrSql = (
|
||||
key: string,
|
||||
value: SchemaValue
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
): NonNullable<SchemaValuePrimitive> | SqlToken | null => {
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
return JSON.stringify(value);
|
||||
}
|
||||
|
||||
if (
|
||||
(['_at', 'At'].some((value) => key.endsWith(value)) || key === 'date') &&
|
||||
typeof value === 'number'
|
||||
) {
|
||||
return sql`to_timestamp(${value}::double precision / 1000)`;
|
||||
}
|
||||
|
||||
if (typeof value === 'number' || typeof value === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
if (value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
throw new Error(`Cannot convert ${key} to primitive`);
|
||||
};
|
||||
|
||||
type FieldIdentifiers<Key extends string> = {
|
||||
[key in Key]: IdentifierSqlToken;
|
||||
};
|
||||
|
||||
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])]
|
||||
);
|
||||
|
||||
return {
|
||||
table: sql.identifier([table]),
|
||||
// Key value inferred from the original fields directly
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
fields: Object.fromEntries(fieldsIdentifiers) as FieldIdentifiers<Key>,
|
||||
};
|
||||
};
|
|
@ -1,3 +1,4 @@
|
|||
import type { Table } from '@logto/shared';
|
||||
import { sql } from 'slonik';
|
||||
import { SqlToken } from 'slonik/dist/src/tokens.js';
|
||||
|
||||
|
@ -9,7 +10,6 @@ import {
|
|||
convertToTimestamp,
|
||||
conditionalSql,
|
||||
} from './sql.js';
|
||||
import type { Table } from './types.js';
|
||||
|
||||
const { jest } = import.meta;
|
||||
|
|
@ -1,20 +1,14 @@
|
|||
import type { SchemaValue, SchemaValuePrimitive, Table } from '@logto/shared';
|
||||
import type { Falsy } from '@silverhand/essentials';
|
||||
import { notFalsy } from '@silverhand/essentials';
|
||||
import type { SqlSqlToken, SqlToken, QueryResult, IdentifierSqlToken } from 'slonik';
|
||||
import type { SqlSqlToken, SqlToken, IdentifierSqlToken } from 'slonik';
|
||||
import { sql } from 'slonik';
|
||||
|
||||
import type { FieldIdentifiers, SchemaValue, SchemaValuePrimitive, Table } from './types.js';
|
||||
|
||||
export const conditionalSql = <T>(value: T, buildSql: (value: Exclude<T, Falsy>) => SqlSqlToken) =>
|
||||
notFalsy(value) ? buildSql(value) : sql``;
|
||||
export const conditionalArraySql = <T>(
|
||||
value: T[],
|
||||
buildSql: (value: Exclude<T[], Falsy>) => SqlSqlToken
|
||||
) => (value.length > 0 ? buildSql(value) : sql``);
|
||||
|
||||
export const autoSetFields = Object.freeze(['tenantId', 'createdAt', 'updatedAt'] as const);
|
||||
export type OmitAutoSetFields<T> = Omit<T, (typeof autoSetFields)[number]>;
|
||||
export type ExcludeAutoSetFields<T> = Exclude<T, (typeof autoSetFields)[number]>;
|
||||
|
||||
export const excludeAutoSetFields = <T extends string>(fields: readonly T[]) =>
|
||||
Object.freeze(
|
||||
fields.filter(
|
||||
|
@ -33,10 +27,10 @@ export const excludeAutoSetFields = <T extends string>(fields: readonly T[]) =>
|
|||
* @param value The value to convert.
|
||||
* @returns A primitive that can be saved into database.
|
||||
*/
|
||||
|
||||
export const convertToPrimitiveOrSql = (
|
||||
key: string,
|
||||
value: SchemaValue
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
): NonNullable<SchemaValuePrimitive> | SqlToken | null => {
|
||||
if (value === null) {
|
||||
return null;
|
||||
|
@ -68,6 +62,10 @@ export const convertToPrimitiveOrSql = (
|
|||
throw new Error(`Cannot convert ${key} to primitive`);
|
||||
};
|
||||
|
||||
type FieldIdentifiers<Key extends string> = {
|
||||
[key in Key]: IdentifierSqlToken;
|
||||
};
|
||||
|
||||
export const convertToIdentifiers = <Key extends string>(
|
||||
{ table, fields }: Table<Key>,
|
||||
withPrefix = false
|
||||
|
@ -87,9 +85,3 @@ export const convertToIdentifiers = <Key extends string>(
|
|||
|
||||
export const convertToTimestamp = (time = new Date()) =>
|
||||
sql`to_timestamp(${time.valueOf() / 1000})`;
|
||||
|
||||
export const manyRows = async <T>(query: Promise<QueryResult<T>>): Promise<readonly T[]> => {
|
||||
const { rows } = await query;
|
||||
|
||||
return rows;
|
||||
};
|
|
@ -64,7 +64,6 @@
|
|||
"chalk": "^5.0.0",
|
||||
"find-up": "^7.0.0",
|
||||
"libphonenumber-js": "^1.9.49",
|
||||
"nanoid": "^5.0.1",
|
||||
"slonik": "^30.0.0"
|
||||
"nanoid": "^5.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import type { IdentifierSqlToken } from 'slonik';
|
||||
|
||||
export type SchemaValuePrimitive = string | number | boolean | undefined;
|
||||
export type SchemaValue = SchemaValuePrimitive | Record<string, unknown> | unknown[] | null;
|
||||
export type SchemaLike<Key extends string> = {
|
||||
|
@ -10,9 +8,6 @@ export type Table<Keys extends string, TableName extends string = string> = {
|
|||
table: TableName;
|
||||
fields: Record<Keys, string>;
|
||||
};
|
||||
export type FieldIdentifiers<Key extends string> = {
|
||||
[key in Key]: IdentifierSqlToken;
|
||||
};
|
||||
|
||||
export type OrderDirection = 'asc' | 'desc';
|
||||
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
export * from './universal.js';
|
||||
export * from './node/index.js';
|
||||
export * from './database/sql.js';
|
||||
|
|
|
@ -4078,9 +4078,6 @@ importers:
|
|||
nanoid:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
slonik:
|
||||
specifier: ^30.0.0
|
||||
version: 30.1.2
|
||||
devDependencies:
|
||||
'@logto/connector-kit':
|
||||
specifier: workspace:^2.1.0
|
||||
|
|
Loading…
Reference in a new issue