Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: remove slonik from shared #5512

Merged
merged 2 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"pg-protocol": "^1.6.0",
"roarr": "^7.11.0",
"semver": "^7.3.8",
"slonik": "^30.0.0",
"slonik": "30.4.0",
"slonik-interceptor-preset": "^1.2.10",
"slonik-sql-tag-raw": "^1.1.4",
"tar": "^6.1.11",
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/commands/database/seed/tables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ 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';

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';
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/database.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/queries/logto-config.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/queries/system.test.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/queries/system.ts
Original file line number Diff line number Diff line change
@@ -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) => {
Expand Down
75 changes: 75 additions & 0 deletions packages/cli/src/sql.ts
Original file line number Diff line number Diff line change
@@ -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>,
};
};
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
"roarr": "^7.11.0",
"samlify": "2.8.10",
"semver": "^7.3.8",
"slonik": "^30.0.0",
"slonik": "30.4.0",
"slonik-interceptor-preset": "^1.2.10",
"slonik-sql-tag-raw": "^1.1.4",
"snake-case": "^3.0.4",
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/database/find-all-entities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { type GeneratedSchema, type SchemaLike } from '@logto/schemas';
import { conditionalSql, convertToIdentifiers, manyRows } from '@logto/shared';
import { sql, type CommonQueryMethods } from 'slonik';

import { conditionalSql, convertToIdentifiers, manyRows } from '#src/utils/sql.js';

import { buildSearchSql, expandFields, type SearchOptions } from './utils.js';

export const buildFindAllEntitiesWithPool =
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/database/find-entity-by-id.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { SchemaLike, GeneratedSchema } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import type { CommonQueryMethods } from 'slonik';
import { sql, NotFoundError } from 'slonik';

import RequestError from '#src/errors/RequestError/index.js';
import assertThat from '#src/utils/assert-that.js';
import { isKeyOf } from '#src/utils/schema.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

type WithId<Key> = Key | 'id';

Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/database/insert-into.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { CreateUser } from '@logto/schemas';
import { Users } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import decamelize from 'decamelize';

import { InsertionError } from '#src/errors/SlonikError/index.js';
import { convertToIdentifiers } from '#src/utils/sql.js';
import { createTestPool } from '#src/utils/test-utils.js';

const { buildInsertIntoWithPool } = await import('./insert-into.js');
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/database/insert-into.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { GeneratedSchema, SchemaLike } from '@logto/schemas';
import type { OmitAutoSetFields } from '@logto/shared';
import {
convertToIdentifiers,
excludeAutoSetFields,
convertToPrimitiveOrSql,
conditionalSql,
} from '@logto/shared';
import { has } from '@silverhand/essentials';
import type { CommonQueryMethods, IdentifierSqlToken } from 'slonik';
import { sql } from 'slonik';

import { InsertionError } from '#src/errors/SlonikError/index.js';
import assertThat from '#src/utils/assert-that.js';
import {
type OmitAutoSetFields,
convertToIdentifiers,
excludeAutoSetFields,
convertToPrimitiveOrSql,
conditionalSql,
} from '#src/utils/sql.js';

