From b57f843e91f01b6cbef29ead4e8b619902cde9ad Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Mon, 20 Jan 2025 20:32:43 +0800 Subject: [PATCH 1/2] CB-5839 fix: resource blocking --- .../src/ConnectionInfoResource.ts | 12 ++++++++++++ .../src/ConnectionsManagerService.ts | 4 ---- .../ElementsTree/useElementsTree.ts | 17 +---------------- .../src/NavigationTree/NavigationTreeService.ts | 16 +++++++++++----- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/webapp/packages/core-connections/src/ConnectionInfoResource.ts b/webapp/packages/core-connections/src/ConnectionInfoResource.ts index 5692c2fc0a..dab8eabbe0 100644 --- a/webapp/packages/core-connections/src/ConnectionInfoResource.ts +++ b/webapp/packages/core-connections/src/ConnectionInfoResource.ts @@ -291,6 +291,18 @@ export class ConnectionInfoResource extends CachedMapResource connection?.connected ?? false); } + // TODO: we need here node path ie ['', 'project://', 'database://...', '...'] + getConnectionIdForNodeId(projectId: string, nodeId: string): IConnectionInfoParams | undefined { + if (!nodeId.startsWith('database://')) { + return; + } + + const indexOfConnectionPart = nodeId.indexOf('/', 11); + const connectionId = nodeId.slice(11, indexOfConnectionPart > -1 ? indexOfConnectionPart : nodeId.length); + + return createConnectionParam(projectId, connectionId); + } + // TODO: we need here node path ie ['', 'project://', 'database://...', '...'] getConnectionForNode(nodeId: string): Connection | undefined { if (!nodeId.startsWith('database://')) { diff --git a/webapp/packages/core-connections/src/ConnectionsManagerService.ts b/webapp/packages/core-connections/src/ConnectionsManagerService.ts index 5e0a946a66..1f463e9b61 100644 --- a/webapp/packages/core-connections/src/ConnectionsManagerService.ts +++ b/webapp/packages/core-connections/src/ConnectionsManagerService.ts @@ -80,10 +80,6 @@ export class ConnectionsManagerService { return connection.connection; } - addOpenedConnection(connection: Connection): void { - this.connectionInfo.add(connection); - } - getObjectContainerById(connectionKey: IConnectionInfoParams, objectCatalogId?: string, objectSchemaId?: string): ObjectContainer | undefined { if (objectCatalogId) { const objectContainers = this.containerContainers.getCatalogData(connectionKey, objectCatalogId); diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts index 927ff50229..a7409c97c3 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts @@ -17,11 +17,10 @@ import { useResource, useUserData, } from '@cloudbeaver/core-blocks'; -import { ConnectionInfoActiveProjectKey, ConnectionInfoResource } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; import { ExecutorInterrupter, type ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; -import { type NavNode, NavNodeInfoResource, NavTreeResource, ROOT_NODE_PATH } from '@cloudbeaver/core-navigation-tree'; +import { type NavNode, NavNodeInfoResource, NavTreeResource } from '@cloudbeaver/core-navigation-tree'; import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects'; import { CachedMapAllKey, @@ -147,11 +146,9 @@ export interface IElementsTree extends ILoadableState { export function useElementsTree(options: IOptions): IElementsTree { const projectsService = useService(ProjectsService); - const projectInfoResource = useService(ProjectInfoResource); const notificationService = useService(NotificationService); const navNodeInfoResource = useService(NavNodeInfoResource); const navTreeResource = useService(NavTreeResource); - const connectionInfoResource = useService(ConnectionInfoResource); const elementsTreeService = useService(ElementsTreeService); const [localTreeNodesState] = useState( @@ -189,8 +186,6 @@ export function useElementsTree(options: IOptions): IElementsTree { const functionsRef = useObjectRef({ async loadTree(...nodes: string[]) { await Promise.all(loadingNodes.values()); - await projectInfoResource.load(); - await connectionInfoResource.load(ConnectionInfoActiveProjectKey); const preloadedRoot = await elementsTree.loadPath(options.folderExplorer.state.fullPath); if (preloadedRoot !== options.folderExplorer.state.folder) { @@ -218,11 +213,6 @@ export function useElementsTree(options: IOptions): IElementsTree { }, async loadNode(nodeId: string) { - await projectInfoResource.waitLoad(); - await connectionInfoResource.waitLoad(); - await navTreeResource.waitLoad(); - await navNodeInfoResource.waitLoad(); - const expanded = elementsTree.isNodeExpanded(nodeId, true); if (!expanded && nodeId !== options.root) { if (navNodeInfoResource.isOutdated(nodeId)) { @@ -792,11 +782,6 @@ export function useElementsTree(options: IOptions): IElementsTree { ], }); - useExecutor({ - executor: projectInfoResource.onDataOutdated, - handlers: [() => navTreeResource.markOutdated(ROOT_NODE_PATH)], - }); - useExecutor({ executor: navTreeResource.onItemUpdate, handlers: [ diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts index a24e34646a..ea42dbed46 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts @@ -8,9 +8,9 @@ import { action, makeObservable } from 'mobx'; import { + type Connection, ConnectionInfoResource, ConnectionsManagerService, - createConnectionParam, type IConnectionInfoParams, NavNodeExtensionsService, } from '@cloudbeaver/core-connections'; @@ -78,10 +78,16 @@ export class NavigationTreeService extends View { async loadNestedNodes(id = ROOT_NODE_PATH, tryConnect?: boolean): Promise { if (this.isConnectionNode(id)) { - let connection = this.connectionInfoResource.getConnectionForNode(id); + const node = this.navNodeInfoResource.get(id); - if (connection) { - connection = await this.connectionInfoResource.load(createConnectionParam(connection)); + if (!node?.projectId) { + return false; + } + + const connectionParam = this.connectionInfoResource.getConnectionIdForNodeId(node.projectId, id); + let connection: Connection | undefined; + if (connectionParam) { + connection = await this.connectionInfoResource.load(connectionParam); } else { return false; } @@ -91,7 +97,7 @@ export class NavigationTreeService extends View { return false; } - const connected = await this.tryInitConnection(createConnectionParam(connection)); + const connected = await this.tryInitConnection(connectionParam); if (!connected) { return false; } From 5248447847b60e3b5dc0c822a52c0a97c28d5e4c Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Wed, 22 Jan 2025 17:47:41 +0800 Subject: [PATCH 2/2] CB-5839 fix: unlock nav resources --- .../src/ConnectionInfoResource.ts | 10 ++++------ .../src/NodesManager/DBObjectResource.ts | 10 +++++++++- .../src/NodesManager/NavTreeResource.ts | 13 +++++++++---- .../src/NodesManager/NodeManagerUtils.ts | 15 ++++++++++++--- .../src/NavigationTree/NavigationTreeService.ts | 6 ++---- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/webapp/packages/core-connections/src/ConnectionInfoResource.ts b/webapp/packages/core-connections/src/ConnectionInfoResource.ts index dab8eabbe0..3eded8489c 100644 --- a/webapp/packages/core-connections/src/ConnectionInfoResource.ts +++ b/webapp/packages/core-connections/src/ConnectionInfoResource.ts @@ -10,6 +10,7 @@ import { action, makeObservable, observable, runInAction, toJS } from 'mobx'; import { AppAuthService, UserInfoResource } from '@cloudbeaver/core-authentication'; import { injectable } from '@cloudbeaver/core-di'; import { ExecutorInterrupter, type ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; +import { NodeManagerUtils } from '@cloudbeaver/core-navigation-tree'; import { ProjectInfoResource, ProjectsService } from '@cloudbeaver/core-projects'; import { CachedMapAllKey, @@ -293,19 +294,16 @@ export class ConnectionInfoResource extends CachedMapResource -1 ? indexOfConnectionPart : nodeId.length); - - return createConnectionParam(projectId, connectionId); + return createConnectionParam(projectId, NodeManagerUtils.getConnectionId(nodeId)); } // TODO: we need here node path ie ['', 'project://', 'database://...', '...'] getConnectionForNode(nodeId: string): Connection | undefined { - if (!nodeId.startsWith('database://')) { + if (!NodeManagerUtils.isDatabaseObject(nodeId)) { return; } diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/DBObjectResource.ts b/webapp/packages/core-navigation-tree/src/NodesManager/DBObjectResource.ts index e6fe3be622..8e04a8e5ef 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/DBObjectResource.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/DBObjectResource.ts @@ -60,7 +60,6 @@ export class DBObjectResource extends CachedMapResource { }); this.beforeLoad.addHandler(async (originalKey, context) => { - await this.navTreeResource.waitLoad(); const parentKey = this.aliases.isAlias(originalKey, DBObjectParentKey); const pageKey = this.aliases.isAlias(originalKey, CachedResourceOffsetPageKey) || this.aliases.isAlias(originalKey, CachedResourceOffsetPageListKey); @@ -80,6 +79,15 @@ export class DBObjectResource extends CachedMapResource { } const key = ResourceKeyUtils.toList(this.aliases.transformToKey(originalKey)); + + for (const nodeId of key) { + const preloaded = await this.navTreeResource.preloadParents(nodeId); + + if (!preloaded) { + throw new DetailsError('Not found: ' + nodeId); + } + } + const parents = [ ...new Set( key.map(nodeId => this.navNodeInfoResource.get(nodeId)?.parentId).filter((nodeId): nodeId is string => nodeId !== undefined), diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts index 3f96e93972..9f6ab3cf8a 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NavTreeResource.ts @@ -455,17 +455,22 @@ export class NavTreeResource extends CachedMapResource { + if (!this.navNodeInfoResource.has(nodeId) && nodeId !== ROOT_NODE_PATH) { + await this.navNodeInfoResource.loadNodeParents(nodeId); + } + const parents = this.navNodeInfoResource.getParents(nodeId); + return await this.preloadNodeParents(parents, nodeId); + } + protected override async preLoadData(key: ResourceKey, contexts: IExecutionContext>): Promise { await ResourceKeyUtils.forEachAsync(key, async nodeId => { if (isResourceAlias(nodeId)) { return; } - if (!this.navNodeInfoResource.has(nodeId) && nodeId !== ROOT_NODE_PATH) { - await this.navNodeInfoResource.loadNodeParents(nodeId); - } + const preloaded = await this.preloadParents(nodeId); const parents = this.navNodeInfoResource.getParents(nodeId); - const preloaded = await this.preloadNodeParents(parents, nodeId); if (!preloaded) { const cause = new DetailsError(`Entity not found:\n"${nodeId}"\nPath:\n${parents.map(parent => `"${parent}"`).join('\n')}`); diff --git a/webapp/packages/core-navigation-tree/src/NodesManager/NodeManagerUtils.ts b/webapp/packages/core-navigation-tree/src/NodesManager/NodeManagerUtils.ts index 9b6debbfb0..d6b895fb5e 100644 --- a/webapp/packages/core-navigation-tree/src/NodesManager/NodeManagerUtils.ts +++ b/webapp/packages/core-navigation-tree/src/NodesManager/NodeManagerUtils.ts @@ -6,13 +6,22 @@ * you may not use this file except in compliance with the License. */ +const CONNECTION_NODE_ID_PREFIX = 'database://'; + export const NodeManagerUtils = { + getConnectionId(nodeId: string) { + const indexOfConnectionPart = nodeId.indexOf('/', CONNECTION_NODE_ID_PREFIX.length); + const connectionId = nodeId.slice(CONNECTION_NODE_ID_PREFIX.length, indexOfConnectionPart > -1 ? indexOfConnectionPart : nodeId.length); + + return connectionId; + }, + connectionIdToConnectionNodeId(connectionId: string): string { - return `database://${connectionId}`; + return `${CONNECTION_NODE_ID_PREFIX}${connectionId}`; }, - isDatabaseObject(objectId: string): boolean { - return objectId.startsWith('database://'); + isDatabaseObject(nodeId: string): boolean { + return nodeId.startsWith(CONNECTION_NODE_ID_PREFIX); }, concatSchemaAndCatalog(catalogId?: string, schemaId?: string): string { diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts index ea42dbed46..b442456a84 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts @@ -104,15 +104,13 @@ export class NavigationTreeService extends View { } } - await this.navTreeResource.waitLoad(); - if (tryConnect && this.navTreeResource.getException(id)) { this.navTreeResource.markOutdated(id); } - const parents = this.navNodeInfoResource.getParents(id); + const preloaded = await this.navTreeResource.preloadParents(id); - if (parents.length > 0 && !this.navNodeInfoResource.has(id)) { + if (!preloaded) { return false; }