diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index c43c95eeed..3e51ba0a00 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -305,7 +305,7 @@ export async function configurePlatform (): Promise { addLocation(printId, () => import(/* webpackChunkName: "print" */ '@hcengineering/print-resources')) addLocation(textEditorId, () => import(/* webpackChunkName: "text-editor" */ '@hcengineering/text-editor-resources')) - setMetadata(client.metadata.FilterModel, true) + setMetadata(client.metadata.FilterModel, 'ui') setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) // Use binary response transfer for faster performance and small transfer sizes. diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 68031ac2e4..9de3123eba 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -397,7 +397,7 @@ export async function configurePlatform() { addLocation(textEditorId, () => import(/* webpackChunkName: "text-editor" */ '@hcengineering/text-editor-resources')) addLocation(uploaderId, () => import(/* webpackChunkName: "uploader" */ '@hcengineering/uploader-resources')) - setMetadata(client.metadata.FilterModel, true) + setMetadata(client.metadata.FilterModel, 'ui') setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) // Use binary response transfer for faster performance and small transfer sizes. diff --git a/packages/core/src/__tests__/client.test.ts b/packages/core/src/__tests__/client.test.ts index f6cf6d466d..997de8ae98 100644 --- a/packages/core/src/__tests__/client.test.ts +++ b/packages/core/src/__tests__/client.test.ts @@ -13,19 +13,30 @@ // See the License for the specific language governing permissions and // limitations under the License. // -import { Plugin, IntlString } from '@hcengineering/platform' +import { IntlString, Plugin } from '@hcengineering/platform' import type { Account, Class, Data, Doc, Domain, PluginConfiguration, Ref, Timestamp } from '../classes' -import { Space, ClassifierKind, DOMAIN_MODEL } from '../classes' -import { createClient, ClientConnection } from '../client' +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' import { TxOperations } from '../operations' -import type { DocumentQuery, FindResult, TxResult, SearchQuery, SearchOptions, SearchResult } from '../storage' +import type { DocumentQuery, FindResult, SearchOptions, SearchQuery, SearchResult, TxResult } from '../storage' import { Tx, TxFactory, TxProcessor } from '../tx' +import { fillConfiguration, pluginFilterTx } from '../utils' import { connect } from './connection' import { genMinModel } from './minmodel' -import { clone } from '../clone' + +function filterPlugin (plugin: Plugin): (txes: Tx[]) => Promise { + return async (txes) => { + const configs = new Map, PluginConfiguration>() + fillConfiguration(txes, configs) + + const excludedPlugins = Array.from(configs.values()).filter((it) => !it.enabled || it.pluginId !== plugin) + return pluginFilterTx(excludedPlugins, configs, txes) + } +} describe('client', () => { it('should create client and spaces', async () => { @@ -136,7 +147,10 @@ describe('client', () => { } const txCreateDoc1 = txFactory.createTxCreateDoc(core.class.PluginConfiguration, core.space.Model, pluginData1) txes.push(txCreateDoc1) - const client1 = new TxOperations(await createClient(connectPlugin, ['testPlugin1' as Plugin]), core.account.System) + const client1 = new TxOperations( + await createClient(connectPlugin, filterPlugin('testPlugin1' as Plugin)), + core.account.System + ) const result1 = await client1.findAll(core.class.PluginConfiguration, {}) expect(result1).toHaveLength(1) @@ -153,7 +167,10 @@ describe('client', () => { } const txCreateDoc2 = txFactory.createTxCreateDoc(core.class.PluginConfiguration, core.space.Model, pluginData2) txes.push(txCreateDoc2) - const client2 = new TxOperations(await createClient(connectPlugin, ['testPlugin1' as Plugin]), core.account.System) + const client2 = new TxOperations( + await createClient(connectPlugin, filterPlugin('testPlugin1' as Plugin)), + core.account.System + ) const result2 = await client2.findAll(core.class.PluginConfiguration, {}) expect(result2).toHaveLength(2) @@ -176,7 +193,10 @@ describe('client', () => { pluginData3 ) txes.push(txUpdateDoc) - const client3 = new TxOperations(await createClient(connectPlugin, ['testPlugin2' as Plugin]), core.account.System) + const client3 = new TxOperations( + await createClient(connectPlugin, filterPlugin('testPlugin2' as Plugin)), + core.account.System + ) const result3 = await client3.findAll(core.class.PluginConfiguration, {}) expect(result3).toHaveLength(1) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 2b063ea834..11d480e873 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -13,17 +13,16 @@ // limitations under the License. // -import { Plugin } from '@hcengineering/platform' import { BackupClient, DocChunk } from './backup' -import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, PluginConfiguration, Ref, Timestamp } from './classes' +import { Account, AttachedDoc, Class, DOMAIN_MODEL, Doc, Domain, Ref, Timestamp } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { MeasureContext, MeasureMetricsContext } from './measurements' import { ModelDb } from './memdb' import type { DocumentQuery, FindOptions, FindResult, FulltextStorage, Storage, TxResult, WithLookup } from './storage' import { SearchOptions, SearchQuery, SearchResult, SortingOrder } from './storage' -import { Tx, TxCUD, TxCollectionCUD, TxCreateDoc, TxProcessor, TxUpdateDoc } from './tx' -import { toFindResult, toIdMap } from './utils' +import { Tx, TxCUD, TxCollectionCUD } from './tx' +import { toFindResult } from './utils' const transactionThreshold = 500 @@ -215,13 +214,15 @@ export interface TxPersistenceStore { store: (model: LoadModelResponse) => Promise } +export type ModelFilter = (tx: Tx[]) => Promise + /** * @public */ export async function createClient ( connect: (txHandler: TxHandler) => Promise, // If set will build model with only allowed plugins. - allowedPlugins?: Plugin[], + modelFilter?: ModelFilter, txPersistence?: TxPersistenceStore, _ctx?: MeasureContext ): Promise { @@ -248,14 +249,12 @@ export async function createClient ( } lastTx = tx.reduce((cur, it) => (it.modifiedOn > cur ? it.modifiedOn : cur), 0) } - const configs = new Map, PluginConfiguration>() - const conn = await ctx.with('connect', {}, async () => await connect(txHandler)) await ctx.with( 'load-model', { reload: false }, - async (ctx) => await loadModel(ctx, conn, allowedPlugins, configs, hierarchy, model, false, txPersistence) + async (ctx) => await loadModel(ctx, conn, modelFilter, hierarchy, model, false, txPersistence) ) txBuffer = txBuffer.filter((tx) => tx.space !== core.space.Model) @@ -277,7 +276,7 @@ export async function createClient ( const loadModelResponse = await ctx.with( 'connect', { reload: true }, - async (ctx) => await loadModel(ctx, conn, allowedPlugins, configs, hierarchy, model, true, txPersistence) + async (ctx) => await loadModel(ctx, conn, modelFilter, hierarchy, model, true, txPersistence) ) if (event === ClientConnectEvent.Reconnected && loadModelResponse.full) { @@ -286,7 +285,7 @@ export async function createClient ( model = new ModelDb(hierarchy) await ctx.with('build-model', {}, async (ctx) => { - await buildModel(ctx, loadModelResponse, allowedPlugins, configs, hierarchy, model) + await buildModel(ctx, loadModelResponse, modelFilter, hierarchy, model) }) await oldOnConnect?.(ClientConnectEvent.Upgraded) @@ -393,8 +392,7 @@ function isPersonAccount (tx: Tx): boolean { async function loadModel ( ctx: MeasureContext, conn: ClientConnection, - allowedPlugins: Plugin[] | undefined, - configs: Map, PluginConfiguration>, + modelFilter: ModelFilter | undefined, hierarchy: Hierarchy, model: ModelDb, reload = false, @@ -418,19 +416,18 @@ async function loadModel ( ) } - await ctx.with('build-model', {}, (ctx) => buildModel(ctx, modelResponse, allowedPlugins, configs, hierarchy, model)) + await ctx.with('build-model', {}, (ctx) => buildModel(ctx, modelResponse, modelFilter, hierarchy, model)) return modelResponse } async function buildModel ( ctx: MeasureContext, modelResponse: LoadModelResponse, - allowedPlugins: Plugin[] | undefined, - configs: Map, PluginConfiguration>, + modelFilter: ModelFilter | undefined, hierarchy: Hierarchy, model: ModelDb ): Promise { - let systemTx: Tx[] = [] + const systemTx: Tx[] = [] const userTx: Tx[] = [] const atxes = modelResponse.transactions @@ -444,23 +441,11 @@ async function buildModel ( ) }) - if (allowedPlugins != null) { - await ctx.with('fill config system', {}, async () => { - fillConfiguration(systemTx, configs) - }) - await ctx.with('fill config user', {}, async () => { - fillConfiguration(userTx, configs) - }) - const excludedPlugins = Array.from(configs.values()).filter( - (it) => !it.enabled || !allowedPlugins.includes(it.pluginId) - ) - await ctx.with('filter txes', {}, async () => { - systemTx = pluginFilterTx(excludedPlugins, configs, systemTx) - }) + let txes = systemTx.concat(userTx) + if (modelFilter !== undefined) { + txes = await modelFilter(txes) } - const txes = systemTx.concat(userTx) - await ctx.with('build hierarchy', {}, async () => { for (const tx of txes) { try { @@ -488,60 +473,3 @@ function getLastTxTime (txes: Tx[]): number { } return lastTxTime } - -function fillConfiguration (systemTx: Tx[], configs: Map, PluginConfiguration>): void { - for (const t of systemTx) { - if (t._class === core.class.TxCreateDoc) { - const ct = t as TxCreateDoc - if (ct.objectClass === core.class.PluginConfiguration) { - configs.set(ct.objectId as Ref, TxProcessor.createDoc2Doc(ct) as PluginConfiguration) - } - } else if (t._class === core.class.TxUpdateDoc) { - const ut = t as TxUpdateDoc - if (ut.objectClass === core.class.PluginConfiguration) { - const c = configs.get(ut.objectId as Ref) - if (c !== undefined) { - TxProcessor.updateDoc2Doc(c, ut) - } - } - } - } -} - -function pluginFilterTx ( - excludedPlugins: PluginConfiguration[], - configs: Map, PluginConfiguration>, - systemTx: Tx[] -): Tx[] { - const stx = toIdMap(systemTx) - const totalExcluded = new Set>() - let msg = '' - for (const a of excludedPlugins) { - for (const c of configs.values()) { - if (a.pluginId === c.pluginId) { - for (const id of c.transactions) { - if (c.classFilter !== undefined) { - const filter = new Set(c.classFilter) - const tx = stx.get(id as Ref) - if ( - tx?._class === core.class.TxCreateDoc || - tx?._class === core.class.TxUpdateDoc || - tx?._class === core.class.TxRemoveDoc - ) { - const cud = tx as TxCUD - if (filter.has(cud.objectClass)) { - totalExcluded.add(id as Ref) - } - } - } else { - totalExcluded.add(id as Ref) - } - } - msg += ` ${c.pluginId}:${c.transactions.length}` - } - } - } - console.log('exclude plugin', msg) - systemTx = systemTx.filter((t) => !totalExcluded.has(t._id)) - return systemTx -} diff --git a/packages/core/src/hierarchy.ts b/packages/core/src/hierarchy.ts index cf83ff9edd..9397c5fa92 100644 --- a/packages/core/src/hierarchy.ts +++ b/packages/core/src/hierarchy.ts @@ -30,7 +30,7 @@ export class Hierarchy { private readonly attributes = new Map, Map>() private readonly attributesById = new Map, AnyAttribute>() private readonly descendants = new Map, Ref[]>() - private readonly ancestors = new Map, { ordered: Ref[], set: Set> }>() + private readonly ancestors = new Map, Set>>() private readonly proxies = new Map>, ProxyHandler>() private readonly classifierProperties = new Map, Record>() @@ -166,7 +166,7 @@ export class Hierarchy { if (result === undefined) { throw new Error('ancestors not found: ' + _class) } - return result.ordered + return Array.from(result) } getClass(_class: Ref>): Class { @@ -301,7 +301,7 @@ export class Hierarchy { * It will iterate over parents. */ isDerived(_class: Ref>, from: Ref>): boolean { - return this.ancestors.get(_class)?.set?.has(from) ?? false + return this.ancestors.get(_class)?.has(from) ?? false } /** @@ -388,19 +388,17 @@ export class Hierarchy { const list = this.ancestors.get(_class) if (list === undefined) { if (add) { - this.ancestors.set(_class, { ordered: [classifier], set: new Set([classifier]) }) + this.ancestors.set(_class, new Set([classifier])) } } else { if (add) { - if (!list.set.has(classifier)) { - list.ordered.push(classifier) - list.set.add(classifier) + if (!list.has(classifier)) { + list.add(classifier) } } else { - const pos = list.ordered.indexOf(classifier) - if (pos !== -1) { - list.ordered.splice(pos, 1) - list.set.delete(classifier) + const pos = list.has(classifier) + if (pos) { + list.delete(classifier) } } } diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 4987f6d617..506201eba5 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -40,7 +40,8 @@ import { roleOrder, Space, TypedSpace, - WorkspaceMode + WorkspaceMode, + type PluginConfiguration } from './classes' import core from './component' import { Hierarchy } from './hierarchy' @@ -48,7 +49,7 @@ import { TxOperations } from './operations' import { isPredicate } from './predicate' import { Branding, BrandingMap } from './server' import { DocumentQuery, FindResult } from './storage' -import { DOMAIN_TX } from './tx' +import { DOMAIN_TX, TxProcessor, type Tx, type TxCreateDoc, type TxCUD, type TxUpdateDoc } from './tx' function toHex (value: number, chars: number): string { const result = value.toString(16) @@ -835,3 +836,60 @@ export function getBranding (brandings: BrandingMap, key: string | undefined): B return Object.values(brandings).find((branding) => branding.key === key) ?? null } + +export function fillConfiguration (systemTx: Tx[], configs: Map, PluginConfiguration>): void { + for (const t of systemTx) { + if (t._class === core.class.TxCreateDoc) { + const ct = t as TxCreateDoc + if (ct.objectClass === core.class.PluginConfiguration) { + configs.set(ct.objectId as Ref, TxProcessor.createDoc2Doc(ct) as PluginConfiguration) + } + } else if (t._class === core.class.TxUpdateDoc) { + const ut = t as TxUpdateDoc + if (ut.objectClass === core.class.PluginConfiguration) { + const c = configs.get(ut.objectId as Ref) + if (c !== undefined) { + TxProcessor.updateDoc2Doc(c, ut) + } + } + } + } +} + +export function pluginFilterTx ( + excludedPlugins: PluginConfiguration[], + configs: Map, PluginConfiguration>, + systemTx: Tx[] +): Tx[] { + const stx = toIdMap(systemTx) + const totalExcluded = new Set>() + let msg = '' + for (const a of excludedPlugins) { + for (const c of configs.values()) { + if (a.pluginId === c.pluginId) { + for (const id of c.transactions) { + if (c.classFilter !== undefined) { + const filter = new Set(c.classFilter) + const tx = stx.get(id as Ref) + if ( + tx?._class === core.class.TxCreateDoc || + tx?._class === core.class.TxUpdateDoc || + tx?._class === core.class.TxRemoveDoc + ) { + const cud = tx as TxCUD + if (filter.has(cud.objectClass)) { + totalExcluded.add(id as Ref) + } + } + } else { + totalExcluded.add(id as Ref) + } + } + msg += ` ${c.pluginId}:${c.transactions.length}` + } + } + } + console.log('exclude plugin', msg) + systemTx = systemTx.filter((t) => !totalExcluded.has(t._id)) + return systemTx +} diff --git a/plugins/client-resources/src/index.ts b/plugins/client-resources/src/index.ts index be8d1bd02b..58748dc5b3 100644 --- a/plugins/client-resources/src/index.ts +++ b/plugins/client-resources/src/index.ts @@ -24,7 +24,16 @@ import core, { TxWorkspaceEvent, WorkspaceEvent, concatLink, - createClient + createClient, + fillConfiguration, + pluginFilterTx, + type Class, + type ClientConnection, + type Doc, + type ModelFilter, + type PluginConfiguration, + type Ref, + type TxCUD } from '@hcengineering/core' import platform, { Severity, Status, getMetadata, getPlugins, setPlatformStatus } from '@hcengineering/platform' import { connect } from './connection' @@ -70,68 +79,123 @@ export default async () => { return { function: { GetClient: async (token: string, endpoint: string, opt?: ClientFactoryOptions): Promise => { - const filterModel = getMetadata(clientPlugin.metadata.FilterModel) ?? false + const filterModel = getMetadata(clientPlugin.metadata.FilterModel) ?? 'none' - const client = createClient( - async (handler: TxHandler) => { - const url = concatLink(endpoint, `/${token}`) + const handler = async (handler: TxHandler): Promise => { + const url = concatLink(endpoint, `/${token}`) - const upgradeHandler: TxHandler = (...txes: Tx[]) => { - for (const tx of txes) { - if (tx?._class === core.class.TxModelUpgrade) { - opt?.onUpgrade?.() - return - } - if (tx?._class === core.class.TxWorkspaceEvent) { - const event = tx as TxWorkspaceEvent - if (event.event === WorkspaceEvent.MaintenanceNotification) { - void setPlatformStatus( - new Status(Severity.WARNING, platform.status.MaintenanceWarning, { - time: event.params.timeMinutes - }) - ) - } + const upgradeHandler: TxHandler = (...txes: Tx[]) => { + for (const tx of txes) { + if (tx?._class === core.class.TxModelUpgrade) { + opt?.onUpgrade?.() + return + } + if (tx?._class === core.class.TxWorkspaceEvent) { + const event = tx as TxWorkspaceEvent + if (event.event === WorkspaceEvent.MaintenanceNotification) { + void setPlatformStatus( + new Status(Severity.WARNING, platform.status.MaintenanceWarning, { + time: event.params.timeMinutes + }) + ) } } - handler(...txes) } - const tokenPayload: { workspace: string, email: string } = decodeTokenPayload(token) + handler(...txes) + } + const tokenPayload: { workspace: string, email: string } = decodeTokenPayload(token) - const newOpt = { ...opt } - const connectTimeout = getMetadata(clientPlugin.metadata.ConnectionTimeout) - let connectPromise: Promise | undefined - if ((connectTimeout ?? 0) > 0) { - connectPromise = new Promise((resolve, reject) => { - const connectTO = setTimeout(() => { - if (!clientConnection.isConnected()) { - newOpt.onConnect = undefined - void clientConnection?.close() - void opt?.onDialTimeout?.() - reject(new Error(`Connection timeout, and no connection established to ${endpoint}`)) - } - }, connectTimeout) - newOpt.onConnect = (event) => { - // Any event is fine, it means server is alive. - clearTimeout(connectTO) - resolve() + const newOpt = { ...opt } + const connectTimeout = getMetadata(clientPlugin.metadata.ConnectionTimeout) + let connectPromise: Promise | undefined + if ((connectTimeout ?? 0) > 0) { + connectPromise = new Promise((resolve, reject) => { + const connectTO = setTimeout(() => { + if (!clientConnection.isConnected()) { + newOpt.onConnect = undefined + void clientConnection?.close() + void opt?.onDialTimeout?.() + reject(new Error(`Connection timeout, and no connection established to ${endpoint}`)) } - }) - } - const clientConnection = connect(url, upgradeHandler, tokenPayload.workspace, tokenPayload.email, newOpt) - if (connectPromise !== undefined) { - await connectPromise - } - return await Promise.resolve(clientConnection) - }, - filterModel ? [...getPlugins(), ...(getMetadata(clientPlugin.metadata.ExtraPlugins) ?? [])] : undefined, - createModelPersistence(getWSFromToken(token)), - opt?.ctx - ) + }, connectTimeout) + newOpt.onConnect = (event) => { + // Any event is fine, it means server is alive. + clearTimeout(connectTO) + resolve() + } + }) + } + const clientConnection = connect(url, upgradeHandler, tokenPayload.workspace, tokenPayload.email, newOpt) + if (connectPromise !== undefined) { + await connectPromise + } + return await Promise.resolve(clientConnection) + } + + const modelFilter: ModelFilter = async (txes) => { + if (filterModel === 'client') { + return returnClientTxes(txes) + } + if (filterModel === 'ui') { + return returnUITxes(txes) + } + return txes + } + + const client = createClient(handler, modelFilter, createModelPersistence(getWSFromToken(token)), opt?.ctx) return await client } } } } +function returnUITxes (txes: Tx[]): Tx[] { + const configs = new Map, PluginConfiguration>() + fillConfiguration(txes, configs) + + const allowedPlugins = [...getPlugins(), ...(getMetadata(clientPlugin.metadata.ExtraPlugins) ?? [])] + const excludedPlugins = Array.from(configs.values()).filter( + (it) => !it.enabled || !allowedPlugins.includes(it.pluginId) + ) + return pluginFilterTx(excludedPlugins, configs, txes) +} + +function returnClientTxes (txes: Tx[]): Tx[] { + const configs = new Map, PluginConfiguration>() + fillConfiguration(txes, configs) + const excludedPlugins = Array.from(configs.values()).filter((it) => !it.enabled || it.pluginId.startsWith('server-')) + + const toExclude = new Set([ + 'workbench:class:Application' as Ref>, + 'presentation:class:ComponentPointExtension' as Ref>, + 'presentation:class:ObjectSearchCategory' as Ref>, + 'notification:class:NotificationGroup' as Ref>, + 'notification:class:NotificationType' as Ref>, + 'view:class:Action' as Ref>, + 'view:class:Viewlet' as Ref>, + 'text-editor:class:TextEditorAction' as Ref>, + 'templates:class:TemplateField' as Ref>, + 'activity:class:DocUpdateMessageViewlet' as Ref>, + 'core:class:PluginConfiguration' as Ref>, + 'core:class:DomainIndexConfiguration' as Ref> + ]) + + const result = pluginFilterTx(excludedPlugins, configs, txes).filter((tx) => { + // Exclude all matched UI plugins + if ( + tx?._class === core.class.TxCreateDoc || + tx?._class === core.class.TxUpdateDoc || + tx?._class === core.class.TxRemoveDoc + ) { + const cud = tx as TxCUD + if (toExclude.has(cud.objectClass)) { + return false + } + } + return true + }) + return result +} + function createModelPersistence (workspace: string): TxPersistenceStore | undefined { const overrideStore = getMetadata(clientPlugin.metadata.OverridePersistenceStore) if (overrideStore !== undefined) { diff --git a/plugins/client/src/index.ts b/plugins/client/src/index.ts index 8eac225b84..1bccb6cd7e 100644 --- a/plugins/client/src/index.ts +++ b/plugins/client/src/index.ts @@ -69,10 +69,15 @@ export interface ClientFactoryOptions { */ export type ClientFactory = (token: string, endpoint: string, opt?: ClientFactoryOptions) => Promise +// client - will filter out all server model elements +// It will also filter out all UI Elements, like Actions, View declarations etc. +// ui - will filter out all server element's and all UI disabled elements. +export type FilterMode = 'none' | 'client' | 'ui' + export default plugin(clientId, { metadata: { ClientSocketFactory: '' as Metadata, - FilterModel: '' as Metadata, + FilterModel: '' as Metadata, ExtraPlugins: '' as Metadata, UseBinaryProtocol: '' as Metadata, UseProtocolCompression: '' as Metadata, diff --git a/products/tracker/src/platform.ts b/products/tracker/src/platform.ts index 44df935ecd..6c17bdb7e8 100644 --- a/products/tracker/src/platform.ts +++ b/products/tracker/src/platform.ts @@ -104,6 +104,5 @@ export async function configurePlatform() { setMetadata(uiPlugin.metadata.PlatformTitle, 'Tracker') setMetadata(workbench.metadata.PlatformTitle, 'Tracker') - setMetadata(client.metadata.FilterModel, true) - setMetadata(client.metadata.ExtraPlugins, ['preference' as Plugin]) + setMetadata(client.metadata.FilterModel, 'ui') } diff --git a/services/calendar/pod-calendar/src/client.ts b/services/calendar/pod-calendar/src/client.ts index fa222927d8..530bafab4e 100644 --- a/services/calendar/pod-calendar/src/client.ts +++ b/services/calendar/pod-calendar/src/client.ts @@ -13,10 +13,13 @@ // limitations under the License. // +import client from '@hcengineering/client' import { type Client } from '@hcengineering/core' +import { setMetadata } from '@hcengineering/platform' import { createClient, getTransactorEndpoint } from '@hcengineering/server-client' export async function getClient (token: string): Promise { const endpoint = await getTransactorEndpoint(token) + setMetadata(client.metadata.FilterModel, 'client') return await createClient(endpoint, token) } diff --git a/services/github/pod-github/src/client.ts b/services/github/pod-github/src/client.ts index 02813d1dad..ed5a64c148 100644 --- a/services/github/pod-github/src/client.ts +++ b/services/github/pod-github/src/client.ts @@ -37,6 +37,7 @@ export async function createPlatformClient ( { mode: 'github' } ) setMetadata(client.metadata.ConnectionTimeout, timeout) + setMetadata(client.metadata.FilterModel, 'client') const endpoint = await getTransactorEndpoint(token) const connection = await ( await clientResources()