diff --git a/webapp/packages/core-connections/src/NavTree/ConnectionNavNodeService.ts b/webapp/packages/core-connections/src/NavTree/ConnectionNavNodeService.ts index 0845edbf48..c51e55e593 100644 --- a/webapp/packages/core-connections/src/NavTree/ConnectionNavNodeService.ts +++ b/webapp/packages/core-connections/src/NavTree/ConnectionNavNodeService.ts @@ -241,7 +241,7 @@ export class ConnectionNavNodeService extends Dependency { connection = await this.connectionsManagerService.requireConnection(createConnectionParam(connection)); if (!connection?.connected) { - throw new Error('Connection not established'); + ExecutorInterrupter.interrupt(contexts); } } } diff --git a/webapp/packages/core-events/src/NotificationService.ts b/webapp/packages/core-events/src/NotificationService.ts index f65f39978a..7718d69dee 100644 --- a/webapp/packages/core-events/src/NotificationService.ts +++ b/webapp/packages/core-events/src/NotificationService.ts @@ -198,6 +198,15 @@ export class NotificationService { console.error(exception); } + throwSilently(exception: Error | GQLError | undefined | null): void { + this.logError({ + title: '', + details: exception, + isSilent: true, + }); + throw exception; + } + close(id: number, delayDeleting = true): void { // TODO: emit event or something 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 e8916572ab..ab8d236078 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/ElementsTree/useElementsTree.ts @@ -153,6 +153,15 @@ export function useElementsTree(options: IOptions): IElementsTree { options.expandStateGetters = useMemo(() => options.expandStateGetters || [], [...(options.expandStateGetters || [])]); const state = options.localState || localTreeNodesState; + async function handleLoadChildren(id: string, manual: boolean): Promise { + try { + return await options.loadChildren(id, manual); + } catch (exception: any) { + notificationService.logException(exception); + return false; + } + } + const functionsRef = useObjectRef({ async loadTree(nodeId: string) { elementsTree.loading = true; @@ -194,7 +203,7 @@ export function useElementsTree(options: IOptions): IElementsTree { return; } - const loaded = await options.loadChildren(child, false); + const loaded = await handleLoadChildren(child, false); if (!loaded) { const node = navNodeInfoResource.get(child); @@ -530,7 +539,7 @@ export function useElementsTree(options: IOptions): IElementsTree { if (!leaf && this.settings?.foldersTree && expandableOrExpanded) { const nodeId = node.id; - const loaded = await options.loadChildren(node.id, true); + const loaded = await handleLoadChildren(node.id, false); if (loaded) { this.setFilter(''); options.folderExplorer.open(path, nodeId); @@ -549,7 +558,7 @@ export function useElementsTree(options: IOptions): IElementsTree { try { if (state || (this.filtering && !treeNodeState.showInFilter)) { - state = await options.loadChildren(node.id, true); + state = await handleLoadChildren(node.id, true); } if (this.filtering) { @@ -611,7 +620,7 @@ export function useElementsTree(options: IOptions): IElementsTree { async loadPath(path: string[], lastNode?: string): Promise { let lastLoadedNode: string | undefined; for (const nodeId of path) { - const loaded = await options.loadChildren(nodeId, false); + const loaded = await handleLoadChildren(nodeId, false); if (!loaded) { return lastLoadedNode; diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts index 8e81695a92..d008deb79e 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/NavigationTreeService.ts @@ -72,53 +72,43 @@ export class NavigationTreeService extends View { await this.navNodeManagerService.navToNode(id, parentId); } - async loadNestedNodes(id = ROOT_NODE_PATH, tryConnect?: boolean, notify = true): Promise { - try { - if (this.isConnectionNode(id)) { - let connection = this.connectionInfoResource.getConnectionForNode(id); + async loadNestedNodes(id = ROOT_NODE_PATH, tryConnect?: boolean): Promise { + if (this.isConnectionNode(id)) { + let connection = this.connectionInfoResource.getConnectionForNode(id); - if (connection) { - connection = await this.connectionInfoResource.load(createConnectionParam(connection)); - } else { - return false; - } - - if (!connection.connected) { - if (!tryConnect) { - return false; - } - - try { - const connected = await this.tryInitConnection(createConnectionParam(connection)); - if (!connected) { - return false; - } - } catch { - return false; - } - } - } - - await this.navTreeResource.waitLoad(); - - if (tryConnect && this.navTreeResource.getException(id)) { - this.navTreeResource.markOutdated(id); - } - - const parents = this.navNodeInfoResource.getParents(id); - - if (parents.length > 0 && !this.navNodeInfoResource.has(id)) { + if (connection) { + connection = await this.connectionInfoResource.load(createConnectionParam(connection)); + } else { return false; } - await this.navTreeResource.load(CachedResourcePageKey(CACHED_RESOURCE_DEFAULT_PAGE_OFFSET, this.navTreeResource.childrenLimit).setTarget(id)); - return true; - } catch (exception: any) { - if (notify) { - this.notificationService.logException(exception); + if (!connection.connected) { + if (!tryConnect) { + return false; + } + + const connected = await this.tryInitConnection(createConnectionParam(connection)); + if (!connected) { + return false; + } } } - return false; + + await this.navTreeResource.waitLoad(); + + if (tryConnect && this.navTreeResource.getException(id)) { + this.navTreeResource.markOutdated(id); + } + + const parents = this.navNodeInfoResource.getParents(id); + + if (parents.length > 0 && !this.navNodeInfoResource.has(id)) { + return false; + } + + await this.navTreeResource.load(CachedResourcePageKey(CACHED_RESOURCE_DEFAULT_PAGE_OFFSET, this.navTreeResource.childrenLimit).setTarget(id)); + + return true; } selectNode(id: string, multiple?: boolean): void { diff --git a/webapp/packages/plugin-navigation-tree/src/NavigationTree/useNavigationTree.ts b/webapp/packages/plugin-navigation-tree/src/NavigationTree/useNavigationTree.ts index 3bb9aa7512..6b9fd7580f 100644 --- a/webapp/packages/plugin-navigation-tree/src/NavigationTree/useNavigationTree.ts +++ b/webapp/packages/plugin-navigation-tree/src/NavigationTree/useNavigationTree.ts @@ -7,6 +7,7 @@ */ import { useObjectRef } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; +import { NotificationService } from '@cloudbeaver/core-events'; import type { NavNode } from '@cloudbeaver/core-navigation-tree'; import { NavigationTreeService } from './NavigationTreeService'; @@ -22,13 +23,19 @@ const bindActions: Array = ['handleOpen', 'handleSelect', export function useNavigationTree(): INavigationTree { const navigationTreeService = useService(NavigationTreeService); + const notificationService = useService(NotificationService); return useObjectRef( () => ({ navigationTreeService, async handleOpen(node: NavNode, folder: boolean) { if (!folder) { - await this.navigationTreeService.navToNode(node.id, node.parentId); + try { + await this.navigationTreeService.navToNode(node.id, node.parentId); + } catch (exception: any) { + notificationService.logException(exception); + throw exception; + } } }, handleSelect(node: NavNode, state: boolean) { diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx index 779b2b245a..2918502721 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditorOverlay.tsx @@ -30,6 +30,7 @@ import { getRealExecutionContextId, } from '@cloudbeaver/core-connections'; import { useService } from '@cloudbeaver/core-di'; +import { NotificationService } from '@cloudbeaver/core-events'; import { NodeManagerUtils } from '@cloudbeaver/core-navigation-tree'; import type { ISqlEditorTabState } from './ISqlEditorTabState'; @@ -50,6 +51,7 @@ export const SqlEditorOverlay = observer(function SqlEditorOverlay({ stat const translate = useTranslate(); const sqlEditorService = useService(SqlEditorService); const sqlDataSourceService = useService(SqlDataSourceService); + const notificationService = useService(NotificationService); const dataSource = sqlDataSourceService.get(state.editorId); const executionContextId = dataSource?.executionContext?.id; const executionContext = dataSource?.executionContext; @@ -77,7 +79,11 @@ export const SqlEditorOverlay = observer(function SqlEditorOverlay({ stat } async function init() { - await sqlEditorService.initEditorConnection(state); + try { + await sqlEditorService.initEditorConnection(state); + } catch (exception: any) { + notificationService.logException(exception); + } } const dataContainer = getComputed(() =>