From 3530e07fdeaab4569a0567d5adf48d87edddbec9 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Tue, 19 Jul 2022 21:01:34 +0800 Subject: [PATCH 1/3] fix(core): resolve some core no-restricted-syntax lint error resolve some core no-restricted-syntax lint error --- packages/core/package.json | 3 +- packages/core/src/database/utils.ts | 30 +++++++++---------- packages/core/src/lib/passcode.test.ts | 18 ++++++----- packages/core/src/middleware/koa-guard.ts | 2 +- packages/core/src/middleware/koa-i18next.ts | 1 + .../src/middleware/koa-oidc-error-handler.ts | 2 ++ .../middleware/koa-slonik-error-handler.ts | 6 +++- packages/core/src/routes/swagger.ts | 12 +++++--- .../src/utils/oidc-provider-event-listener.ts | 9 +++--- packages/core/src/utils/test-utils.ts | 2 ++ packages/core/src/utils/zod.ts | 5 ++-- 11 files changed, 52 insertions(+), 38 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index edc218f9e45..c76adcfe36c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -115,8 +115,7 @@ "eslintConfig": { "extends": "@silverhand", "rules": { - "complexity": "off", - "no-restricted-syntax": "off" + "complexity": "off" } }, "prettier": "@silverhand/eslint-config/.prettierrc" diff --git a/packages/core/src/database/utils.ts b/packages/core/src/database/utils.ts index 7a52c60d346..0bd63cd3863 100644 --- a/packages/core/src/database/utils.ts +++ b/packages/core/src/database/utils.ts @@ -1,7 +1,7 @@ import { SchemaValuePrimitive, SchemaValue } from '@logto/schemas'; import { Falsy, notFalsy } from '@silverhand/essentials'; import dayjs from 'dayjs'; -import { sql, SqlSqlToken, SqlToken, QueryResult } from 'slonik'; +import { sql, SqlSqlToken, SqlToken, QueryResult, IdentifierSqlToken } from 'slonik'; import { FieldIdentifiers, Table } from './types'; @@ -15,6 +15,8 @@ export const excludeAutoSetFields = (fields: readonly T[]) => Object.freeze( fields.filter( (field): field is ExcludeAutoSetFields => + // Read only string arrays + // eslint-disable-next-line no-restricted-syntax !(autoSetFields as readonly string[]).includes(field) ) ); @@ -52,20 +54,18 @@ export const convertToPrimitiveOrSql = ( throw new Error(`Cannot convert ${key} to primitive`); }; -export const convertToIdentifiers = ( - { table, fields }: T, - withPrefix = false -) => ({ - table: sql.identifier([table]), - fields: Object.entries(fields).reduce( - (previous, [key, value]) => ({ - ...previous, - [key]: sql.identifier(withPrefix ? [table, value] : [value]), - }), - // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter - {} as FieldIdentifiers - ), -}); +export const convertToIdentifiers = ({ table, fields }: T, withPrefix = false) => { + const fieldsIdentifiers = Object.entries(fields).map< + [keyof T['fields'], IdentifierSqlToken] + >(([key, value]) => [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, + }; +}; export const convertToTimestamp = (time = dayjs()) => sql`to_timestamp(${time.valueOf() / 1000})`; diff --git a/packages/core/src/lib/passcode.test.ts b/packages/core/src/lib/passcode.test.ts index fdc6084345a..5d1b8b0e38b 100644 --- a/packages/core/src/lib/passcode.test.ts +++ b/packages/core/src/lib/passcode.test.ts @@ -47,14 +47,16 @@ const mockedIncreasePasscodeTryCount = increasePasscodeTryCount as jest.MockedFu beforeAll(() => { mockedFindUnconsumedPasscodesByJtiAndType.mockResolvedValue([]); - mockedInsertPasscode.mockImplementation(async (data) => ({ - ...data, - createdAt: Date.now(), - phone: data.phone ?? null, - email: data.email ?? null, - consumed: data.consumed ?? false, - tryCount: data.tryCount ?? 0, - })); + mockedInsertPasscode.mockImplementation(async (data): Promise => { + return { + phone: null, + email: null, + consumed: false, + tryCount: 0, + ...data, + createdAt: Date.now(), + }; + }); }); afterEach(() => { diff --git a/packages/core/src/middleware/koa-guard.ts b/packages/core/src/middleware/koa-guard.ts index 2cf3861f4d7..22c0bad5261 100644 --- a/packages/core/src/middleware/koa-guard.ts +++ b/packages/core/src/middleware/koa-guard.ts @@ -81,7 +81,7 @@ export default function koaGuard< WithGuardedRequestContext, GuardResponseT > = async (ctx, next) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions, no-restricted-syntax ctx.guard = { query: tryParse('query', query, ctx.request.query), body: tryParse('body', body, ctx.request.body), diff --git a/packages/core/src/middleware/koa-i18next.ts b/packages/core/src/middleware/koa-i18next.ts index cda338eedab..971367f81c2 100644 --- a/packages/core/src/middleware/koa-i18next.ts +++ b/packages/core/src/middleware/koa-i18next.ts @@ -22,6 +22,7 @@ export default function koaI18next< return async (ctx, next) => { const languages = detectLanguage(ctx); // Cannot patch type def directly, see https://github.com/microsoft/TypeScript/issues/36146 + // eslint-disable-next-line no-restricted-syntax const languageUtils = i18next.services.languageUtils as LanguageUtils; const foundLanguage = languages .map((code) => languageUtils.formatLanguageCode(code)) diff --git a/packages/core/src/middleware/koa-oidc-error-handler.ts b/packages/core/src/middleware/koa-oidc-error-handler.ts index b7a2162041a..4f26a00ada4 100644 --- a/packages/core/src/middleware/koa-oidc-error-handler.ts +++ b/packages/core/src/middleware/koa-oidc-error-handler.ts @@ -45,6 +45,8 @@ export default function koaOIDCErrorHandler(): Middleware(): Middleware).schema.tableSingular, }); case UpdateError: throw new RequestError({ code: 'entity.not_exists', - name: (error as InsertionError).schema.tableSingular, + // Error constructor does not assert the instance type + // eslint-disable-next-line no-restricted-syntax + name: (error as UpdateError).schema.tableSingular, }); case DeletionError: case NotFoundError: diff --git a/packages/core/src/routes/swagger.ts b/packages/core/src/routes/swagger.ts index 2d583da8a51..f922b638e8b 100644 --- a/packages/core/src/routes/swagger.ts +++ b/packages/core/src/routes/swagger.ts @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises'; -import { toTitle } from '@silverhand/essentials'; +import { toTitle, has } from '@silverhand/essentials'; import { load } from 'js-yaml'; import Router, { IMiddleware } from 'koa-router'; import { OpenAPIV3 } from 'openapi-types'; @@ -123,6 +123,7 @@ export default function swaggerRoutes { // Use `as` here since we'll check typing with integration tests + // eslint-disable-next-line no-restricted-syntax const additionalSwagger = load( await readFile('static/yaml/additional-swagger.yaml', { encoding: 'utf-8' }) ) as OpenAPIV3.Document; @@ -130,11 +131,14 @@ export default function swaggerRoutes((router) => router.stack.flatMap(({ path: routerPath, stack, methods }) => methods + .map((method) => method.toLowerCase()) // There is no need to show the HEAD method. - .filter((method) => method !== 'HEAD') - .map((method) => { + .filter( + (method): method is OpenAPIV3.HttpMethods => + method !== 'head' && has(OpenAPIV3.HttpMethods, method.toUpperCase()) + ) + .map((httpMethod) => { const path = `/api${routerPath}`; - const httpMethod = method.toLowerCase() as OpenAPIV3.HttpMethods; const additionalPathItem = additionalSwagger.paths[path] ?? {}; const additionalResponses = additionalPathItem[httpMethod]?.responses; diff --git a/packages/core/src/utils/oidc-provider-event-listener.ts b/packages/core/src/utils/oidc-provider-event-listener.ts index 7da7995feb2..59ba56d807c 100644 --- a/packages/core/src/utils/oidc-provider-event-listener.ts +++ b/packages/core/src/utils/oidc-provider-event-listener.ts @@ -29,11 +29,10 @@ interface GrantBody { } const getLogType = (grantType: unknown) => { - if ( - !grantType || - ![GrantType.AuthorizationCode, GrantType.RefreshToken].includes(grantType as GrantType) - ) { - // Only log token exchange by authorization code or refresh token. + const allowedGrantType = new Set([GrantType.AuthorizationCode, GrantType.RefreshToken]); + + // Only log token exchange by authorization code or refresh token. + if (!grantType || !allowedGrantType.has(grantType)) { return; } diff --git a/packages/core/src/utils/test-utils.ts b/packages/core/src/utils/test-utils.ts index 6cb488a158c..ad23c7d12d6 100644 --- a/packages/core/src/utils/test-utils.ts +++ b/packages/core/src/utils/test-utils.ts @@ -128,6 +128,8 @@ export function createRequester({ if (provider) { route(anonymousRouter, provider); } else { + // For test use only + // eslint-disable-next-line no-restricted-syntax (route as RouteLauncher)(anonymousRouter); } } diff --git a/packages/core/src/utils/zod.ts b/packages/core/src/utils/zod.ts index f3a4b7ab63b..795896d4582 100644 --- a/packages/core/src/utils/zod.ts +++ b/packages/core/src/utils/zod.ts @@ -12,7 +12,6 @@ import { ZodOptional, ZodString, ZodStringDef, - ZodType, ZodUnion, ZodUnknown, } from 'zod'; @@ -129,7 +128,9 @@ export const zodTypeToSwagger = (config: unknown): OpenAPIV3.SchemaObject => { if (config instanceof ZodUnion) { return { - oneOf: (config.options as ZodType[]).map((option) => zodTypeToSwagger(option)), + // ZodUnion.options type is any + // eslint-disable-next-line no-restricted-syntax + oneOf: (config.options as unknown[]).map((option) => zodTypeToSwagger(option)), }; } From 7a58441a979d668479b23a1f3f8d38efb64f6b61 Mon Sep 17 00:00:00 2001 From: simeng-li Date: Wed, 20 Jul 2022 13:57:42 +0800 Subject: [PATCH 2/3] fix(core): cr fix --- .../middleware/koa-slonik-error-handler.ts | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/packages/core/src/middleware/koa-slonik-error-handler.ts b/packages/core/src/middleware/koa-slonik-error-handler.ts index b9a7e60b7cd..2b516aac131 100644 --- a/packages/core/src/middleware/koa-slonik-error-handler.ts +++ b/packages/core/src/middleware/koa-slonik-error-handler.ts @@ -35,30 +35,32 @@ export default function koaSlonikErrorHandler(): Middleware).schema.tableSingular, - }); - case UpdateError: - throw new RequestError({ - code: 'entity.not_exists', - // Error constructor does not assert the instance type - // eslint-disable-next-line no-restricted-syntax - name: (error as UpdateError).schema.tableSingular, - }); - case DeletionError: - case NotFoundError: - throw new RequestError({ - code: 'entity.not_found', - status: 404, - }); - default: - throw error; + if (error instanceof InsertionError) { + throw new RequestError({ + code: 'entity.create_failed', + // Assert generic type of the Class instance + // eslint-disable-next-line no-restricted-syntax + name: (error as InsertionError).schema.tableSingular, + }); } + + if (error instanceof UpdateError) { + throw new RequestError({ + code: 'entity.not_exists', + // Assert generic type of the Class instance + // eslint-disable-next-line no-restricted-syntax + name: (error as UpdateError).schema.tableSingular, + }); + } + + if (error instanceof DeletionError || error instanceof NotFoundError) { + throw new RequestError({ + code: 'entity.not_found', + status: 404, + }); + } + + throw error; } }; } From 5ba5a837234457bb8e3251dd4fe381fb40cabc3b Mon Sep 17 00:00:00 2001 From: simeng-li Date: Wed, 20 Jul 2022 18:18:56 +0800 Subject: [PATCH 3/3] fix(core): remove OpenAPIV3 enum instance reference remove OpenAPIV3 enum instance reference --- packages/core/src/routes/swagger.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/core/src/routes/swagger.ts b/packages/core/src/routes/swagger.ts index f922b638e8b..95c51ff3e03 100644 --- a/packages/core/src/routes/swagger.ts +++ b/packages/core/src/routes/swagger.ts @@ -1,6 +1,6 @@ import { readFile } from 'fs/promises'; -import { toTitle, has } from '@silverhand/essentials'; +import { toTitle } from '@silverhand/essentials'; import { load } from 'js-yaml'; import Router, { IMiddleware } from 'koa-router'; import { OpenAPIV3 } from 'openapi-types'; @@ -133,10 +133,7 @@ export default function swaggerRoutes method.toLowerCase()) // There is no need to show the HEAD method. - .filter( - (method): method is OpenAPIV3.HttpMethods => - method !== 'head' && has(OpenAPIV3.HttpMethods, method.toUpperCase()) - ) + .filter((method): method is OpenAPIV3.HttpMethods => method !== 'head') .map((httpMethod) => { const path = `/api${routerPath}`;