diff --git a/packages/core/src/__tests__/client.test.ts b/packages/core/src/__tests__/client.test.ts index d22405c96e..21173e041b 100644 --- a/packages/core/src/__tests__/client.test.ts +++ b/packages/core/src/__tests__/client.test.ts @@ -14,10 +14,10 @@ // limitations under the License. // import { IntlString, Plugin } from '@hcengineering/platform' +import { ClientConnectEvent, DocChunk } from '..' import type { Account, Class, Data, Doc, Domain, PluginConfiguration, Ref, Timestamp } from '../classes' import { ClassifierKind, DOMAIN_MODEL, Space } from '../classes' import { ClientConnection, createClient } from '../client' -import { clone } from '../clone' import core from '../component' import { Hierarchy } from '../hierarchy' import { ModelDb, TxDb } from '../memdb' @@ -103,38 +103,62 @@ describe('client', () => { return await transactions.findAll(_class, query) } - return { - isConnected: () => true, - findAll, + return new (class implements ClientConnection { + handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise - searchFulltext: async (query: SearchQuery, options: SearchOptions): Promise => { + set onConnect ( + handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined + ) { + this.handler = handler + void this.handler?.(ClientConnectEvent.Connected, '', {}) + } + + get onConnect (): + | ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) + | undefined { + return this.handler + } + + isConnected = (): boolean => true + findAll = findAll + + searchFulltext = async (query: SearchQuery, options: SearchOptions): Promise => { return { docs: [] } - }, + } - tx: async (tx: Tx): Promise => { + tx = async (tx: Tx): Promise => { if (tx.objectSpace === core.space.Model) { hierarchy.tx(tx) } const result = await Promise.all([transactions.tx(tx)]) return result[0] - }, - close: async () => {}, + } + + close = async (): Promise => {} - loadChunk: async (domain: Domain, idx?: number) => ({ + loadChunk = async (domain: Domain, idx?: number): Promise => ({ idx: -1, - index: -1, docs: [], - finished: true, - digest: '' - }), - closeChunk: async (idx: number) => {}, - loadDocs: async (domain: Domain, docs: Ref[]) => [], - upload: async (domain: Domain, docs: Doc[]) => {}, - clean: async (domain: Domain, docs: Ref[]) => {}, - loadModel: async (last: Timestamp) => clone(txes), - getAccount: async () => null as unknown as Account, - sendForceClose: async () => {} - } + finished: true + }) + + async closeChunk (idx: number): Promise {} + async loadDocs (domain: Domain, docs: Ref[]): Promise { + return [] + } + + async upload (domain: Domain, docs: Doc[]): Promise {} + async clean (domain: Domain, docs: Ref[]): Promise {} + async loadModel (last: Timestamp): Promise { + return txes + } + + async getAccount (): Promise { + return null as unknown as Account + } + + async sendForceClose (): Promise {} + })() } const spyCreate = jest.spyOn(TxProcessor, 'createDoc2Doc') const spyUpdate = jest.spyOn(TxProcessor, 'updateDoc2Doc') diff --git a/packages/core/src/__tests__/connection.ts b/packages/core/src/__tests__/connection.ts index 17b549e10d..84570e22cf 100644 --- a/packages/core/src/__tests__/connection.ts +++ b/packages/core/src/__tests__/connection.ts @@ -13,12 +13,13 @@ // limitations under the License. // +import { ClientConnectEvent, DocChunk } from '..' import type { Account, Class, Doc, Domain, Ref, Timestamp } from '../classes' import { ClientConnection } from '../client' import core from '../component' import { Hierarchy } from '../hierarchy' import { ModelDb, TxDb } from '../memdb' -import type { DocumentQuery, FindResult, TxResult, SearchQuery, SearchOptions, SearchResult } from '../storage' +import type { DocumentQuery, FindResult, SearchOptions, SearchQuery, SearchResult, TxResult } from '../storage' import type { Tx } from '../tx' import { DOMAIN_TX } from '../tx' import { genMinModel } from './minmodel' @@ -42,37 +43,62 @@ export async function connect (handler: (tx: Tx) => void): Promise true, - findAll, + class ClientConnectionImpl implements ClientConnection { + isConnected = (): boolean => true + findAll = findAll - searchFulltext: async (query: SearchQuery, options: SearchOptions): Promise => { + handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise + + set onConnect ( + handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined + ) { + this.handler = handler + void this.handler?.(ClientConnectEvent.Connected, '', {}) + } + + get onConnect (): ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined { + return this.handler + } + + async searchFulltext (query: SearchQuery, options: SearchOptions): Promise { return { docs: [] } - }, + } - tx: async (tx: Tx): Promise => { + async tx (tx: Tx): Promise { if (tx.objectSpace === core.space.Model) { hierarchy.tx(tx) } const result = await Promise.all([model.tx(tx), transactions.tx(tx)]) return result[0] - // handler(tx) - we have only one client, should not update? - }, - close: async () => {}, - - loadChunk: async (domain: Domain, idx?: number) => ({ - idx: -1, - index: -1, - docs: [], - finished: true, - digest: '' - }), - closeChunk: async (idx: number) => {}, - loadDocs: async (domain: Domain, docs: Ref[]) => [], - upload: async (domain: Domain, docs: Doc[]) => {}, - clean: async (domain: Domain, docs: Ref[]) => {}, - loadModel: async (last: Timestamp) => txes, - getAccount: async () => null as unknown as Account, - sendForceClose: async () => {} + } + + async close (): Promise {} + + async loadChunk (domain: Domain, idx?: number): Promise { + return { + idx: -1, + docs: [], + finished: true + } + } + + async closeChunk (idx: number): Promise {} + async loadDocs (domain: Domain, docs: Ref[]): Promise { + return [] + } + + async upload (domain: Domain, docs: Doc[]): Promise {} + async clean (domain: Domain, docs: Ref[]): Promise {} + async loadModel (last: Timestamp): Promise { + return txes + } + + async getAccount (): Promise { + return null as unknown as Account + } + + async sendForceClose (): Promise {} } + + return new ClientConnectionImpl() } diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 0f9e9e4849..e3f009213b 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -254,12 +254,9 @@ export async function createClient ( } } } + let initialized = false const conn = await ctx.with('connect', {}, () => connect(txHandler)) - await ctx.with('load-model', { reload: false }, (ctx) => - loadModel(ctx, conn, modelFilter, hierarchy, model, false, txPersistence) - ) - txBuffer = txBuffer.filter((tx) => tx.space !== core.space.Model) client = new ClientImpl(conn) @@ -271,56 +268,92 @@ export async function createClient ( const oldOnConnect: | ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined = conn.onConnect - conn.onConnect = async (event, _lastTx, data) => { - console.log('Client: onConnect', event) - if (event === ClientConnectEvent.Maintenance) { - lastTx = _lastTx - await oldOnConnect?.(ClientConnectEvent.Maintenance, _lastTx, data) - return - } - // Find all new transactions and apply - const loadModelResponse = await ctx.with('connect', { reload: true }, (ctx) => - loadModel(ctx, conn, modelFilter, hierarchy, model, true, txPersistence) - ) - - if (event === ClientConnectEvent.Reconnected && loadModelResponse.full) { - // We have upgrade procedure and need rebuild all stuff. - hierarchy = new Hierarchy() - model = new ModelDb(hierarchy) - await ctx.with('build-model', {}, (ctx) => buildModel(ctx, loadModelResponse, modelFilter, hierarchy, model)) - await oldOnConnect?.(ClientConnectEvent.Upgraded, _lastTx, data) - lastTx = _lastTx - // No need to fetch more stuff since upgrade was happened. - return - } + await new Promise((resolve) => { + conn.onConnect = async (event, _lastTx, data) => { + console.log('Client: onConnect', event) + if (event === ClientConnectEvent.Maintenance) { + lastTx = _lastTx + await oldOnConnect?.(ClientConnectEvent.Maintenance, _lastTx, data) + return + } + // Find all new transactions and apply + const { mode, current, addition } = await ctx.with('load-model', {}, (ctx) => loadModel(ctx, conn, txPersistence)) + if (!initialized) { + switch (mode) { + case 'same': + case 'upgrade': + await ctx.with('build-model', {}, (ctx) => buildModel(ctx, current, modelFilter, hierarchy, model)) + break + case 'addition': + await ctx.with('build-model', {}, (ctx) => + buildModel(ctx, current.concat(addition), modelFilter, hierarchy, model) + ) + } + initialized = true + } else { + switch (mode) { + case 'upgrade': + // We have upgrade procedure and need rebuild all stuff. + hierarchy = new Hierarchy() + model = new ModelDb(hierarchy) + ;(client as ClientImpl).setModel(hierarchy, model) + + await ctx.with('build-model', {}, (ctx) => buildModel(ctx, current, modelFilter, hierarchy, model)) + await oldOnConnect?.(ClientConnectEvent.Upgraded, _lastTx, data) + // No need to fetch more stuff since upgrade was happened. + break + case 'addition': + await ctx.with('build-model', {}, (ctx) => + buildModel(ctx, current.concat(addition), modelFilter, hierarchy, model) + ) + break + } + } + resolve() + + if (lastTx === undefined) { + // No need to do anything here since we connected. + await oldOnConnect?.(event, _lastTx, data) + lastTx = _lastTx + resolve() + return + } - if (event === ClientConnectEvent.Connected && _lastTx !== lastTx && lastTx === undefined) { - // No need to do anything here since we connected. - await oldOnConnect?.(event, _lastTx, data) + if (lastTx === _lastTx) { + // Same lastTx, no need to refresh + await oldOnConnect?.(ClientConnectEvent.Reconnected, _lastTx, data) + resolve() + return + } lastTx = _lastTx - return - } - - if (_lastTx === lastTx) { - // Same lastTx, no need to refresh - await oldOnConnect?.(ClientConnectEvent.Reconnected, _lastTx, data) - return + // We need to trigger full refresh on queries, etc. + await oldOnConnect?.(ClientConnectEvent.Refresh, lastTx, data) + resolve() } - lastTx = _lastTx - // We need to trigger full refresh on queries, etc. - await oldOnConnect?.(ClientConnectEvent.Refresh, lastTx, data) - } + }) return client } -async function tryLoadModel ( +// Ignore Employee accounts. +function isPersonAccount (tx: Tx): boolean { + return ( + (tx._class === core.class.TxCreateDoc || + tx._class === core.class.TxUpdateDoc || + tx._class === core.class.TxRemoveDoc) && + ((tx as TxCUD).objectClass === 'contact:class:PersonAccount' || + (tx as TxCUD).objectClass === 'core:class:Account') + ) +} + +async function loadModel ( ctx: MeasureContext, conn: ClientConnection, - reload: boolean, persistence?: TxPersistenceStore -): Promise { +): Promise<{ mode: 'same' | 'addition' | 'upgrade', current: Tx[], addition: Tx[] }> { + const t = Date.now() + const current = (await ctx.with('persistence-load', {}, () => persistence?.load())) ?? { full: true, transactions: [], @@ -329,8 +362,7 @@ async function tryLoadModel ( if (conn.getLastHash !== undefined && (await conn.getLastHash(ctx)) === current.hash) { // We have same model hash. - current.full = false // Since we load, no need to send full - return current + return { mode: 'same', current: current.transactions, addition: [] } } const lastTxTime = getLastTxTime(current.transactions) const result = await ctx.with('connection-load-model', { hash: current.hash !== '' }, (ctx) => @@ -340,78 +372,37 @@ async function tryLoadModel ( if (Array.isArray(result)) { // Fallback to old behavior, only for tests return { - full: true, - transactions: result, - hash: '' + mode: 'same', + current: result, + addition: [] } } - const transactions = current.transactions.concat(result.transactions) - if (result.hash !== current.hash) { - // Save concatenated, if have some more of them. - void ctx - .with('persistence-store', {}, (ctx) => - persistence?.store({ - ...result, - transactions: !result.full ? transactions : result.transactions - }) - ) - .catch((err) => { - Analytics.handleError(err) - }) - } - - if (!result.full && !reload) { - result.transactions = transactions - } - - return result -} - -// Ignore Employee accounts. -function isPersonAccount (tx: Tx): boolean { - return ( - (tx._class === core.class.TxCreateDoc || - tx._class === core.class.TxUpdateDoc || - tx._class === core.class.TxRemoveDoc) && - ((tx as TxCUD).objectClass === 'contact:class:PersonAccount' || - (tx as TxCUD).objectClass === 'core:class:Account') - ) -} -async function loadModel ( - ctx: MeasureContext, - conn: ClientConnection, - modelFilter: ModelFilter | undefined, - hierarchy: Hierarchy, - model: ModelDb, - reload = false, - persistence?: TxPersistenceStore -): Promise { - const t = Date.now() - - const modelResponse = await ctx.with('try-load-model', { reload }, (ctx) => - tryLoadModel(ctx, conn, reload, persistence) - ) - - if (reload && modelResponse.full) { - return modelResponse - } + // Save concatenated, if have some more of them. + void ctx + .with('persistence-store', {}, (ctx) => + persistence?.store({ + ...result, + // Store concatinated old + new txes + transactions: result.full ? result.transactions : current.transactions.concat(result.transactions) + }) + ) + .catch((err) => { + Analytics.handleError(err) + }) if (typeof window !== 'undefined') { - console.log( - 'find' + (modelResponse.full ? 'full model' : 'model diff'), - modelResponse.transactions.length, - Date.now() - t - ) + console.log('find' + (result.full ? 'full model' : 'model diff'), result.transactions.length, Date.now() - t) } - - await ctx.with('build-model', {}, (ctx) => buildModel(ctx, modelResponse, modelFilter, hierarchy, model)) - return modelResponse + if (result.full) { + return { mode: 'upgrade', current: result.transactions, addition: [] } + } + return { mode: 'addition', current: current.transactions, addition: result.transactions } } async function buildModel ( ctx: MeasureContext, - modelResponse: LoadModelResponse, + transactions: Tx[], modelFilter: ModelFilter | undefined, hierarchy: Hierarchy, model: ModelDb @@ -419,7 +410,7 @@ async function buildModel ( const systemTx: Tx[] = [] const userTx: Tx[] = [] - const atxes = modelResponse.transactions + const atxes = transactions ctx.withSync('split txes', {}, () => { atxes.forEach((tx) => diff --git a/packages/query/src/__tests__/connection.ts b/packages/query/src/__tests__/connection.ts index 3cb9a5c4a6..6c41e0cd76 100644 --- a/packages/query/src/__tests__/connection.ts +++ b/packages/query/src/__tests__/connection.ts @@ -13,26 +13,33 @@ // limitations under the License. // -import type { +import core, { + Account, AccountClient, BackupClient, Class, + ClientConnectEvent, + ClientConnection, Doc, + DocChunk, DocumentQuery, Domain, + DOMAIN_TX, FindOptions, FindResult, + FulltextStorage, + Hierarchy, LoadModelResponse, + ModelDb, Ref, + SearchOptions, + SearchQuery, + SearchResult, Timestamp, Tx, - TxResult, - FulltextStorage, - SearchQuery, - SearchOptions, - SearchResult + TxDb, + TxResult } from '@hcengineering/core' -import core, { DOMAIN_TX, Hierarchy, ModelDb, TxDb } from '@hcengineering/core' import { genMinModel } from './minmodel' export async function connect (handler: (tx: Tx) => void): Promise< @@ -55,49 +62,103 @@ FulltextStorage & { await model.tx(tx) } - async function findAll ( - _class: Ref>, - query: DocumentQuery, - options?: FindOptions - ): Promise> { - const domain = hierarchy.getClass(_class).domain - if (domain === DOMAIN_TX) return await transactions.findAll(_class, query, options) - return await model.findAll(_class, query, options) - } + class TestConnection implements ClientConnection { + private readonly hierarchy: Hierarchy + private readonly model: ModelDb + private readonly transactions: TxDb + + constructor (hierarchy: Hierarchy, model: ModelDb, transactions: TxDb) { + this.hierarchy = hierarchy + this.model = model + this.transactions = transactions + } + + isConnected (): boolean { + return true + } + + async findAll( + _class: Ref>, + query: DocumentQuery, + options?: FindOptions + ): Promise> { + const domain = this.hierarchy.getClass(_class).domain + if (domain === DOMAIN_TX) return await this.transactions.findAll(_class, query, options) + return await this.model.findAll(_class, query, options) + } + + async findOne( + _class: Ref>, + query: DocumentQuery, + options?: FindOptions + ): Promise { + return (await this.findAll(_class, query, { ...options, limit: 1 })).shift() + } + + getHierarchy (): Hierarchy { + return this.hierarchy + } + + getModel (): ModelDb { + return this.model + } + + async getAccount (): Promise { + return {} as unknown as any + } - return { - isConnected: () => true, - findAll, - findOne: async (_class, query, options) => (await findAll(_class, query, { ...options, limit: 1 })).shift(), - getHierarchy: () => hierarchy, - getModel: () => model, - getAccount: async () => ({}) as unknown as any, - tx: async (tx: Tx): Promise => { + async tx (tx: Tx): Promise { if (tx.objectSpace === core.space.Model) { - hierarchy.tx(tx) + this.hierarchy.tx(tx) } - await Promise.all([model.tx(tx), transactions.tx(tx)]) - // Not required, since handled in client. + await Promise.all([this.model.tx(tx), this.transactions.tx(tx)]) handler(tx) return {} - }, - close: async () => {}, - loadChunk: async (domain: Domain, idx?: number) => ({ - idx: -1, - index: -1, - docs: [], - finished: true, - digest: '' - }), - loadModel: async (lastTxTime) => txes, - closeChunk: async (idx: number) => {}, - loadDocs: async (domain: Domain, docs: Ref[]) => [], - upload: async (domain: Domain, docs: Doc[]) => {}, - clean: async (domain: Domain, docs: Ref[]) => {}, - - searchFulltext: async (query: SearchQuery, options: SearchOptions): Promise => { + } + + async close (): Promise {} + + async loadChunk (domain: Domain, idx?: number): Promise { + return { + idx: -1, + docs: [], + finished: true + } + } + + async loadModel (lastTxTime: Timestamp): Promise { + return txes + } + + async closeChunk (idx: number): Promise {} + + async loadDocs (domain: Domain, docs: Ref[]): Promise { + return [] + } + + async upload (domain: Domain, docs: Doc[]): Promise {} + + async clean (domain: Domain, docs: Ref[]): Promise {} + + async searchFulltext (query: SearchQuery, options: SearchOptions): Promise { return { docs: [] } - }, - sendForceClose: async () => {} + } + + async sendForceClose (): Promise {} + + handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise + + set onConnect ( + handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined + ) { + this.handler = handler + void this.handler?.(ClientConnectEvent.Connected, '', {}) + } + + get onConnect (): ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined { + return this.handler + } } + + return new TestConnection(hierarchy, model, transactions) } diff --git a/packages/query/src/__tests__/query.test.ts b/packages/query/src/__tests__/query.test.ts index 517dee02c6..3e19654ecc 100644 --- a/packages/query/src/__tests__/query.test.ts +++ b/packages/query/src/__tests__/query.test.ts @@ -984,7 +984,7 @@ describe('query', () => { it('test clone ops', async () => { const { liveQuery, factory } = await getClient() - const counter = 10000 + const counter = 1000 const ctx = new MeasureMetricsContext('tool', {}) let data: Space[] = [] const pp = new Promise((resolve) => { diff --git a/server/core/src/utils.ts b/server/core/src/utils.ts index 5fb08a4021..c0f43a2002 100644 --- a/server/core/src/utils.ts +++ b/server/core/src/utils.ts @@ -1,5 +1,6 @@ import core, { AccountRole, + ClientConnectEvent, WorkspaceEvent, generateId, getTypeOf, @@ -11,17 +12,27 @@ import core, { type BulkUpdateEvent, type Class, type Client, + type ClientConnection, type Doc, + type DocChunk, + type DocumentQuery, + type Domain, + type FindOptions, + type FindResult, type MeasureContext, type ModelDb, type Ref, + type SearchResult, type SessionData, + type Tx, + type TxResult, type TxWorkspaceEvent, type WorkspaceIdWithUrl } from '@hcengineering/core' import platform, { PlatformError, Severity, Status, unknownError } from '@hcengineering/platform' import { type Hash } from 'crypto' import fs from 'fs' +import type { DbAdapter } from './adapter' import { BackupClientOps } from './storage' import type { Pipeline } from './types' @@ -293,3 +304,65 @@ export function wrapPipeline ( notify: (...tx) => {} } } + +export function wrapAdapterToClient (ctx: MeasureContext, storageAdapter: DbAdapter, txes: Tx[]): ClientConnection { + class TestClientConnection implements ClientConnection { + isConnected = (): boolean => true + + handler?: (event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise + + set onConnect ( + handler: ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined + ) { + this.handler = handler + void this.handler?.(ClientConnectEvent.Connected, '', {}) + } + + get onConnect (): ((event: ClientConnectEvent, lastTx: string | undefined, data: any) => Promise) | undefined { + return this.handler + } + + async findAll( + _class: Ref>, + query: DocumentQuery, + options?: FindOptions + ): Promise> { + return (await storageAdapter.findAll(ctx, _class, query, options)) as any + } + + async tx (tx: Tx): Promise { + return await storageAdapter.tx(ctx, tx) + } + + async searchFulltext (): Promise { + return { docs: [] } + } + + async close (): Promise {} + + async loadChunk (domain: Domain): Promise { + throw new Error('unsupported') + } + + async closeChunk (idx: number): Promise {} + + async loadDocs (domain: Domain, docs: Ref[]): Promise { + return [] + } + + async upload (domain: Domain, docs: Doc[]): Promise {} + + async clean (domain: Domain, docs: Ref[]): Promise {} + + async loadModel (): Promise { + return txes + } + + async getAccount (): Promise { + return {} as any + } + + async sendForceClose (): Promise {} + } + return new TestClientConnection() +} diff --git a/server/mongo/src/__tests__/storage.test.ts b/server/mongo/src/__tests__/storage.test.ts index 99a408c17a..71216c5a9b 100644 --- a/server/mongo/src/__tests__/storage.test.ts +++ b/server/mongo/src/__tests__/storage.test.ts @@ -15,11 +15,7 @@ // import core, { type Client, - type ClientConnection, createClient, - type Doc, - type DocChunk, - type Domain, generateId, getWorkspaceId, Hierarchy, @@ -30,7 +26,7 @@ import core, { type Space, TxOperations } from '@hcengineering/core' -import { type DbAdapter } from '@hcengineering/server-core' +import { type DbAdapter, wrapAdapterToClient } from '@hcengineering/server-core' import { createMongoAdapter, createMongoTxAdapter } from '..' import { getMongoClient, type MongoClientReference, shutdown } from '../utils' import { genMinModel } from './minmodel' @@ -108,22 +104,7 @@ describe('mongo operations', () => { const ctx = new MeasureMetricsContext('client', {}) client = await createClient(async (handler) => { - const st: ClientConnection = { - isConnected: () => true, - findAll: async (_class, query, options) => await serverStorage.findAll(ctx, _class, query, options), - tx: async (tx) => await serverStorage.tx(ctx, tx), - searchFulltext: async () => ({ docs: [] }), - close: async () => {}, - loadChunk: async (domain): Promise => await Promise.reject(new Error('unsupported')), - closeChunk: async (idx) => {}, - loadDocs: async (domain: Domain, docs: Ref[]) => [], - upload: async (domain: Domain, docs: Doc[]) => {}, - clean: async (domain: Domain, docs: Ref[]) => {}, - loadModel: async () => txes, - getAccount: async () => ({}) as any, - sendForceClose: async () => {} - } - return st + return wrapAdapterToClient(ctx, serverStorage, txes) }) operations = new TxOperations(client, core.account.System) diff --git a/server/postgres/src/__tests__/storage.test.ts b/server/postgres/src/__tests__/storage.test.ts index 7b7b5acea2..bc619a440e 100644 --- a/server/postgres/src/__tests__/storage.test.ts +++ b/server/postgres/src/__tests__/storage.test.ts @@ -14,11 +14,7 @@ // import core, { type Client, - type ClientConnection, createClient, - type Doc, - type DocChunk, - type Domain, generateId, getWorkspaceId, Hierarchy, @@ -29,7 +25,7 @@ import core, { type Space, TxOperations } from '@hcengineering/core' -import { type DbAdapter } from '@hcengineering/server-core' +import { type DbAdapter, wrapAdapterToClient } from '@hcengineering/server-core' import { createPostgresAdapter, createPostgresTxAdapter } from '..' import { getDBClient, type PostgresClientReference, shutdown } from '../utils' import { genMinModel } from './minmodel' @@ -121,22 +117,7 @@ describe('postgres operations', () => { ) await serverStorage.init?.(ctx) client = await createClient(async (handler) => { - const st: ClientConnection = { - isConnected: () => true, - findAll: async (_class, query, options) => await serverStorage.findAll(ctx, _class, query, options), - tx: async (tx) => await serverStorage.tx(ctx, tx), - searchFulltext: async () => ({ docs: [] }), - close: async () => {}, - loadChunk: async (domain): Promise => await Promise.reject(new Error('unsupported')), - closeChunk: async (idx) => {}, - loadDocs: async (domain: Domain, docs: Ref[]) => [], - upload: async (domain: Domain, docs: Doc[]) => {}, - clean: async (domain: Domain, docs: Ref[]) => {}, - loadModel: async () => txes, - getAccount: async () => ({}) as any, - sendForceClose: async () => {} - } - return st + return wrapAdapterToClient(ctx, serverStorage, txes) }) operations = new TxOperations(client, core.account.System)