From b699096e2230e55eded49660d56b24726dbd2950 Mon Sep 17 00:00:00 2001 From: Andrey Sobolev Date: Fri, 7 Apr 2023 12:18:07 +0700 Subject: [PATCH] TSK-1065: Check model version (#2916) Signed-off-by: Andrey Sobolev --- packages/core/src/classes.ts | 7 ++++++ packages/core/src/client.ts | 10 ++++---- plugins/client-resources/src/connection.ts | 10 ++++---- plugins/client-resources/src/index.ts | 15 +++++++++--- plugins/client/src/index.ts | 2 +- plugins/workbench-resources/src/connect.ts | 27 +++++++++++++++++----- server/core/src/storage.ts | 4 ++-- 7 files changed, 53 insertions(+), 22 deletions(-) diff --git a/packages/core/src/classes.ts b/packages/core/src/classes.ts index 888d4866d5..c6160eb71f 100644 --- a/packages/core/src/classes.ts +++ b/packages/core/src/classes.ts @@ -337,6 +337,13 @@ export interface Version extends Doc { patch: number } +/** + * @public + */ +export function versionToString (version: Version): string { + return `${version?.major}.${version?.minor}.${version?.patch}` +} + /** * Blob data from s3 storage * @public diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 26d37ddc9a..3e24764d28 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -15,8 +15,7 @@ import { Plugin } from '@hcengineering/platform' import { BackupClient, DocChunk } from './backup' -import type { Class, Doc, Domain, PluginConfiguration, Ref } from './classes' -import { DOMAIN_MODEL } from './classes' +import { Class, DOMAIN_MODEL, Doc, Domain, PluginConfiguration, Ref } from './classes' import core from './component' import { Hierarchy } from './hierarchy' import { ModelDb } from './memdb' @@ -52,7 +51,7 @@ export interface Client extends Storage { */ export interface ClientConnection extends Storage, BackupClient { close: () => Promise - onConnect?: () => Promise + onConnect?: (apply: boolean) => Promise } class ClientImpl implements Client, BackupClient { @@ -189,7 +188,7 @@ export async function createClient ( } txBuffer = undefined - const oldOnConnect: (() => void) | undefined = conn.onConnect + const oldOnConnect: ((apply: boolean) => void) | undefined = conn.onConnect conn.onConnect = async () => { // Find all new transactions and apply await loadModel(conn, loadedTxIds, allowedPlugins, configs, hierarchy, model) @@ -205,9 +204,10 @@ export async function createClient ( for (const tx of atxes) { txHandler(tx) } + await oldOnConnect?.(true) } else { // We need to trigger full refresh on queries, etc. - await oldOnConnect?.() + await oldOnConnect?.(false) } } diff --git a/plugins/client-resources/src/connection.ts b/plugins/client-resources/src/connection.ts index f665bb16de..727a5c97d1 100644 --- a/plugins/client-resources/src/connection.ts +++ b/plugins/client-resources/src/connection.ts @@ -73,7 +73,7 @@ class Connection implements ClientConnection { private readonly handler: TxHandler, private readonly onUpgrade?: () => void, private readonly onUnauthorized?: () => void, - readonly onConnect?: () => Promise + readonly onConnect?: (apply: boolean) => Promise ) { console.log('connection created') this.interval = setInterval(() => { @@ -151,7 +151,7 @@ class Connection implements ClientConnection { v.reconnect?.() } resolve(websocket) - void this.onConnect?.() + void this.onConnect?.(false) return } @@ -307,9 +307,9 @@ export async function connect ( handler: TxHandler, onUpgrade?: () => void, onUnauthorized?: () => void, - onConnect?: () => void + onConnect?: (apply: boolean) => void ): Promise { - return new Connection(url, handler, onUpgrade, onUnauthorized, async () => { - onConnect?.() + return new Connection(url, handler, onUpgrade, onUnauthorized, async (apply) => { + onConnect?.(apply) }) } diff --git a/plugins/client-resources/src/index.ts b/plugins/client-resources/src/index.ts index 80a04a9a89..80bc8cf563 100644 --- a/plugins/client-resources/src/index.ts +++ b/plugins/client-resources/src/index.ts @@ -14,7 +14,7 @@ // import clientPlugin from '@hcengineering/client' -import { Client, createClient, TxHandler } from '@hcengineering/core' +import core, { Client, createClient, TxHandler, TxWorkspaceEvent, WorkspaceEvent } from '@hcengineering/core' import { getMetadata, getPlugins, getResource } from '@hcengineering/platform' import { connect } from './connection' @@ -29,7 +29,7 @@ export default async () => { endpoint: string, onUpgrade?: () => void, onUnauthorized?: () => void, - onConnect?: () => void + onConnect?: (apply: boolean) => void ): Promise => { const filterModel = getMetadata(clientPlugin.metadata.FilterModel) ?? false @@ -37,7 +37,16 @@ export default async () => { (handler: TxHandler) => { const url = new URL(`/${token}`, endpoint) console.log('connecting to', url.href) - return connect(url.href, handler, onUpgrade, onUnauthorized, onConnect) + const upgradeHandler: TxHandler = (tx) => { + if (tx._class === core.class.TxWorkspaceEvent) { + if ((tx as TxWorkspaceEvent).event === WorkspaceEvent.Upgrade) { + onUpgrade?.() + } + } + handler(tx) + } + + return connect(url.href, upgradeHandler, onUpgrade, onUnauthorized, onConnect) }, filterModel ? getPlugins() : undefined ) diff --git a/plugins/client/src/index.ts b/plugins/client/src/index.ts index cc634b1683..3ed2d4852c 100644 --- a/plugins/client/src/index.ts +++ b/plugins/client/src/index.ts @@ -57,7 +57,7 @@ export type ClientFactory = ( endpoint: string, onUpgrade?: () => void, onUnauthorized?: () => void, - onConnect?: () => void + onConnect?: (apply: boolean) => void ) => Promise export default plugin(clientId, { diff --git a/plugins/workbench-resources/src/connect.ts b/plugins/workbench-resources/src/connect.ts index 4bcf0e069f..0377dfa3c0 100644 --- a/plugins/workbench-resources/src/connect.ts +++ b/plugins/workbench-resources/src/connect.ts @@ -1,6 +1,6 @@ import client from '@hcengineering/client' import contact from '@hcengineering/contact' -import core, { Client, setCurrentAccount, Version } from '@hcengineering/core' +import core, { Client, setCurrentAccount, Version, versionToString } from '@hcengineering/core' import login, { loginId } from '@hcengineering/login' import { getMetadata, getResource, setMetadata } from '@hcengineering/platform' import presentation, { refreshClient, setClient } from '@hcengineering/presentation' @@ -41,6 +41,8 @@ export async function connect (title: string): Promise { let clientSet = false + let version: Version | undefined + const clientFactory = await getResource(client.function.GetClient) _client = await clientFactory( token, @@ -56,11 +58,24 @@ export async function connect (title: string): Promise { }) }, // We need to refresh all active live queries and clear old queries. - () => { + (apply: boolean) => { try { - if (clientSet) { + if (clientSet && !apply) { void refreshClient() } + + void (async () => { + const newVersion = await _client?.findOne(core.class.Version, {}) + console.log('Reconnect Model version', version) + + const currentVersionStr = versionToString(version as Version) + const reconnectVersionStr = versionToString(newVersion as Version) + + if (currentVersionStr !== reconnectVersionStr) { + // It seems upgrade happened + location.reload() + } + })() } catch (err) { console.error(err) } @@ -87,13 +102,13 @@ export async function connect (title: string): Promise { } try { - const version = await _client.findOne(core.class.Version, {}) + version = await _client.findOne(core.class.Version, {}) console.log('Model version', version) const requirdVersion = getMetadata(presentation.metadata.RequiredVersion) - if (requirdVersion !== undefined) { + if (requirdVersion !== undefined && version !== undefined) { console.log('checking min model version', requirdVersion) - const versionStr = `${version?.major as number}.${version?.minor as number}.${version?.patch as number}` + const versionStr = versionToString(version) if (version === undefined || requirdVersion !== versionStr) { versionError = `${versionStr} => ${requirdVersion}` diff --git a/server/core/src/storage.ts b/server/core/src/storage.ts index d4e5bf1586..3ee87b406e 100644 --- a/server/core/src/storage.ts +++ b/server/core/src/storage.ts @@ -175,7 +175,7 @@ class TServerStorage implements ServerStorage { const txCUD = TxProcessor.extractTx(tx) as TxCUD if (!this.hierarchy.isDerived(txCUD._class, core.class.TxCUD)) { // Skip unsupported tx - console.error('Unsupported transacton', tx) + console.error('Unsupported transaction', tx) continue } const domain = this.hierarchy.getDomain(txCUD.objectClass) @@ -640,7 +640,7 @@ class TServerStorage implements ServerStorage { } if (tx.objectSpace === core.space.Model) { - // maintain hiearachy and triggers + // maintain hierarchy and triggers this.hierarchy.tx(tx) await this.triggers.tx(tx) await this.modelDb.tx(tx)