0
Fork 0
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:
Gao Sun 2024-03-16 00:50:10 +08:00
parent 2908c816e7
commit e22eb3a9d2
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
12 changed files with 93 additions and 33 deletions

View file

@ -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';

View file

@ -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';

View file

@ -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) => {

View file

@ -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';

View file

@ -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
View 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>,
};
};

View file

@ -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;

View file

@ -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;
};

View file

@ -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"
}
}

View file

@ -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';

View file

@ -1,3 +1,2 @@
export * from './universal.js';
export * from './node/index.js';
export * from './database/sql.js';

View file

@ -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