const setExcluded = (...fields: IdentifierSqlToken[]) =>
sql.join(
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/database/update-where.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { SchemaLike, GeneratedSchema, SchemaValue } from '@logto/schemas';
import { convertToIdentifiers, convertToPrimitiveOrSql, conditionalSql } from '@logto/shared';
import type { UpdateWhereData } from '@logto/shared';
import type { Truthy } from '@silverhand/essentials';
import { notFalsy } from '@silverhand/essentials';
Expand All @@ -9,6 +8,7 @@ import { sql } from 'slonik';
import { UpdateError } from '#src/errors/SlonikError/index.js';
import assertThat from '#src/utils/assert-that.js';
import { isKeyOf } from '#src/utils/schema.js';
import { convertToIdentifiers, convertToPrimitiveOrSql, conditionalSql } from '#src/utils/sql.js';

type BuildUpdateWhere = {
<
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/database/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { type GeneratedSchema } from '@logto/schemas';
import { type SchemaLike, conditionalSql, convertToIdentifiers, type Table } from '@logto/shared';
import { type SchemaLike, type Table } from '@logto/shared';
import { type SqlSqlToken, sql } from 'slonik';

import { conditionalSql, convertToIdentifiers } from '#src/utils/sql.js';

/**
* Options for searching for a string within a set of fields (case-insensitive).
*/
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/errors/SlonikError/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { SchemaLike, GeneratedSchema } from '@logto/schemas';
import type { OmitAutoSetFields, UpdateWhereData } from '@logto/shared';
import type { UpdateWhereData } from '@logto/shared';
import { SlonikError } from 'slonik';

import { type OmitAutoSetFields } from '#src/utils/sql.js';

export class DeletionError extends SlonikError {
public constructor(
public readonly table?: string,
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/libraries/logto-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
jwtCustomizerConfigGuard,
} from '@logto/schemas';
import type { LogtoOidcConfigType, LogtoJwtTokenKey } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import chalk from 'chalk';
import { z, ZodError } from 'zod';

import RequestError from '#src/errors/RequestError/index.js';
import type Queries from '#src/tenants/Queries.js';
import { consoleLog } from '#src/utils/console.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

export type LogtoConfigLibrary = ReturnType<typeof createLogtoConfigLibrary>;
const { table } = convertToIdentifiers(LogtoConfigs);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/libraries/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { User, CreateUser, Scope, BindMfa, MfaVerification } from '@logto/schemas';
import { MfaFactor, Users, UsersPasswordEncryptionMethod } from '@logto/schemas';
import { generateStandardShortId, generateStandardId } from '@logto/shared';
import type { OmitAutoSetFields } from '@logto/shared';
import type { Nullable } from '@silverhand/essentials';
import { deduplicate } from '@silverhand/essentials';
import { argon2Verify, bcryptVerify, md5, sha1, sha256 } from 'hash-wasm';
Expand All @@ -14,6 +13,7 @@
import type Queries from '#src/tenants/Queries.js';
import assertThat from '#src/utils/assert-that.js';
import { encryptPassword } from '#src/utils/password.js';
import type { OmitAutoSetFields } from '#src/utils/sql.js';

export const encryptUserPassword = async (
password: string
Expand Down Expand Up @@ -190,7 +190,7 @@
};

const addUserMfaVerification = async (userId: string, payload: BindMfa) => {
// TODO @sijie use jsonb array append

Check warning on line 193 in packages/core/src/libraries/user.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/libraries/user.ts#L193

[no-warning-comments] Unexpected 'todo' comment: 'TODO @sijie use jsonb array append'.
const { mfaVerifications } = await findUserById(userId);
await updateUserById(userId, {
mfaVerifications: [...mfaVerifications, converBindMfaToMfaVerification(payload)],
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/middleware/koa-auth/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
LogtoOidcConfigKey,
LogtoConfigs,
} from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { appendPath } from '@silverhand/essentials';
import type { JWK } from 'jose';
import { sql } from 'slonik';

import { EnvSet, getTenantEndpoint } from '#src/env-set/index.js';
import { exportJWK } from '#src/utils/jwks.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

const { table, fields } = convertToIdentifiers(LogtoConfigs);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ApplicationSignInExperiences, type ApplicationSignInExperience } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql, type CommonQueryMethods } from 'slonik';

import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

const createApplicationSignInExperienceQueries = (pool: CommonQueryMethods) => {
const insert = buildInsertIntoWithPool(pool)(ApplicationSignInExperiences, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
Organizations,
Users,
} from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql, type CommonQueryMethods } from 'slonik';

import RelationQueries from '#src/utils/RelationQueries.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

class ApplicationUserConsentOrganizationsQuery extends RelationQueries<
[typeof Applications, typeof Users, typeof Organizations]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import {
OrganizationScopes,
Scopes,
} from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { sql, type CommonQueryMethods } from 'slonik';

import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import { TwoRelationsQueries } from '#src/utils/RelationQueries.js';
import { convertToIdentifiers } from '#src/utils/sql.js';

export class ApplicationUserConsentOrganizationScopeQueries extends TwoRelationsQueries<
typeof Applications,
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/queries/application.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Applications } from '@logto/schemas';
import { convertToIdentifiers, convertToPrimitiveOrSql, excludeAutoSetFields } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';
import { snakeCase } from 'snake-case';

import { mockApplication } from '#src/__mocks__/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import {
convertToIdentifiers,
convertToPrimitiveOrSql,
excludeAutoSetFields,
} from '#src/utils/sql.js';
import type { QueryType } from '#src/utils/test-utils.js';
import { expectSqlAssert } from '#src/utils/test-utils.js';

Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/queries/application.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import type { Application, CreateApplication } from '@logto/schemas';
import { ApplicationType, Applications, SearchJointMode } from '@logto/schemas';
import type { OmitAutoSetFields } from '@logto/shared';
import { convertToIdentifiers, conditionalSql, conditionalArraySql } from '@logto/shared';
import type { CommonQueryMethods, SqlSqlToken } from 'slonik';
import { sql } from 'slonik';

Expand All @@ -12,6 +10,8 @@ import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import { buildConditionsFromSearch } from '#src/utils/search.js';
import type { Search } from '#src/utils/search.js';
import { convertToIdentifiers, conditionalSql, conditionalArraySql } from '#src/utils/sql.js';
import type { OmitAutoSetFields } from '#src/utils/sql.js';

import ApplicationUserConsentOrganizationsQuery from './application-user-consent-organizations.js';
import {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/queries/applications-roles.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ApplicationsRole, CreateApplicationsRole, Role } from '@logto/schemas';
import { Roles, ApplicationsRoles, RolesScopes } from '@logto/schemas';
import { convertToIdentifiers, conditionalSql } from '@logto/shared';
import { type Nullable } from '@silverhand/essentials';
import type { CommonQueryMethods } from 'slonik';
import { sql } from 'slonik';

import { DeletionError } from '#src/errors/SlonikError/index.js';
import { convertToIdentifiers, conditionalSql } from '#src/utils/sql.js';

const { table, fields } = convertToIdentifiers(ApplicationsRoles, true);
const { fields: insertFields } = convertToIdentifiers(ApplicationsRoles);
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/queries/connector.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Connectors } from '@logto/schemas';
import { convertToIdentifiers } from '@logto/shared';
import { createMockPool, createMockQueryResult, sql } from 'slonik';

import { mockConnector } from '#src/__mocks__/index.js';
import { DeletionError } from '#src/errors/SlonikError/index.js';
import { MockWellKnownCache } from '#src/test-utils/tenant.js';
import { convertToIdentifiers } from '#src/utils/sql.js';
import type { QueryType } from '#src/utils/test-utils.js';
import { expectSqlAssert } from '#src/utils/test-utils.js';

Expand Down
Loading
Loading