From 9ba0d495e6885a57ab4c88e2227e52cfcc2e117f Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Tue, 10 Jan 2023 13:38:12 +0800 Subject: [PATCH 1/2] refactor(core): migrate hook library to factory mode --- packages/core/src/libraries/hook.test.ts | 56 ++++--- packages/core/src/libraries/hook.ts | 148 +++++++++--------- .../core/src/middleware/koa-audit-log.test.ts | 30 ++-- packages/core/src/middleware/koa-audit-log.ts | 10 +- packages/core/src/model-routers/index.ts | 11 +- packages/core/src/oidc/init.test.ts | 4 +- packages/core/src/oidc/init.ts | 13 +- packages/core/src/routes/hook.ts | 5 +- packages/core/src/routes/interaction/index.ts | 6 +- .../middleware/koa-interaction-hooks.ts | 10 +- packages/core/src/tenants/Libraries.ts | 5 +- packages/core/src/tenants/Tenant.ts | 11 +- packages/core/src/tenants/TenantContext.ts | 3 + packages/core/src/test-utils/tenant.ts | 5 +- 14 files changed, 179 insertions(+), 138 deletions(-) diff --git a/packages/core/src/libraries/hook.test.ts b/packages/core/src/libraries/hook.test.ts index c23701718..65443520e 100644 --- a/packages/core/src/libraries/hook.test.ts +++ b/packages/core/src/libraries/hook.test.ts @@ -4,54 +4,64 @@ import { createMockUtils } from '@logto/shared/esm'; import type { InferModelType } from '@withtyped/server'; import { got } from 'got'; -import modelRouters from '#src/model-routers/index.js'; -import { MockQueryClient } from '#src/test-utils/query-client.js'; +import type { ModelRouters } from '#src/model-routers/index.js'; import type { Interaction } from './hook.js'; const { jest } = import.meta; -const { mockEsm, mockEsmDefault } = createMockUtils(jest); +const { mockEsmDefault, mockEsmWithActual } = createMockUtils(jest); + +const nanoIdMock = 'mockId'; +await mockEsmWithActual('@logto/core-kit', () => ({ + // eslint-disable-next-line unicorn/consistent-function-scoping + buildIdGenerator: () => () => nanoIdMock, + generateStandardId: () => nanoIdMock, +})); + +const { createModelRouters } = await import('#src/model-routers/index.js'); +const { MockQueryClient } = await import('#src/test-utils/query-client.js'); +const { MockQueries } = await import('#src/test-utils/tenant.js'); const queryClient = new MockQueryClient(); const queryFunction = jest.fn(); const url = 'https://logto.gg'; -const hook: InferModelType = { +const hook: InferModelType = { id: 'foo', event: HookEvent.PostSignIn, config: { headers: { bar: 'baz' }, url, retries: 3 }, createdAt: new Date(), }; -const readAll = jest - .spyOn(modelRouters.hook.client, 'readAll') - .mockResolvedValue({ rows: [hook], rowCount: 1 }); const post = jest .spyOn(got, 'post') // @ts-expect-error for testing .mockImplementation(jest.fn(async () => ({ statusCode: 200, body: '{"message":"ok"}' }))); -const nanoIdMock = 'mockId'; -mockEsm('@logto/core-kit', () => ({ - generateStandardId: () => nanoIdMock, -})); - -const { insertLog } = mockEsm('#src/queries/log.js', () => ({ - insertLog: jest.fn(), -})); - -mockEsm('#src/queries/user.js', () => ({ - findUserById: () => ({ id: 'user_id', username: 'user', extraField: 'not_ok' }), -})); -mockEsm('#src/queries/application.js', () => ({ - findApplicationById: () => ({ id: 'app_id', extraField: 'not_ok' }), -})); +const insertLog = jest.fn(); // eslint-disable-next-line unicorn/consistent-function-scoping mockEsmDefault('#src/env-set/create-query-client.js', () => () => queryClient); jest.spyOn(queryClient, 'query').mockImplementation(queryFunction); -const { triggerInteractionHooksIfNeeded } = await import('./hook.js'); +const { createHookLibrary } = await import('./hook.js'); +const modelRouters = createModelRouters(new MockQueryClient()); +const { triggerInteractionHooksIfNeeded } = createHookLibrary( + new MockQueries({ + // @ts-expect-error + users: { findUserById: () => ({ id: 'user_id', username: 'user', extraField: 'not_ok' }) }, + applications: { + // @ts-expect-error + findApplicationById: async () => ({ id: 'app_id', extraField: 'not_ok' }), + }, + logs: { insertLog }, + }), + modelRouters +); + +const readAll = jest + .spyOn(modelRouters.hook.client, 'readAll') + .mockResolvedValue({ rows: [hook], rowCount: 1 }); describe('triggerInteractionHooksIfNeeded()', () => { afterEach(() => { diff --git a/packages/core/src/libraries/hook.ts b/packages/core/src/libraries/hook.ts index 4c739c965..23e25bee3 100644 --- a/packages/core/src/libraries/hook.ts +++ b/packages/core/src/libraries/hook.ts @@ -8,10 +8,8 @@ import { got, HTTPError } from 'got'; import type Provider from 'oidc-provider'; import { LogEntry } from '#src/middleware/koa-audit-log.js'; -import modelRouters from '#src/model-routers/index.js'; -import { findApplicationById } from '#src/queries/application.js'; -import { insertLog } from '#src/queries/log.js'; -import { findUserById } from '#src/queries/user.js'; +import type { ModelRouters } from '#src/model-routers/index.js'; +import type Queries from '#src/tenants/Queries.js'; const parseResponse = ({ statusCode, body }: Response) => ({ statusCode, @@ -27,80 +25,90 @@ const eventToHook: Record = { export type Interaction = Awaited>; -export const triggerInteractionHooksIfNeeded = async ( - event: InteractionEvent, - details?: Interaction, - userAgent?: string -) => { - const userId = details?.result?.login?.accountId; - const sessionId = details?.jti; - const applicationId = details?.params.client_id; +export const createHookLibrary = (queries: Queries, { hook }: ModelRouters) => { + const { + applications: { findApplicationById }, + logs: { insertLog }, + users: { findUserById }, + } = queries; - if (!userId) { - return; - } + const triggerInteractionHooksIfNeeded = async ( + event: InteractionEvent, + details?: Interaction, + userAgent?: string + ) => { + const userId = details?.result?.login?.accountId; + const sessionId = details?.jti; + const applicationId = details?.params.client_id; - const hookEvent = eventToHook[event]; - const { rows } = await modelRouters.hook.client.readAll(); + if (!userId) { + return; + } - const [user, application] = await Promise.all([ - trySafe(findUserById(userId)), - trySafe(async () => - conditional(typeof applicationId === 'string' && (await findApplicationById(applicationId))) - ), - ]); + const hookEvent = eventToHook[event]; + const { rows } = await hook.client.readAll(); - const payload = { - event: hookEvent, - interactionEvent: event, - createdAt: new Date().toISOString(), - sessionId, - userAgent, - userId, - user: user && pick(user, ...userInfoSelectFields), - application: application && pick(application, 'id', 'type', 'name', 'description'), - } satisfies Omit; + const [user, application] = await Promise.all([ + trySafe(findUserById(userId)), + trySafe(async () => + conditional(typeof applicationId === 'string' && (await findApplicationById(applicationId))) + ), + ]); - await Promise.all( - rows - .filter(({ event }) => event === hookEvent) - .map(async ({ config: { url, headers, retries }, id }) => { - console.log(`\tTriggering hook ${id} due to ${hookEvent} event`); - const json: HookEventPayload = { hookId: id, ...payload }; - const logEntry = new LogEntry(`TriggerHook.${hookEvent}`); + const payload = { + event: hookEvent, + interactionEvent: event, + createdAt: new Date().toISOString(), + sessionId, + userAgent, + userId, + user: user && pick(user, ...userInfoSelectFields), + application: application && pick(application, 'id', 'type', 'name', 'description'), + } satisfies Omit; - logEntry.append({ json, hookId: id }); + await Promise.all( + rows + .filter(({ event }) => event === hookEvent) + .map(async ({ config: { url, headers, retries }, id }) => { + console.log(`\tTriggering hook ${id} due to ${hookEvent} event`); + const json: HookEventPayload = { hookId: id, ...payload }; + const logEntry = new LogEntry(`TriggerHook.${hookEvent}`); - // Trigger web hook and log response - await got - .post(url, { - headers: { 'user-agent': 'Logto (https://logto.io)', ...headers }, - json, - retry: { limit: retries }, - timeout: { request: 10_000 }, - }) - .then(async (response) => { - logEntry.append({ - response: parseResponse(response), - }); - }) - .catch(async (error) => { - logEntry.append({ - result: LogResult.Error, - response: conditional(error instanceof HTTPError && parseResponse(error.response)), - error: conditional(error instanceof Error && String(error)), + logEntry.append({ json, hookId: id }); + + // Trigger web hook and log response + await got + .post(url, { + headers: { 'user-agent': 'Logto (https://logto.io)', ...headers }, + json, + retry: { limit: retries }, + timeout: { request: 10_000 }, + }) + .then(async (response) => { + logEntry.append({ + response: parseResponse(response), + }); + }) + .catch(async (error) => { + logEntry.append({ + result: LogResult.Error, + response: conditional(error instanceof HTTPError && parseResponse(error.response)), + error: conditional(error instanceof Error && String(error)), + }); }); + + console.log( + `\tHook ${id} ${logEntry.payload.result === LogResult.Success ? 'succeeded' : 'failed'}` + ); + + await insertLog({ + id: generateStandardId(), + key: logEntry.key, + payload: logEntry.payload, }); + }) + ); + }; - console.log( - `\tHook ${id} ${logEntry.payload.result === LogResult.Success ? 'succeeded' : 'failed'}` - ); - - await insertLog({ - id: generateStandardId(), - key: logEntry.key, - payload: logEntry.payload, - }); - }) - ); + return { triggerInteractionHooksIfNeeded }; }; diff --git a/packages/core/src/middleware/koa-audit-log.test.ts b/packages/core/src/middleware/koa-audit-log.test.ts index 8c58962ac..a6c90be86 100644 --- a/packages/core/src/middleware/koa-audit-log.test.ts +++ b/packages/core/src/middleware/koa-audit-log.test.ts @@ -3,24 +3,26 @@ import { LogResult } from '@logto/schemas'; import { pickDefault, createMockUtils } from '@logto/shared/esm'; import i18next from 'i18next'; -import RequestError from '#src/errors/RequestError/index.js'; -import { createContextWithRouteParameters } from '#src/utils/test-utils.js'; - import type { WithLogContext, LogPayload } from './koa-audit-log.js'; const { jest } = import.meta; -const { mockEsm } = createMockUtils(jest); - -const { insertLog } = mockEsm('#src/queries/log.js', () => ({ - insertLog: jest.fn(), -})); +const { mockEsmWithActual } = createMockUtils(jest); const nanoIdMock = 'mockId'; -mockEsm('@logto/core-kit', () => ({ +await mockEsmWithActual('@logto/core-kit', () => ({ + // eslint-disable-next-line unicorn/consistent-function-scoping + buildIdGenerator: () => () => nanoIdMock, generateStandardId: () => nanoIdMock, })); +const { default: RequestError } = await import('#src/errors/RequestError/index.js'); +const { MockQueries } = await import('#src/test-utils/tenant.js'); +const { createContextWithRouteParameters } = await import('#src/utils/test-utils.js'); + +const insertLog = jest.fn(); +const queries = new MockQueries({ logs: { insertLog } }); + const koaLog = await pickDefault(import('./koa-audit-log.js')); describe('koaAuditLog middleware', () => { @@ -51,7 +53,7 @@ describe('koaAuditLog middleware', () => { log.append(mockPayload); log.append(additionalMockPayload); }; - await koaLog()(ctx, next); + await koaLog(queries)(ctx, next); expect(insertLog).toBeCalledWith({ id: nanoIdMock, @@ -82,7 +84,7 @@ describe('koaAuditLog middleware', () => { const log2 = ctx.createLog(logKey); log2.append(mockPayload); }; - await koaLog()(ctx, next); + await koaLog(queries)(ctx, next); const basePayload = { ...mockPayload, @@ -116,7 +118,7 @@ describe('koaAuditLog middleware', () => { // eslint-disable-next-line unicorn/consistent-function-scoping, @typescript-eslint/no-empty-function const next = async () => {}; - await koaLog()(ctx, next); + await koaLog(queries)(ctx, next); expect(insertLog).not.toBeCalled(); }); @@ -136,7 +138,7 @@ describe('koaAuditLog middleware', () => { log.append(mockPayload); throw error; }; - await expect(koaLog()(ctx, next)).rejects.toMatchError(error); + await expect(koaLog(queries)(ctx, next)).rejects.toMatchError(error); expect(insertLog).toBeCalledWith({ id: nanoIdMock, @@ -172,7 +174,7 @@ describe('koaAuditLog middleware', () => { log2.append(mockPayload); throw error; }; - await expect(koaLog()(ctx, next)).rejects.toMatchError(error); + await expect(koaLog(queries)(ctx, next)).rejects.toMatchError(error); expect(insertLog).toHaveBeenCalledTimes(2); expect(insertLog).toBeCalledWith({ diff --git a/packages/core/src/middleware/koa-audit-log.ts b/packages/core/src/middleware/koa-audit-log.ts index bb8817aa7..877e5bb61 100644 --- a/packages/core/src/middleware/koa-audit-log.ts +++ b/packages/core/src/middleware/koa-audit-log.ts @@ -6,7 +6,7 @@ import type { Context, MiddlewareType } from 'koa'; import type { IRouterParamContext } from 'koa-router'; import RequestError from '#src/errors/RequestError/index.js'; -import { insertLog } from '#src/queries/log.js'; +import type Queries from '#src/tenants/Queries.js'; const removeUndefinedKeys = (object: Record) => Object.fromEntries(Object.entries(object).filter(([, value]) => value !== undefined)); @@ -93,11 +93,9 @@ export type WithLogContext(): MiddlewareType, ResponseBodyT> { +export default function koaAuditLog({ + logs: { insertLog }, +}: Queries): MiddlewareType, ResponseBodyT> { return async (ctx, next) => { const entries: LogEntry[] = []; diff --git a/packages/core/src/model-routers/index.ts b/packages/core/src/model-routers/index.ts index 52a2778d5..258b9cd28 100644 --- a/packages/core/src/model-routers/index.ts +++ b/packages/core/src/model-routers/index.ts @@ -1,10 +1,9 @@ import { Hooks } from '@logto/schemas/models'; import { createModelRouter } from '@withtyped/postgres'; +import type { QueryClient } from '@withtyped/server'; -import envSet from '#src/env-set/index.js'; +export type ModelRouters = ReturnType; -const modelRouters = { - hook: createModelRouter(Hooks, envSet.queryClient).withCrud(), -}; - -export default modelRouters; +export const createModelRouters = (queryClient: QueryClient) => ({ + hook: createModelRouter(Hooks, queryClient).withCrud(), +}); diff --git a/packages/core/src/oidc/init.test.ts b/packages/core/src/oidc/init.test.ts index ed096df50..c9f86870d 100644 --- a/packages/core/src/oidc/init.test.ts +++ b/packages/core/src/oidc/init.test.ts @@ -1,7 +1,9 @@ +import { MockQueries } from '#src/test-utils/tenant.js'; + import initOidc from './init.js'; describe('oidc provider init', () => { it('init should not throw', async () => { - expect(() => initOidc()).not.toThrow(); + expect(() => initOidc(new MockQueries())).not.toThrow(); }); }); diff --git a/packages/core/src/oidc/init.ts b/packages/core/src/oidc/init.ts index 08757bdc6..dc3668669 100644 --- a/packages/core/src/oidc/init.ts +++ b/packages/core/src/oidc/init.ts @@ -13,10 +13,8 @@ import { addOidcEventListeners } from '#src/event-listeners/index.js'; import koaAuditLog from '#src/middleware/koa-audit-log.js'; import postgresAdapter from '#src/oidc/adapter.js'; import { isOriginAllowed, validateCustomClientMetadata } from '#src/oidc/utils.js'; -import { findApplicationById } from '#src/queries/application.js'; -import { findResourceByIndicator } from '#src/queries/resource.js'; -import { findUserById } from '#src/queries/user.js'; import { routes } from '#src/routes/consts.js'; +import type Queries from '#src/tenants/Queries.js'; import assertThat from '#src/utils/assert-that.js'; import { claimToUserKey, getUserClaims } from './scope.js'; @@ -24,7 +22,12 @@ import { claimToUserKey, getUserClaims } from './scope.js'; // Temporarily removed 'EdDSA' since it's not supported by browser yet const supportedSigningAlgs = Object.freeze(['RS256', 'PS256', 'ES256', 'ES384', 'ES512'] as const); -export default function initOidc(): Provider { +export default function initOidc(queries: Queries): Provider { + const { + applications: { findApplicationById }, + resources: { findResourceByIndicator }, + users: { findUserById }, + } = queries; const { issuer, cookieKeys, @@ -208,7 +211,7 @@ export default function initOidc(): Provider { addOidcEventListeners(oidc); // Provide audit log context for event listeners - oidc.use(koaAuditLog()); + oidc.use(koaAuditLog(queries)); return oidc; } diff --git a/packages/core/src/routes/hook.ts b/packages/core/src/routes/hook.ts index d19fbbee7..9f894a96c 100644 --- a/packages/core/src/routes/hook.ts +++ b/packages/core/src/routes/hook.ts @@ -3,7 +3,6 @@ import type { MiddlewareType } from 'koa'; import koaBody from 'koa-body'; import LogtoRequestError from '#src/errors/RequestError/index.js'; -import modelRouters from '#src/model-routers/index.js'; import type { AuthedRouter, RouterInitArgs } from './types.js'; @@ -23,6 +22,8 @@ const errorHandler: MiddlewareType = async (_, next) => { } }; -export default function hookRoutes(...[router]: RouterInitArgs) { +export default function hookRoutes( + ...[router, { modelRouters }]: RouterInitArgs +) { router.all('/hooks/(.*)?', koaBody(), errorHandler, koaAdapter(modelRouters.hook.routes())); } diff --git a/packages/core/src/routes/interaction/index.ts b/packages/core/src/routes/interaction/index.ts index 8549c9006..6daea338a 100644 --- a/packages/core/src/routes/interaction/index.ts +++ b/packages/core/src/routes/interaction/index.ts @@ -45,12 +45,12 @@ export type RouterContext = T extends Router ? Contex export default function interactionRoutes( ...[anonymousRouter, tenant]: RouterInitArgs ) { - const { provider } = tenant; + const { provider, queries } = tenant; const router = // @ts-expect-error for good koa types // eslint-disable-next-line no-restricted-syntax (anonymousRouter as Router>>).use( - koaAuditLog(), + koaAuditLog(queries), koaInteractionDetails(provider) ); @@ -280,7 +280,7 @@ export default function interactionRoutes( router.post( `${interactionPrefix}/submit`, koaInteractionSie(), - koaInteractionHooks(provider), + koaInteractionHooks(tenant), async (ctx, next) => { const { interactionDetails, createLog } = ctx; const interactionStorage = getInteractionStorage(interactionDetails.result); diff --git a/packages/core/src/routes/interaction/middleware/koa-interaction-hooks.ts b/packages/core/src/routes/interaction/middleware/koa-interaction-hooks.ts index 779ca5fab..62aa74d37 100644 --- a/packages/core/src/routes/interaction/middleware/koa-interaction-hooks.ts +++ b/packages/core/src/routes/interaction/middleware/koa-interaction-hooks.ts @@ -1,9 +1,8 @@ import { trySafe } from '@logto/shared'; import type { MiddlewareType } from 'koa'; import type { IRouterParamContext } from 'koa-router'; -import type Provider from 'oidc-provider'; -import { triggerInteractionHooksIfNeeded } from '#src/libraries/hook.js'; +import type TenantContext from '#src/tenants/TenantContext.js'; import { getInteractionStorage } from '../utils/interaction.js'; import type { WithInteractionDetailsContext } from './koa-interaction-details.js'; @@ -12,7 +11,12 @@ export default function koaInteractionHooks< StateT, ContextT extends WithInteractionDetailsContext, ResponseT ->(provider: Provider): MiddlewareType { +>({ + provider, + libraries: { + hooks: { triggerInteractionHooksIfNeeded }, + }, +}: TenantContext): MiddlewareType { return async (ctx, next) => { const { event } = getInteractionStorage(ctx.interactionDetails.result); diff --git a/packages/core/src/tenants/Libraries.ts b/packages/core/src/tenants/Libraries.ts index add111195..95da5b6bb 100644 --- a/packages/core/src/tenants/Libraries.ts +++ b/packages/core/src/tenants/Libraries.ts @@ -1,8 +1,10 @@ import { createConnectorLibrary } from '#src/libraries/connector.js'; +import { createHookLibrary } from '#src/libraries/hook.js'; import { createPhraseLibrary } from '#src/libraries/phrase.js'; import { createResourceLibrary } from '#src/libraries/resource.js'; import { createSignInExperienceLibrary } from '#src/libraries/sign-in-experience/index.js'; import { createUserLibrary } from '#src/libraries/user.js'; +import type { ModelRouters } from '#src/model-routers/index.js'; import type Queries from './Queries.js'; @@ -12,6 +14,7 @@ export default class Libraries { signInExperiences = createSignInExperienceLibrary(this.queries, this.connectors); phrases = createPhraseLibrary(this.queries); resources = createResourceLibrary(this.queries); + hooks = createHookLibrary(this.queries, this.modelRouters); - constructor(public readonly queries: Queries) {} + constructor(private readonly queries: Queries, private readonly modelRouters: ModelRouters) {} } diff --git a/packages/core/src/tenants/Tenant.ts b/packages/core/src/tenants/Tenant.ts index 86ca6167b..b12c4e6ad 100644 --- a/packages/core/src/tenants/Tenant.ts +++ b/packages/core/src/tenants/Tenant.ts @@ -16,6 +16,8 @@ import koaSlonikErrorHandler from '#src/middleware/koa-slonik-error-handler.js'; import koaSpaProxy from '#src/middleware/koa-spa-proxy.js'; import koaSpaSessionGuard from '#src/middleware/koa-spa-session-guard.js'; import koaWelcomeProxy from '#src/middleware/koa-welcome-proxy.js'; +import type { ModelRouters } from '#src/model-routers/index.js'; +import { createModelRouters } from '#src/model-routers/index.js'; import initOidc from '#src/oidc/init.js'; import initRouter from '#src/routes/init.js'; @@ -27,6 +29,7 @@ export default class Tenant implements TenantContext { public readonly provider: Provider; public readonly queries: Queries; public readonly libraries: Libraries; + public readonly modelRouters: ModelRouters; public readonly app: Koa; @@ -35,16 +38,18 @@ export default class Tenant implements TenantContext { } constructor(public id: string) { + const modelRouters = createModelRouters(envSet.queryClient); const queries = new Queries(envSet.pool); - const libraries = new Libraries(queries); + const libraries = new Libraries(queries, modelRouters); + this.modelRouters = modelRouters; this.queries = queries; this.libraries = libraries; // Init app const app = new Koa(); - const provider = initOidc(); + const provider = initOidc(queries); app.use(mount('/oidc', provider.app)); app.use(koaLogger()); @@ -54,7 +59,7 @@ export default class Tenant implements TenantContext { app.use(koaConnectorErrorHandler()); app.use(koaI18next()); - const apisApp = initRouter({ provider, queries, libraries }); + const apisApp = initRouter({ provider, queries, libraries, modelRouters }); app.use(mount('/api', apisApp)); app.use(mount('/', koaRootProxy())); diff --git a/packages/core/src/tenants/TenantContext.ts b/packages/core/src/tenants/TenantContext.ts index 07419823b..0ddc9242c 100644 --- a/packages/core/src/tenants/TenantContext.ts +++ b/packages/core/src/tenants/TenantContext.ts @@ -1,5 +1,7 @@ import type Provider from 'oidc-provider'; +import type { ModelRouters } from '#src/model-routers/index.js'; + import type Libraries from './Libraries.js'; import type Queries from './Queries.js'; @@ -7,4 +9,5 @@ export default abstract class TenantContext { public abstract readonly provider: Provider; public abstract readonly queries: Queries; public abstract readonly libraries: Libraries; + public abstract readonly modelRouters: ModelRouters; } diff --git a/packages/core/src/test-utils/tenant.ts b/packages/core/src/test-utils/tenant.ts index 89b729f1a..259b8105f 100644 --- a/packages/core/src/test-utils/tenant.ts +++ b/packages/core/src/test-utils/tenant.ts @@ -1,11 +1,13 @@ import { createMockPool, createMockQueryResult } from 'slonik'; +import { createModelRouters } from '#src/model-routers/index.js'; import Libraries from '#src/tenants/Libraries.js'; import Queries from '#src/tenants/Queries.js'; import type TenantContext from '#src/tenants/TenantContext.js'; import type { GrantMock } from './oidc-provider.js'; import { createMockProvider } from './oidc-provider.js'; +import { MockQueryClient } from './query-client.js'; export class MockQueries extends Queries { constructor(queriesOverride?: Partial2) { @@ -44,6 +46,7 @@ export type Partial2 = { [key in keyof T]?: Partial }; export class MockTenant implements TenantContext { public queries: Queries; public libraries: Libraries; + public modelRouters = createModelRouters(new MockQueryClient()); constructor( public provider = createMockProvider(), @@ -51,7 +54,7 @@ export class MockTenant implements TenantContext { librariesOverride?: Partial2 ) { this.queries = new MockQueries(queriesOverride); - this.libraries = new Libraries(this.queries); + this.libraries = new Libraries(this.queries, this.modelRouters); this.setPartial('libraries', librariesOverride); } From 7df71dee2fab99f3157f4716cd7c88d422ad44b3 Mon Sep 17 00:00:00 2001 From: Gao Sun Date: Tue, 10 Jan 2023 14:42:47 +0800 Subject: [PATCH 2/2] ci: set max worker to 75% --- packages/core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/package.json b/packages/core/package.json index 3fe0f7d75..c179e9325 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -21,7 +21,7 @@ "start": "NODE_ENV=production node build/index.js", "test:only": "NODE_OPTIONS=--experimental-vm-modules jest", "test": "pnpm build:test && pnpm test:only", - "test:ci": "pnpm test:only --coverage --silent --maxWorkers=50%", + "test:ci": "pnpm test:only --coverage --silent --maxWorkers=75%", "test:report": "codecov -F core" }, "dependencies": {