diff --git a/webapp/packages/authentication/src/Administration/UsersManagerService.ts b/webapp/packages/authentication/src/Administration/UsersManagerService.ts
index 4ff6569245..2d8896e110 100644
--- a/webapp/packages/authentication/src/Administration/UsersManagerService.ts
+++ b/webapp/packages/authentication/src/Administration/UsersManagerService.ts
@@ -39,7 +39,7 @@ export class UsersManagerService {
async grantRole(userId: string, roleId: string, update?: boolean) {
await this.graphQLService.gql.grantUserRole({ userId, roleId });
if (update) {
- await this.users.refresh(userId);
+ await this.users.refresh(true, userId);
}
}
@@ -50,7 +50,7 @@ export class UsersManagerService {
await this.graphQLService.gql.deleteUser({ userId });
if (update) {
- await this.users.refresh(userId);
+ await this.users.refresh(true, userId);
}
}
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx b/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx
index b7ec6f1113..619c414574 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTree.tsx
@@ -11,6 +11,7 @@ import styled, { css } from 'reshadow';
import { usePermission, EPermission } from '@dbeaver/core/root';
+import { ROOT_NODE_PATH } from '../shared/NodesManager/NavNodeManagerService';
import { useChildren } from '../shared/useChildren';
import { NavigationTreeNode } from './NavigationTreeNode/NavigationTreeNode';
@@ -46,7 +47,7 @@ export const NavigationTree = observer(function NavigationTree() {
return null;
}
- if (!nodeChildren || nodeChildren.children.length === 0) {
+ if (!nodeChildren.children || nodeChildren.children.length === 0) {
return styled(navigationTreeStyles)(
@@ -62,7 +63,7 @@ export const NavigationTree = observer(function NavigationTree() {
return styled(navigationTreeStyles)(
{nodeChildren.children.map(id => (
-
+
))}
);
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeContextMenuService.ts b/webapp/packages/core/src/app/NavigationTree/NavigationTreeContextMenuService.ts
index 34624f99b6..a4ce669e59 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeContextMenuService.ts
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeContextMenuService.ts
@@ -7,63 +7,65 @@
*/
import { injectable } from '@dbeaver/core/di';
-import {
- ContextMenuService, IContextMenuItem, IMenuContext, IMenuPanel,
-} from '@dbeaver/core/dialogs';
+import { ContextMenuService, IMenuPanel } from '@dbeaver/core/dialogs';
-import { NodesManagerService } from '../shared/NodesManager/NodesManagerService';
-import { NodeWithParent } from '../shared/NodesManager/NodeWithParent';
+import { NavNode } from '../shared/NodesManager/EntityTypes';
+import { NavNodeManagerService } from '../shared/NodesManager/NavNodeManagerService';
@injectable()
export class NavigationTreeContextMenuService {
static nodeContextType = 'NodeWithParent';
private static menuToken = 'navTreeMenu';
- constructor(private contextMenuService: ContextMenuService,
- private nodesManagerService: NodesManagerService) {
- }
+ constructor(
+ private contextMenuService: ContextMenuService,
+ private navNodeManagerService: NavNodeManagerService
+ ) { }
getMenuToken() {
return NavigationTreeContextMenuService.menuToken;
}
- constructMenuWithContext(node: NodeWithParent): IMenuPanel {
- const context: IMenuContext = {
+ constructMenuWithContext(node: NavNode): IMenuPanel {
+ return this.contextMenuService.createContextMenu({
menuId: this.getMenuToken(),
contextId: node.id,
contextType: NavigationTreeContextMenuService.nodeContextType,
data: node,
- };
- return this.contextMenuService.createContextMenu(context);
+ });
}
registerMenuItems() {
- const openNodeTab: IContextMenuItem = {
- id: 'openNodeTab',
- isPresent(context) {
- return context.contextType === NavigationTreeContextMenuService.nodeContextType;
- },
- order: 1,
- title: 'app_navigationTree_openNodeTab',
- onClick: (context: IMenuContext) => {
- const node = context.data;
- this.nodesManagerService.navToNode(node.id);
- },
- };
- this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), openNodeTab);
+ this.contextMenuService.addMenuItem(
+ this.contextMenuService.getRootMenuToken(),
+ {
+ id: 'openNodeTab',
+ isPresent(context) {
+ return context.contextType === NavigationTreeContextMenuService.nodeContextType;
+ },
+ order: 1,
+ title: 'app_navigationTree_openNodeTab',
+ onClick: (context) => {
+ const node = context.data;
+ this.navNodeManagerService.navToNode(node.id, node.parentId);
+ },
+ }
+ );
- const refreshNode: IContextMenuItem = {
- id: 'refreshNode',
- isPresent(context) {
- return context.contextType === NavigationTreeContextMenuService.nodeContextType;
- },
- order: Number.MAX_SAFE_INTEGER,
- title: 'app_navigationTree_refreshNode',
- onClick: (context: IMenuContext) => {
- const node = context.data;
- this.nodesManagerService.refreshNode(node.id);
- },
- };
- this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), refreshNode);
+ this.contextMenuService.addMenuItem(
+ this.contextMenuService.getRootMenuToken(),
+ {
+ id: 'refreshNode',
+ isPresent(context) {
+ return context.contextType === NavigationTreeContextMenuService.nodeContextType;
+ },
+ order: Number.MAX_SAFE_INTEGER,
+ title: 'app_navigationTree_refreshNode',
+ onClick: (context) => {
+ const node = context.data;
+ this.navNodeManagerService.refresh(node.id);
+ },
+ }
+ );
}
}
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNode/NavigationNode.tsx b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNode/NavigationNode.tsx
index 9c99fae994..787a9d7311 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNode/NavigationNode.tsx
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNode/NavigationNode.tsx
@@ -21,13 +21,13 @@ type NodeProps = PropsWithChildren<{
title?: string;
icon?: string;
isExpanded: boolean;
- isLoaded?: boolean;
+ isLoading: boolean;
isSelected: boolean;
portal?: JSX.Element;
isExpandable?: boolean;
- onExpand?: (id: string) => void;
- onDoubleClick?: (id: string) => void;
- onClick?: (id: string, isMultiple?: boolean) => void;
+ onExpand?: () => void;
+ onDoubleClick?: () => void;
+ onClick?: (isMultiple?: boolean) => void;
}>
const KEYCODE = {
@@ -36,23 +36,19 @@ const KEYCODE = {
DOWN: 40,
};
-export function NavigationNode(
- props: NodeProps
-) {
- const {
- id,
- title,
- icon,
- children,
- isLoaded,
- isExpanded,
- isSelected,
- portal,
- isExpandable,
- onExpand,
- onDoubleClick,
- onClick,
- } = props;
+export function NavigationNode({
+ title,
+ icon,
+ children,
+ isLoading,
+ isExpanded,
+ isSelected,
+ portal,
+ isExpandable,
+ onExpand,
+ onDoubleClick,
+ onClick,
+}: NodeProps) {
const styles = useStyles(navigationNodeStyles);
@@ -60,28 +56,19 @@ export function NavigationNode(
(e: MouseEvent) => {
e.stopPropagation();
if (onExpand) {
- onExpand(id);
+ onExpand();
}
},
- [id, onExpand]
- );
-
- const handleDoubleClick = useCallback(
- (e: MouseEvent) => {
- if (onDoubleClick) {
- onDoubleClick(id);
- }
- },
- [id, onDoubleClick]
+ [onExpand]
);
const handleClick = useCallback(
(e: MouseEvent) => {
if (onClick) {
- onClick(id, e.ctrlKey);
+ onClick(e.ctrlKey);
}
},
- [id, onClick]
+ [onClick]
);
const handleEnter = useCallback(
@@ -89,29 +76,29 @@ export function NavigationNode(
switch (event.keyCode) {
case KEYCODE.ENTER:
if (onClick) {
- onClick(id, event.ctrlKey);
+ onClick(event.ctrlKey);
}
break;
}
return true;
},
- [id, onClick]
+ [onClick]
);
return styled(styles)(
<>
-
+
- {!isLoaded && isExpanded && }
- {(isLoaded || !isExpanded) && }
+ {isLoading && }
+ {!isLoading && }
{title}
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNodeChildren.tsx b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNodeChildren.tsx
index ade15af012..30c6396598 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNodeChildren.tsx
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationNodeChildren.tsx
@@ -13,24 +13,27 @@ import { useChildren } from '../../shared/useChildren';
type NavigationNodeChildrenProps = PropsWithChildren<{
parentId: string;
- component: React.ElementType<{id: string}>;
+ component: React.ElementType<{
+ id: string;
+ parentId: string;
+ }>;
}>
export const NavigationNodeChildren = observer(function NavigationNodeChildren({
parentId,
component,
}: NavigationNodeChildrenProps) {
- const nodeChildren = useChildren(parentId);
+ const { children } = useChildren(parentId);
- if (!nodeChildren?.isLoaded) {
+ if (!children) {
return null;
}
const NavigationNodeChild = component;
return (
<>
- {nodeChildren.children!.map(id => (
-
+ {children.map(id => (
+
))}
>
);
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationTreeNode.tsx b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationTreeNode.tsx
index 5f0e962a62..593a7c3d73 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationTreeNode.tsx
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/NavigationTreeNode.tsx
@@ -15,12 +15,14 @@ import { useNavigationTree } from './useNavigationTree';
type NavigationTreeNodeProps = {
id: string;
+ parentId: string;
}
export const NavigationTreeNode = observer(function NavigationTreeNodeFn({
id,
+ parentId,
}: NavigationTreeNodeProps) {
- const node = useNavigationTree(id);
+ const node = useNavigationTree(id, parentId);
if (!node) {
return null;
@@ -33,12 +35,12 @@ export const NavigationTreeNode = observer(function NavigationTreeNodeFn({
icon={node.icon}
isSelected={node.isSelected}
isExpanded={node.isExpanded}
- isLoaded={node.isLoaded}
- isExpandable={node.hasChildren && node.isExpandable}
+ isLoading={node.isLoading}
+ isExpandable={node.isExpandable}
onExpand={node.handleExpand}
onClick={node.handleSelect}
onDoubleClick={node.handleDoubleClick}
- portal={}
+ portal={}
>
navigationTreeContextMenuService.constructMenuWithContext(node),
- [nodeId]
+ [node]
);
const isHidden = useMemo(
() => computed(() => !menuPanel.menuItems.length
|| menuPanel.menuItems.every(item => item.isHidden)),
- [nodeId]
+ [menuPanel]
);
if (isHidden.get()) {
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/useNavigationTree.ts b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/useNavigationTree.ts
index 12cae46442..47637a330a 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/useNavigationTree.ts
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeNode/useNavigationTree.ts
@@ -10,49 +10,52 @@ import { useState, useCallback, useEffect } from 'react';
import { useService } from '@dbeaver/core/di';
+import { NavNode } from '../../shared/NodesManager/EntityTypes';
import { EObjectFeature } from '../../shared/NodesManager/EObjectFeature';
-import { NodesManagerService } from '../../shared/NodesManager/NodesManagerService';
-import { NodeWithParent } from '../../shared/NodesManager/NodeWithParent';
+import { NavNodeManagerService } from '../../shared/NodesManager/NavNodeManagerService';
import { useNode } from '../../shared/NodesManager/useNode';
import { useChildren } from '../../shared/useChildren';
import { NavigationTreeService } from '../NavigationTreeService';
-export function useNavigationTree(nodeId: string) {
+export function useNavigationTree(nodeId: string, parentId: string) {
const navigationTreeService = useService(NavigationTreeService);
- const nodesManagerService = useService(NodesManagerService);
+ const navNodeManagerService = useService(NavNodeManagerService);
const [isExpanded, switchExpand] = useState(false);
const [isSelected, switchSelect] = useState(false);
- const node = useNode(nodeId);
+ const { node, isLoaded: nodeLoaded } = useNode(nodeId);
const children = useChildren(nodeId);
if (!node) {
return undefined;
}
- const isLoaded = children?.isLoaded;
- const isExpandable = isExpandableFilter(node) && (!children?.isLoaded || children.children.length > 0);
+ const isLoaded = children.isLoaded;
+ const isExpandable = isExpandableFilter(node) && (!isLoaded || children.children!.length > 0);
const handleDoubleClick = useCallback(
- (nodeId: string) => nodesManagerService.navToNode(nodeId),
- [nodesManagerService]
+ () => navNodeManagerService.navToNode(nodeId, parentId),
+ [navNodeManagerService, nodeId, parentId]
);
const handleExpand = useCallback(
- (id: string) => {
+ async () => {
if (!isExpanded) {
- navigationTreeService.loadNestedNodes(id).then(state => !state && switchExpand(false));
+ const state = await navigationTreeService.loadNestedNodes(nodeId);
+ if (!state) {
+ switchExpand(false);
+ }
}
switchExpand(!isExpanded);
},
- [isExpanded]
+ [isExpanded, nodeId]
);
const handleSelect = useCallback(
- (id: string, isMultiple?: boolean) => {
- switchSelect(navigationTreeService.selectNode(id, isMultiple));
+ (isMultiple?: boolean) => {
+ switchSelect(navigationTreeService.selectNode(nodeId, isMultiple));
},
- [isSelected]
+ [isSelected, nodeId]
);
const {
@@ -65,17 +68,25 @@ export function useNavigationTree(nodeId: string) {
}
}, [isExpandable && hasChildren]);
+ useEffect(() => {
+ if (isExpanded && !children.isLoaded && !children.isLoading && !!children.children && nodeLoaded) {
+ navigationTreeService.loadNestedNodes(nodeId);
+ }
+ }, [isExpanded, children.isLoaded, children.isLoading, children.children, nodeLoaded, nodeId]);
+
// Here we subscribe to selected nodes if current node selected (mobx)
- if (isSelected && !navigationTreeService.selectedNodes.includes(node.id)) {
+ if (isSelected && !navigationTreeService.selectedNodes.includes(nodeId)) {
switchSelect(false);
}
return {
name,
+ node,
nodeType,
icon,
isExpanded,
isLoaded,
+ isLoading: children.isLoading,
isExpandable,
isSelected,
hasChildren,
@@ -85,6 +96,6 @@ export function useNavigationTree(nodeId: string) {
};
}
-export function isExpandableFilter(node: NodeWithParent) {
- return !node.object?.features?.find(feature => feature === EObjectFeature.entity);
+export function isExpandableFilter(node: NavNode) {
+ return node.hasChildren && !node.objectFeatures.includes(EObjectFeature.entity);
}
diff --git a/webapp/packages/core/src/app/NavigationTree/NavigationTreeService.ts b/webapp/packages/core/src/app/NavigationTree/NavigationTreeService.ts
index b5563effe6..c68fb25492 100644
--- a/webapp/packages/core/src/app/NavigationTree/NavigationTreeService.ts
+++ b/webapp/packages/core/src/app/NavigationTree/NavigationTreeService.ts
@@ -11,23 +11,22 @@ import { observable } from 'mobx';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
-import { NodesManagerService } from '../shared/NodesManager/NodesManagerService';
+import { NavNodeManagerService, ROOT_NODE_PATH } from '../shared/NodesManager/NavNodeManagerService';
@injectable()
export class NavigationTreeService {
selectedNodes = observable.array([]);
- constructor(private nodesManagerService: NodesManagerService,
- private notificationService: NotificationService) {
- }
+ constructor(
+ private NavNodeManagerService: NavNodeManagerService,
+ private notificationService: NotificationService
+ ) { }
- async loadNestedNodes(id = '/') {
+ async loadNestedNodes(id = ROOT_NODE_PATH) {
try {
- const children = await this.nodesManagerService.loadChildren(id);
- if (children) {
- return true;
- }
+ await this.NavNodeManagerService.loadTree(id);
+ return true;
} catch (exception) {
this.notificationService.logException(exception, `Can't load tree node: ${id}`);
}
diff --git a/webapp/packages/core/src/app/index.ts b/webapp/packages/core/src/app/index.ts
index 306e139230..e3d6387a31 100644
--- a/webapp/packages/core/src/app/index.ts
+++ b/webapp/packages/core/src/app/index.ts
@@ -15,15 +15,16 @@ export * from './shared/NodesManager/extensions/IObjectCatalogProvider';
export * from './shared/NodesManager/extensions/IObjectCatalogSetter';
export * from './shared/NodesManager/extensions/IObjectSchemaProvider';
export * from './shared/NodesManager/extensions/IObjectSchemaSetter';
+export * from './shared/NodesManager/DBObjectService';
+export * from './shared/NodesManager/NavNodeManagerService';
export * from './shared/NodesManager/NodeManagerUtils';
export * from './shared/Navigation/NavigationService';
export * from './shared/NavigationTabs/NavigationTabsService';
export * from './shared/NavigationTabs/TabNavigationContext';
-export * from './shared/NodesManager/NodesManagerService';
-export * from './NavigationTree/NavigationTreeService';
-export * from './NavigationTree/NavigationTreeContextMenuService';
export * from './shared/ToolsPanel/LogViewTab/LogViewerMenuService';
export * from './shared/ToolsPanel/LogViewTab/LogViewerService';
+export * from './NavigationTree/NavigationTreeService';
+export * from './NavigationTree/NavigationTreeContextMenuService';
export * from './Notifications/NotificationsItem/Snackbar/styles';
export * from './Notifications/NotificationsItem/Snackbar/NotificationMark';
@@ -33,7 +34,8 @@ export * from './shared/NavigationTabs/TabHandler';
export * from './shared/Navigation/NavigationContext';
export * from './shared/TabEntity/TabEntity';
// Enums
-// export * from '';
+export * from './shared/NodesManager/ENodeFeature';
+export * from './shared/NodesManager/EObjectFeature';
// hooks
export * from './shared/useChildren';
@@ -50,10 +52,8 @@ export * from './TopNavBar/Logo';
// Interfaces
export * from './shared/Navigation/INavigator';
-export * from './shared/NodesManager/ENodeFeature';
-export * from './shared/NodesManager/EObjectFeature';
export * from './shared/NodesManager/NodeManagerUtils';
-export * from './shared/NodesManager/NodeWithParent';
+export * from './shared/NodesManager/EntityTypes';
// tab entity
export * from './shared/TabEntity/TabContainerEntity';
diff --git a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionDialogsService.ts b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionDialogsService.ts
index aa760a237f..baeb175ad1 100644
--- a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionDialogsService.ts
+++ b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionDialogsService.ts
@@ -15,9 +15,9 @@ import {
import { NavigationTreeContextMenuService } from '../../NavigationTree/NavigationTreeContextMenuService';
import { EMainMenu, MainMenuService } from '../../TopNavBar/MainMenu/MainMenuService';
+import { NavNode } from '../NodesManager/EntityTypes';
import { EObjectFeature } from '../NodesManager/EObjectFeature';
import { NodeManagerUtils } from '../NodesManager/NodeManagerUtils';
-import { NodeWithParent } from '../NodesManager/NodeWithParent';
import { ConnectionsManagerService } from './ConnectionsManagerService';
@@ -52,20 +52,20 @@ export class ConnectionDialogsService {
}
);
- const closeConnection: IContextMenuItem = {
+ const closeConnection: IContextMenuItem = {
id: 'closeConnection',
isPresent(context) {
return context.contextType === NavigationTreeContextMenuService.nodeContextType
- && !!context.data.object?.features?.includes(EObjectFeature.dataSource);
+ && !!context.data.objectFeatures.includes(EObjectFeature.dataSource);
},
title: 'Disconnect',
- onClick: (context: IMenuContext) => {
+ onClick: (context: IMenuContext) => {
const node = context.data;
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(node.id);
this.connectionsManagerService.closeConnectionAsync(connectionId);
},
};
- this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), closeConnection);
+ this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), closeConnection);
}
}
diff --git a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts
index 41b55991ed..1f1fe2208d 100644
--- a/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts
+++ b/webapp/packages/core/src/app/shared/ConnectionsManager/ConnectionsManagerService.ts
@@ -20,7 +20,7 @@ import {
DatabaseObjectInfo,
} from '@dbeaver/core/sdk';
-import { NodesManagerService } from '../NodesManager/NodesManagerService';
+import { NavNodeManagerService } from '../NodesManager/NavNodeManagerService';
export type DBDriver = Pick<
DriverInfo,
@@ -67,7 +67,7 @@ export class ConnectionsManagerService {
constructor(
private graphQLService: GraphQLService,
- private nodesManagerService: NodesManagerService,
+ private navNodeManagerService: NavNodeManagerService,
private sessionService: SessionService
) {
this.sessionService.onUpdate.subscribe(this.restoreConnections.bind(this));
@@ -81,10 +81,10 @@ export class ConnectionsManagerService {
return this.dbDrivers.load();
}
- addOpenedConnection(connection: Connection) {
+ async addOpenedConnection(connection: Connection) {
this.connectionsMap.set(connection.id, connection);
this.onOpenConnection.next(connection);
- this.nodesManagerService.updateRootChildren(); // Update connections list, probably here we must also request node info and add it to nodes manager
+ await this.navNodeManagerService.updateRootChildren(); // Update connections list, probably here we must also request node info and add it to nodes manager
}
getConnectionById(connectionId: string): Connection | undefined {
@@ -113,7 +113,7 @@ export class ConnectionsManagerService {
for (const connection of this.connections) {
await this.closeConnectionAsync(connection.id, true);
}
- await this.nodesManagerService.updateRootChildren();
+ await this.navNodeManagerService.updateRootChildren();
}
async closeConnectionAsync(id: string, skipNodesRefresh?: boolean): Promise {
@@ -122,7 +122,7 @@ export class ConnectionsManagerService {
this.connectionsMap.delete(id);
if (!skipNodesRefresh) {
- await this.nodesManagerService.updateRootChildren(); // Update connections list, probably here we must just remove nodes from nodes manager
+ await this.navNodeManagerService.updateRootChildren(); // Update connections list, probably here we must just remove nodes from nodes manager
}
}
@@ -132,7 +132,7 @@ export class ConnectionsManagerService {
}
private async afterConnectionClose(id: string) {
- await this.nodesManagerService.closeConnection(id);
+ await this.navNodeManagerService.remove(id);
this.onCloseConnection.next(id);
}
@@ -154,7 +154,7 @@ export class ConnectionsManagerService {
this.connectionsMap.delete(connection.id);
}
- await this.nodesManagerService.updateRootChildren();
+ await this.navNodeManagerService.updateRootChildren();
}
private isObjectContainersLoaded(
diff --git a/webapp/packages/core/src/app/shared/NavigationTabs/ITab.ts b/webapp/packages/core/src/app/shared/NavigationTabs/ITab.ts
index d336f9f112..47429dbac7 100644
--- a/webapp/packages/core/src/app/shared/NavigationTabs/ITab.ts
+++ b/webapp/packages/core/src/app/shared/NavigationTabs/ITab.ts
@@ -14,4 +14,5 @@ export interface ITabOptions {
export interface ITab extends ITabOptions {
id: string;
+ restored: boolean;
}
diff --git a/webapp/packages/core/src/app/shared/NavigationTabs/NavigationTabsService.ts b/webapp/packages/core/src/app/shared/NavigationTabs/NavigationTabsService.ts
index 85d5fb9562..0a64fec389 100644
--- a/webapp/packages/core/src/app/shared/NavigationTabs/NavigationTabsService.ts
+++ b/webapp/packages/core/src/app/shared/NavigationTabs/NavigationTabsService.ts
@@ -67,6 +67,7 @@ export class NavigationTabsService {
typeof value.id === 'string'
&& typeof value.handlerId === 'string'
) {
+ value.restored = false;
map[key] = value;
}
}
@@ -86,7 +87,7 @@ export class NavigationTabsService {
}
}
- @action selectTab(tabId: string, skipHandlers?: boolean) {
+ @action async selectTab(tabId: string, skipHandlers?: boolean) {
const tab = this.tabsMap.get(tabId);
if (!tab) {
return;
@@ -99,7 +100,7 @@ export class NavigationTabsService {
}
if (!skipHandlers) {
- this.callHandlerCallback(tab, handler => handler.onSelect);
+ await this.callHandlerCallback(tab, handler => handler.onSelect);
}
this.tabSelectSubject.next(tab);
@@ -177,7 +178,6 @@ export class NavigationTabsService {
// must be executed with low priority, because this call runs many requests to backend and blocks others
async restoreTabs() {
const removedTabs: string[] = [];
- const restoreTasks: Promise[] = [];
const session = await this.sessionService.session.load();
for (const tabId of this.state.tabs) {
@@ -192,10 +192,9 @@ export class NavigationTabsService {
continue;
}
- restoreTasks.push(this.restoreTab(tab, removedTabs));
+ await this.restoreTab(tab, removedTabs);
}
- await Promise.all(restoreTasks);
if (removedTabs.length > 0) {
this.notificationService.logError({ title: 'Some tabs cannot be restored properly', isSilent: true });
@@ -237,6 +236,8 @@ export class NavigationTabsService {
if (restoreFail) {
removedTabs.push(tab.id);
+ } else {
+ tab.restored = true;
}
}
}
diff --git a/webapp/packages/core/src/app/shared/NavigationTabs/TabNavigationContext.ts b/webapp/packages/core/src/app/shared/NavigationTabs/TabNavigationContext.ts
index 7bf1807251..1e9fa6e8f5 100644
--- a/webapp/packages/core/src/app/shared/NavigationTabs/TabNavigationContext.ts
+++ b/webapp/packages/core/src/app/shared/NavigationTabs/TabNavigationContext.ts
@@ -40,6 +40,7 @@ export class TabNavigationContext implements ITabNavigationContext {
openNewTab(options: ITabOptions): ITab {
this._tab = observable({
id: uuid(),
+ restored: true,
...options,
});
this._isNewlyCreated = true;
diff --git a/webapp/packages/core/src/app/shared/NodesManager/DBObjectService.ts b/webapp/packages/core/src/app/shared/NodesManager/DBObjectService.ts
new file mode 100644
index 0000000000..96cd3d1e0e
--- /dev/null
+++ b/webapp/packages/core/src/app/shared/NodesManager/DBObjectService.ts
@@ -0,0 +1,88 @@
+/*
+ * cloudbeaver - Cloud Database Manager
+ * Copyright (C) 2020 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { injectable } from '@dbeaver/core/di';
+import { GraphQLService, CachedResource } from '@dbeaver/core/sdk';
+import { MetadataMap } from '@dbeaver/core/utils';
+
+import { DBObject } from './EntityTypes';
+
+export interface IDBObjectParams {
+ navNodeId: string[];
+ remove?: boolean;
+}
+
+interface IDBObjectMetadata {
+ loaded: boolean;
+ loading: boolean;
+}
+
+@injectable()
+export class DBObjectService {
+ readonly dbObject = new CachedResource(
+ new Map(),
+ this.loadDBObject.bind(this),
+ (_, metadata, { navNodeId }) => navNodeId.every(navNodeId => metadata.get(navNodeId).loaded),
+ new MetadataMap(() => ({ loaded: false, loading: false })),
+ (_, metadata, { navNodeId }) => navNodeId.some(navNodeId => metadata.get(navNodeId).loading)
+ )
+
+ constructor(private graphQLService: GraphQLService) { }
+
+ getDBObject(navNodeId: string) {
+ return this.dbObject.data.get(navNodeId);
+ }
+
+ async load(navNodeId: string): Promise< DBObject>
+ async load(navNodeId: string[]): Promise
+ async load(navNodeId: string | string[]): Promise {
+ const dbObject = await this.dbObject.load({ navNodeId: Array.isArray(navNodeId) ? navNodeId : [navNodeId] });
+
+ if (!Array.isArray(navNodeId)) {
+ return dbObject.get(navNodeId)!;
+ }
+ return navNodeId.map(navNodeId => dbObject.get(navNodeId)!);
+ }
+
+ async remove(navNodeId: string[]) {
+ await this.dbObject.refresh(true, { navNodeId, remove: true });
+ }
+
+ private async loadDBObject(
+ dbObject: Map,
+ metadata: MetadataMap,
+ load: boolean,
+ data: IDBObjectParams
+ ) {
+ for (const navNodeId of data.navNodeId) {
+ if (data.remove) {
+ dbObject.delete(navNodeId);
+ metadata.delete(navNodeId);
+ } else {
+ const itemMetadata = metadata.get(navNodeId);
+
+ itemMetadata.loaded = false;
+ if (load) {
+ itemMetadata.loading = true;
+
+ try {
+ const { objectInfo: { object } } = await this.graphQLService.gql.getDBObjectInfo({
+ navNodeId,
+ });
+
+ dbObject.set(navNodeId, { navNodeId, ...object } as DBObject);
+ itemMetadata.loaded = true;
+ } finally {
+ itemMetadata.loading = false;
+ }
+ }
+ }
+ }
+ return dbObject;
+ }
+}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/EntityTypes.ts b/webapp/packages/core/src/app/shared/NodesManager/EntityTypes.ts
new file mode 100644
index 0000000000..9e82e43168
--- /dev/null
+++ b/webapp/packages/core/src/app/shared/NodesManager/EntityTypes.ts
@@ -0,0 +1,20 @@
+/*
+ * cloudbeaver - Cloud Database Manager
+ * Copyright (C) 2020 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { DatabaseObjectInfo, NavigatorNodeInfo } from '@dbeaver/core/sdk';
+
+export type NavNodeInfo = NavigatorNodeInfo;
+
+export type NavNode = Omit & {
+ parentId: string;
+ objectFeatures: string[];
+}
+
+export type DBObject = DatabaseObjectInfo & {
+ navNodeId: string;
+}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/NavNodeManagerService.ts b/webapp/packages/core/src/app/shared/NodesManager/NavNodeManagerService.ts
new file mode 100644
index 0000000000..f94115a74f
--- /dev/null
+++ b/webapp/packages/core/src/app/shared/NodesManager/NavNodeManagerService.ts
@@ -0,0 +1,489 @@
+/*
+ * cloudbeaver - Cloud Database Manager
+ * Copyright (C) 2020 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0.
+ * you may not use this file except in compliance with the License.
+ */
+
+import { injectable } from '@dbeaver/core/di';
+import { PermissionsService, EPermission } from '@dbeaver/core/root';
+import { GraphQLService, CachedResource } from '@dbeaver/core/sdk';
+import { MetadataMap } from '@dbeaver/core/utils';
+
+import { INavigator } from '../Navigation/INavigator';
+import { IContextProvider } from '../Navigation/NavigationContext';
+import { NavigationService } from '../Navigation/NavigationService';
+import { DBObjectService } from './DBObjectService';
+import { ENodeFeature } from './ENodeFeature';
+import { NavNodeInfo, NavNode } from './EntityTypes';
+import { EObjectFeature } from './EObjectFeature';
+import { NodeManagerUtils } from './NodeManagerUtils';
+
+export enum NavigationType {
+ open,
+ closeConnection
+}
+
+export interface NavNodeKey {
+ nodeId: string;
+ parentId: string;
+}
+
+export interface NavNodeValue {
+ node: NavNodeInfo;
+ parentId: string;
+}
+
+export interface INodeContainerInfo {
+ connectionId?: string;
+ catalogId?: string;
+ schemaId?: string;
+}
+
+export interface INavNodePath {
+ nodes: NavNodeKey[];
+ remove?: boolean;
+ navNodeId?: never;
+ nodesValue?: never;
+}
+
+export interface INavNodeData {
+ nodesValue: NavNodeValue[];
+ navNodeId?: never;
+ nodes?: never;
+ remove?: never;
+}
+
+export interface INavNodeId {
+ navNodeId: string[];
+ remove?: boolean;
+ nodes?: never;
+ nodesValue?: never;
+}
+
+export interface INodeNavigationContext {
+ type: NavigationType;
+ nodeId: string;
+ parentId: string;
+ folderId: string;
+ name?: string;
+ icon?: string;
+ getParents: () => string[];
+ loadParents: (parents: string[]) => Promise;
+}
+
+export interface INodeNavigationData {
+ type: NavigationType;
+ nodeId: string;
+ parentId: string;
+ folderId?: string;
+}
+
+interface INavNodeMetadata {
+ loaded: boolean;
+ loading: boolean;
+}
+
+interface INavTreeMetadata {
+ loaded: boolean;
+ loading: boolean;
+}
+
+export const ROOT_NODE_PATH = '/';
+
+@injectable()
+export class NavNodeManagerService {
+ readonly navNode = new CachedResource(
+ new Map(),
+ this.loadNavNodeInfo.bind(this),
+ (_, metadata, data) => {
+ if (data.nodesValue) {
+ return false;
+ }
+
+ const nodesId = data.navNodeId
+ ? data.navNodeId
+ : data.nodes.map(node => node.nodeId);
+
+ return nodesId.every(navNodeId => metadata.get(navNodeId).loaded);
+ },
+ new MetadataMap(() => ({ loaded: false, loading: false })),
+ (_, metadata, data) => {
+ if (data.nodesValue) {
+ return false;
+ }
+
+ const nodesId = data.navNodeId
+ ? data.navNodeId
+ : data.nodes.map(node => node.nodeId);
+
+ return nodesId.some(navNodeId => metadata.get(navNodeId).loading);
+ }
+ );
+ readonly navTree = new CachedResource(
+ new Map(),
+ this.loadNavTree.bind(this),
+ (_, metadata, parentId) => metadata.get(parentId).loaded,
+ new MetadataMap(() => ({ loaded: false, loading: false })),
+ (_, metadata, parentId) => metadata.get(parentId).loading
+ )
+ readonly navigator!: INavigator;
+
+ constructor(
+ private graphQLService: GraphQLService,
+ private navigationService: NavigationService,
+ private permissionsService: PermissionsService,
+ private dbObjectService: DBObjectService,
+ ) {
+
+ this.navigator = this.navigationService.createNavigator(
+ data => data.nodeId,
+ this.navigateHandler.bind(this),
+ {
+ type: NavigationType.open,
+ nodeId: ROOT_NODE_PATH,
+ parentId: ROOT_NODE_PATH,
+ }
+ );
+ }
+
+ async navToNode(nodeId: string, parentId: string, folderId?: string) {
+ await this.navigator.navigateTo({
+ type: NavigationType.open,
+ nodeId,
+ parentId,
+ folderId,
+ });
+ }
+
+ async refresh(navNodeId: string) {
+ await this.graphQLService.gql.navRefreshNode({
+ nodePath: navNodeId,
+ });
+ await this.navTree.refresh(true, navNodeId, false);
+ }
+
+ async updateRootChildren() {
+ if (!await this.permissionsService.hasAsync(EPermission.public)) {
+ return;
+ }
+ await this.navTree.refresh(true, ROOT_NODE_PATH, false);
+ }
+
+ getTree(navNodeId: string): string[] | undefined
+ getTree(navNodeKey: NavNodeKey): string[] | undefined
+ getTree(navNodeKey: NavNodeKey[]): (string[] | undefined)[]
+ getTree(navNodeId: string | NavNodeKey | NavNodeKey[]) {
+ if (typeof navNodeId === 'string') {
+ return this.navTree.data.get(navNodeId);
+ }
+
+ if (Array.isArray(navNodeId)) {
+ return navNodeId.map(node => this.navTree.data.get(node.nodeId));
+ }
+
+ return this.navTree.data.get(navNodeId.nodeId);
+ }
+
+ async loadTree(navNodeId: string) {
+ await this.navTree.load(navNodeId, false);
+ return this.getTree(navNodeId)!;
+ }
+
+ getNode(navNodeId: string): NavNode | undefined
+ getNode(navNodeKey: NavNodeKey): NavNode | undefined
+ getNode(navNodeKey: NavNodeKey[]): (NavNode | undefined)[]
+ getNode(navNodeId: string | NavNodeKey | NavNodeKey[]) {
+ if (typeof navNodeId === 'string') {
+ return this.navNode.data.get(navNodeId);
+ }
+
+ if (Array.isArray(navNodeId)) {
+ return navNodeId.map(node => this.navNode.data.get(node.nodeId));
+ }
+
+ return this.navNode.data.get(navNodeId.nodeId);
+ }
+
+ getNestedChildren(navNode: string | string[]) {
+ const nestedChildren: string[] = [];
+ let prevChildren: string[];
+ if (Array.isArray(navNode)) {
+ prevChildren = navNode.concat();
+ nestedChildren.push(...navNode);
+ } else {
+ prevChildren = (this.getTree(navNode)?.concat() || []);
+ nestedChildren.push(...prevChildren);
+ }
+
+ while (prevChildren.length) {
+ const nodeKey = prevChildren.shift()!;
+ const children = this.getTree(nodeKey) || [];
+ prevChildren.push(...children);
+ nestedChildren.push(...children);
+ }
+
+ return nestedChildren;
+ }
+
+ async loadNode(node: NavNodeKey): Promise
+ async loadNode(...nodes: NavNodeKey[]): Promise
+ async loadNode(...nodes: NavNodeKey[]) {
+ await this.navNode.load({ nodes });
+
+ if (nodes.length === 1) {
+ return this.getNode(nodes[0])!;
+ }
+
+ return this.getNode(nodes);
+ }
+
+ getParent(node: NavNode) {
+ return this.navNode.data.get(node.parentId);
+ }
+
+ isNodeHasData(node?: string | NavNode) {
+ if (typeof node === 'string') {
+ node = this.getNode(node);
+ }
+
+ if (!node || !node.objectFeatures) {
+ return false;
+ }
+
+ return node.objectFeatures.includes(ENodeFeature.dataContainer)
+ || node.objectFeatures.includes(ENodeFeature.container);
+ }
+
+ getNodeContainerInfo(nodeId: string): INodeContainerInfo {
+ const initial: INodeContainerInfo = {};
+
+ const scanParents = (res: INodeContainerInfo,
+ nodeId?: string): INodeContainerInfo => {
+ if (!nodeId) {
+ return res;
+ }
+ const object = this.getNode(nodeId);
+ if (!object) {
+ return res;
+ }
+ if (object.objectFeatures.includes(EObjectFeature.dataSource)) {
+ res.connectionId = object.id;
+ }
+ if (object.objectFeatures.includes(EObjectFeature.catalog)) {
+ res.catalogId = object.name; // note that catalogId is node name
+ }
+ if (object.objectFeatures.includes(EObjectFeature.schema)) {
+ res.schemaId = object.name; // note that schemaId is node name
+ }
+ return scanParents(res, object.parentId);
+ };
+
+ return scanParents(initial, nodeId);
+ }
+
+ async remove(path = ROOT_NODE_PATH) {
+ await this.navTree.refresh(true, path, true);
+ }
+
+ navigationNavNodeContext = async (
+ contexts: IContextProvider,
+ data: INodeNavigationData
+ ): Promise => {
+ let nodeId = data.nodeId;
+ let parentId = data.parentId;
+ let folderId = '';
+ let name: string | undefined;
+ let icon: string | undefined;
+
+ if (NodeManagerUtils.isDatabaseObject(nodeId) && data.type !== NavigationType.closeConnection) {
+ const node = await this.loadNode({ nodeId, parentId });
+
+ name = node.name;
+ icon = node.icon;
+
+ if (node.folder) {
+ const parent = this.getNode(node.parentId);
+ folderId = nodeId;
+ if (parent && !parent.folder) {
+ nodeId = parent.id;
+ parentId = parent.parentId;
+ name = parent.name;
+ icon = parent.icon;
+ }
+ }
+
+ if (data.folderId) {
+ folderId = data.folderId;
+ }
+ }
+
+ const getParents = () => {
+ const parents: string[] = [];
+ let parent = this.getNode(nodeId);
+
+ while (parent && parent.parentId !== ROOT_NODE_PATH) {
+ parents.unshift(parent.parentId);
+ parent = this.getNode(parent.parentId);
+ }
+
+ return parents;
+ };
+
+ const loadParents = async (parents: string[]) => {
+ let parentId = ROOT_NODE_PATH;
+
+ await this.navNode.load({
+ nodes: parents.map((nodeId) => {
+ try {
+ return { nodeId, parentId };
+ } finally {
+ parentId = nodeId;
+ }
+ }),
+ });
+ };
+
+ return {
+ type: data.type,
+ nodeId,
+ parentId,
+ folderId,
+ name,
+ icon,
+ getParents,
+ loadParents,
+ };
+ }
+
+ private async navigateHandler(contexts: IContextProvider) {
+ }
+
+ private async loadNavNodeInfo(
+ navNode: Map,
+ metadata: MetadataMap,
+ load: boolean,
+ data: INavNodePath | INavNodeData | INavNodeId,
+ ) {
+ if (data.nodesValue) {
+ for (const nodeValue of data.nodesValue) {
+ const itemMetadata = metadata.get(nodeValue.node.id);
+ navNode.set(nodeValue.node.id, {
+ ...nodeValue.node,
+ objectFeatures: nodeValue.node.object?.features || [],
+ parentId: nodeValue.parentId,
+ });
+ itemMetadata.loaded = true;
+ }
+ return navNode;
+ }
+
+ const nodesId = data.navNodeId
+ ? data.navNodeId
+ : data.nodes.map(node => node.nodeId);
+
+ if (data.remove) {
+ for (const navNodeId of nodesId) {
+ navNode.delete(navNodeId);
+ metadata.delete(navNodeId);
+ }
+
+ await this.dbObjectService.remove(nodesId);
+ return navNode;
+ }
+
+ for (const navNodeId of nodesId) {
+ const itemMetadata = metadata.get(navNodeId);
+
+ try {
+ itemMetadata.loaded = false;
+ if (load) {
+ itemMetadata.loading = true;
+ const { navNodeInfo } = await this.graphQLService.gql.navNodeInfo({
+ nodePath: navNodeId,
+ });
+
+ let parentId = ROOT_NODE_PATH;
+ if (data.navNodeId) {
+ parentId = navNode.get(navNodeId)?.parentId || parentId;
+ } else {
+ parentId = data.nodes.find(node => node.nodeId === navNodeId)?.parentId || parentId;
+ }
+
+ navNode.set(navNodeId, {
+ ...navNodeInfo,
+ objectFeatures: navNodeInfo.object?.features || [],
+ parentId,
+ });
+ itemMetadata.loaded = true;
+ }
+ } finally {
+ itemMetadata.loading = false;
+ }
+ }
+
+ await this.dbObjectService.dbObject.refresh(false, { navNodeId: nodesId });
+
+ return navNode;
+ }
+
+ private async loadNavTree(
+ navTree: Map,
+ metadata: MetadataMap,
+ load: boolean,
+ parentId: string,
+ remove: boolean
+ ) {
+ const itemMetadata = metadata.get(parentId);
+ let childrenToRemove = navTree.get(parentId)?.concat() || [];
+
+ if (remove) {
+ childrenToRemove = [parentId];
+ } else {
+ try {
+ itemMetadata.loaded = false;
+ if (load) {
+ itemMetadata.loading = true;
+
+ const { navNodeChildren } = await this.graphQLService.gql.navNodeChildren({
+ parentPath: parentId,
+ });
+
+ await this.navNode.refresh(true, {
+ nodesValue: navNodeChildren.map(node => ({ node, parentId })),
+ });
+ navTree.set(parentId, navNodeChildren.map(node => node.id));
+ itemMetadata.loaded = true;
+ }
+ } finally {
+ itemMetadata.loading = false;
+ }
+
+ const newChildren = navTree.get(parentId);
+ const nestedChildren = this.getNestedChildren(parentId);
+
+ await this.navNode.refresh(false, {
+ navNodeId: nestedChildren.filter(navNodeId => !newChildren || !newChildren.includes(navNodeId)),
+ });
+
+ for (const navNodeId of nestedChildren) {
+ const itemMetadata = metadata.get(navNodeId);
+ itemMetadata.loaded = false;
+ }
+
+ if (newChildren) {
+ childrenToRemove = childrenToRemove.filter(navNodeId => !newChildren.includes(navNodeId));
+ }
+ }
+
+ const nestedChildren = this.getNestedChildren(childrenToRemove);
+ await this.navNode.refresh(true, { navNodeId: nestedChildren, remove: true });
+ for (const navNodeId of nestedChildren) {
+ navTree.delete(navNodeId);
+ metadata.delete(navNodeId);
+ }
+
+ return navTree;
+ }
+}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/NodeWithParent.ts b/webapp/packages/core/src/app/shared/NodesManager/NodeWithParent.ts
deleted file mode 100644
index d463b53fdc..0000000000
--- a/webapp/packages/core/src/app/shared/NodesManager/NodeWithParent.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * cloudbeaver - Cloud Database Manager
- * Copyright (C) 2020 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { DatabaseObjectInfo, NavigatorNodeInfo } from '@dbeaver/core/sdk';
-
-// Node Info without ObjectProperties
-export type NavigatorNodeInfoLight = Omit & {
- object?: {
- features?: string[]; // EObjectFeature
- };
-}
-
-export type NodeWithParent = NavigatorNodeInfoLight & {
- parentId: string;
-}
-
-export type DatabaseObjectInfoWithId = DatabaseObjectInfo & {
- id: string;
-}
-
-export type NodeChildren = {
- children: string[];
- isLoaded: boolean;
-}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts b/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts
deleted file mode 100644
index f0e56a9096..0000000000
--- a/webapp/packages/core/src/app/shared/NodesManager/NodesManagerService.ts
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * cloudbeaver - Cloud Database Manager
- * Copyright (C) 2020 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { injectable } from '@dbeaver/core/di';
-import { PermissionsService, EPermission } from '@dbeaver/core/root';
-import { DatabaseObjectInfo, GraphQLService } from '@dbeaver/core/sdk';
-
-import { INavigator } from '../Navigation/INavigator';
-import { IContextProvider } from '../Navigation/NavigationContext';
-import { NavigationService } from '../Navigation/NavigationService';
-import { ENodeFeature } from './ENodeFeature';
-import { EObjectFeature } from './EObjectFeature';
-import { NodesStore } from './NodesStore';
-import { DatabaseObjectInfoWithId, NodeChildren, NodeWithParent } from './NodeWithParent';
-
-export enum NavigationType {
- open,
- closeConnection
-}
-
-export interface INodeContainerInfo {
- connectionId?: string;
- catalogId?: string;
- schemaId?: string;
-}
-
-export interface INodeNavigationContext {
- type: NavigationType;
- nodeId: string;
- folderId: string;
- name?: string;
- icon?: string;
-}
-
-export interface INodeNavigationData {
- type: NavigationType;
- nodeId: string;
- folderId?: string;
-}
-
-const ROOT_NODE_PATH = '/';
-
-// TODO: should be renamed to DBObjectManagerService
-@injectable()
-export class NodesManagerService {
- readonly navigator!: INavigator;
- private nodesStore = new NodesStore();
-
- constructor(private graphQLService: GraphQLService,
- private navigationService: NavigationService,
- private permissionsService: PermissionsService) {
-
- this.navigator = this.navigationService.createNavigator(
- data => data.nodeId,
- this.navigateHandler.bind(this),
- {
- type: NavigationType.open,
- nodeId: ROOT_NODE_PATH,
- }
- );
- }
-
- async closeConnection(nodeId: string) {
- await this.navigator.navigateTo({
- type: NavigationType.closeConnection,
- nodeId,
- });
- }
-
- async navToNode(nodeId: string, folderId?: string) {
- await this.navigator.navigateTo({
- type: NavigationType.open,
- nodeId,
- folderId,
- });
- }
-
- async refreshNode(nodeId: string) {
- await this.purgeServerCache(nodeId);
- this.nodesStore.removeNode(nodeId);
- await this.updateNodeInfo(nodeId);
- }
-
- getDatabaseObjectInfo(nodeId: string): DatabaseObjectInfoWithId | undefined {
- return this.nodesStore.getDatabaseObjectInfo(nodeId);
- }
-
- getNode(nodeId: string): NodeWithParent | undefined {
- return this.nodesStore.getNode(nodeId);
- }
-
- getChildren(nodeId: string): NodeChildren | undefined {
- return this.nodesStore.getChildren(nodeId);
- }
-
- async updateNodeInfo(nodeId: string) {
- const { navNodeInfo } = await this.graphQLService.gql.navNodeInfo({
- nodePath: nodeId,
- });
-
- let parentId = nodeId.match(/^(.*)\/.*?$/)![1];
- if (parentId === 'database:/') {
- parentId = ROOT_NODE_PATH;
- }
-
- const newNode = { ...navNodeInfo, parentId };
- this.nodesStore.updateNodeInfo(newNode);
- return newNode;
- }
-
- async updateDatabaseObjectInfo(nodeId: string) {
- const node = this.nodesStore.getNode(nodeId);
- if (!node) {
- await this.updateNodeInfo(nodeId);
- }
-
- const { objectInfo } = await this.graphQLService.gql.queryDatabaseObjectInfo({
- nodeId,
- // filter: { features: ['viewable', 'editPossible'] },
- });
-
- const info = { ...objectInfo.object, id: objectInfo.id };
- this.nodesStore.updateDatabaseObjectInfo(info);
- return info;
- }
-
- async purgeServerCache(nodeId: string) {
- await this.graphQLService.gql.navRefreshNode({
- nodePath: nodeId,
- });
- }
-
- async updateChildren(parentId: string) {
- const { navNodeChildren } = await this.graphQLService.gql.navNodeChildren({
- parentPath: parentId,
- });
- const children = navNodeChildren.map(child => child.id);
- this.nodesStore.updateChildren(parentId, children);
- for (const child of navNodeChildren) {
- this.nodesStore.updateNodeInfo({ ...child, parentId });
- }
- return children;
- }
-
- async updateRootChildren() {
- if (!await this.permissionsService.hasAsync(EPermission.public)) {
- return;
- }
- this.updateChildren(ROOT_NODE_PATH);
- }
-
- async updateChildrenDatabaseObjectInfo(nodeId: string) {
- const {
- childrenDatabaseObjectInfo,
- } = await this.graphQLService.gql.queryChildrenDatabaseObjectInfo({
- nodePath: nodeId,
- // filter: { features: ['viewable', 'editPossible'] }, // filter properties for view
- });
-
- const children = childrenDatabaseObjectInfo.map(children => ({
- ...children.object,
- id: children.id,
- }));
- this.nodesStore.updateChildrenDatabaseObjectInfo(children);
-
- return children;
- }
-
- async loadNodeInfo(nodeId: string): Promise {
- let node = this.nodesStore.getNode(nodeId);
- if (node) {
- if (node.parentId !== ROOT_NODE_PATH) {
- await this.loadNodeInfo(node.parentId);
- }
- return node;
- }
-
- node = await this.updateNodeInfo(nodeId);
-
- if (node.parentId !== ROOT_NODE_PATH) {
- await this.loadNodeInfo(node.parentId);
- }
-
- return node;
- }
-
- async loadChildren(parentId = ROOT_NODE_PATH): Promise {
- const nodeChildren = this.nodesStore.getChildren(parentId);
- if (nodeChildren?.isLoaded) {
- return nodeChildren.children;
- }
-
- return this.updateChildren(parentId);
- }
-
- async loadDatabaseObjectInfo(
- nodeId: string
- ): Promise {
- const databaseObjectInfo = this.nodesStore.getDatabaseObjectInfo(nodeId);
- if (databaseObjectInfo) {
- return databaseObjectInfo;
- }
-
- return this.updateDatabaseObjectInfo(nodeId);
- }
-
- async loadChildrenDatabaseObjectInfo(parentId: string): Promise {
- const children = await this.loadChildren(parentId);
- const childrenDatabaseObjectInfo: DatabaseObjectInfoWithId[] = [];
- for (const child of children) {
- const databaseObjectInfo = this.nodesStore.getDatabaseObjectInfo(child);
- if (!databaseObjectInfo) {
- return this.updateChildrenDatabaseObjectInfo(parentId);
- }
- childrenDatabaseObjectInfo.push(databaseObjectInfo);
- }
-
- return childrenDatabaseObjectInfo;
- }
-
- getParent(node: NodeWithParent) {
- return this.nodesStore.getNode(node.parentId);
- }
-
- getNodeContainerInfo(nodeId: string): INodeContainerInfo {
- const initial: INodeContainerInfo = {};
-
- const scanParents = (res: INodeContainerInfo,
- nodeId?: string): INodeContainerInfo => {
- if (!nodeId) {
- return res;
- }
- const node = this.getNode(nodeId);
- if (!node) {
- return res;
- }
- if (node?.object?.features?.includes(EObjectFeature.dataSource)) {
- res.connectionId = node.id;
- }
- if (node?.object?.features?.includes(EObjectFeature.catalog)) {
- res.catalogId = node.name; // note that catalogId is node name
- }
- if (node?.object?.features?.includes(EObjectFeature.schema)) {
- res.schemaId = node.name; // note that schemaId is node name
- }
- return scanParents(res, node.parentId);
- };
-
- return scanParents(initial, nodeId);
- }
-
- removeNodes(path = ROOT_NODE_PATH) {
- this.nodesStore.removeNode(path);
- }
-
- navigationNodeContext = async (
- contexts: IContextProvider,
- data: INodeNavigationData
- ): Promise => {
- let nodeId = data.nodeId;
- let folderId = '';
- let name: string | undefined;
- let icon: string | undefined;
-
- if (isDatabaseObject(nodeId) && data.type !== NavigationType.closeConnection) {
- const node = await this.loadNodeInfo(nodeId);
- name = node.name;
- icon = node.icon;
-
- if (node.folder) {
- const parent = await this.loadNodeInfo(node.parentId);
- folderId = nodeId;
- if (parent && !parent.folder) {
- nodeId = parent.id;
- name = parent.name;
- icon = parent.icon;
- }
- }
-
- if (data.folderId) {
- folderId = data.folderId;
- }
- }
-
- return {
- type: data.type,
- nodeId,
- folderId,
- name,
- icon,
- };
- }
-
- async navigateHandler(contexts: IContextProvider) {
- }
-
- isNodeHasData(info?: string | DatabaseObjectInfo) {
- if (typeof info === 'string') {
- info = this.nodesStore.getDatabaseObjectInfo(info);
- }
-
- if (!info || !info.features) {
- return false;
- }
-
- return info.features.includes(ENodeFeature.dataContainer)
- || info.features.includes(ENodeFeature.container);
- }
-}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/NodesStore.ts b/webapp/packages/core/src/app/shared/NodesManager/NodesStore.ts
deleted file mode 100644
index b9f6be1252..0000000000
--- a/webapp/packages/core/src/app/shared/NodesManager/NodesStore.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * cloudbeaver - Cloud Database Manager
- * Copyright (C) 2020 DBeaver Corp and others
- *
- * Licensed under the Apache License, Version 2.0.
- * you may not use this file except in compliance with the License.
- */
-
-import { action, observable } from 'mobx';
-
-import {
- DatabaseObjectInfoWithId,
- NodeChildren,
- NodeWithParent,
-} from './NodeWithParent';
-
-
-export class NodesStore {
- @observable
- private nodesMap: Map = new Map()
- @observable
- private databaseObjectInfoMap: Map = new Map()
- @observable
- private nodeChildrenMap: Map = new Map()
-
- getNode(nodeId: string): NodeWithParent | undefined {
- return this.nodesMap.get(nodeId);
- }
-
- getChildren(nodeId: string): NodeChildren | undefined {
- return this.nodeChildrenMap.get(nodeId);
- }
-
- getDatabaseObjectInfo(nodeId: string): DatabaseObjectInfoWithId | undefined {
- return this.databaseObjectInfoMap.get(nodeId);
- }
-
- @action
- updateNodeInfo(node: NodeWithParent) {
- this.nodesMap.set(node.id, node);
-
- const parent = this.nodeChildrenMap.get(node.parentId);
-
- if (!parent) {
- this.nodeChildrenMap.set(node.parentId, {
- children: [node.id],
- isLoaded: false,
- });
- } else if (!parent.children.includes(node.id)) {
- parent.children.push(node.id);
- }
- }
-
- @action
- updateDatabaseObjectInfo(databaseObjectInfo: DatabaseObjectInfoWithId) {
- this.databaseObjectInfoMap.set(databaseObjectInfo.id, databaseObjectInfo);
- }
-
- @action
- updateChildren(parentId: string, children: string[]) {
- this.nodeChildrenMap.set(parentId, {
- children,
- isLoaded: true,
- });
- }
-
- @action
- updateChildrenDatabaseObjectInfo(children: DatabaseObjectInfoWithId[]) {
- for (const child of children) {
- this.databaseObjectInfoMap.set(child.id, child);
- }
- }
-
- @action
- removeNode(nodeId: string) {
- const node = this.nodesMap.get(nodeId);
- if (!node) {
- return;
- }
- const parent = this.nodeChildrenMap.get(node.id);
- if (parent) {
- for (const children of parent.children) {
- this.removeNode(children);
- }
- this.nodeChildrenMap.delete(node.id);
- }
- this.databaseObjectInfoMap.delete(nodeId);
- this.nodesMap.delete(nodeId);
- }
-}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/queries/queryChildrenDatabaseObjectInfo.gql b/webapp/packages/core/src/app/shared/NodesManager/queries/getChildrenDBObjectInfo.gql
similarity index 65%
rename from webapp/packages/core/src/app/shared/NodesManager/queries/queryChildrenDatabaseObjectInfo.gql
rename to webapp/packages/core/src/app/shared/NodesManager/queries/getChildrenDBObjectInfo.gql
index b06b2b61f3..52b53e4f6c 100644
--- a/webapp/packages/core/src/app/shared/NodesManager/queries/queryChildrenDatabaseObjectInfo.gql
+++ b/webapp/packages/core/src/app/shared/NodesManager/queries/getChildrenDBObjectInfo.gql
@@ -1,8 +1,8 @@
-query queryChildrenDatabaseObjectInfo(
- $nodePath: ID!
+query getChildrenDBObjectInfo(
+ $navNodeId: ID!
$filter: ObjectPropertyFilter
) {
- childrenDatabaseObjectInfo: navNodeChildren(parentPath: $nodePath) {
+ dbObjects: navNodeChildren(parentPath: $navNodeId) {
id
object {
features
diff --git a/webapp/packages/core/src/app/shared/NodesManager/queries/queryDatabaseObjectInfo.gql b/webapp/packages/core/src/app/shared/NodesManager/queries/getDBObjectInfo.gql
similarity index 60%
rename from webapp/packages/core/src/app/shared/NodesManager/queries/queryDatabaseObjectInfo.gql
rename to webapp/packages/core/src/app/shared/NodesManager/queries/getDBObjectInfo.gql
index 54854ee4ca..753adf711d 100644
--- a/webapp/packages/core/src/app/shared/NodesManager/queries/queryDatabaseObjectInfo.gql
+++ b/webapp/packages/core/src/app/shared/NodesManager/queries/getDBObjectInfo.gql
@@ -1,6 +1,5 @@
-query queryDatabaseObjectInfo($nodeId: ID!, $filter: ObjectPropertyFilter) {
- objectInfo: navNodeInfo(nodePath: $nodeId) {
- id
+query getDBObjectInfo($navNodeId: ID!, $filter: ObjectPropertyFilter) {
+ objectInfo: navNodeInfo(nodePath: $navNodeId) {
object {
features
properties(filter: $filter) {
diff --git a/webapp/packages/core/src/app/shared/NodesManager/useDatabaseObjectInfo.ts b/webapp/packages/core/src/app/shared/NodesManager/useDatabaseObjectInfo.ts
index f1b759ec74..8beba2a904 100644
--- a/webapp/packages/core/src/app/shared/NodesManager/useDatabaseObjectInfo.ts
+++ b/webapp/packages/core/src/app/shared/NodesManager/useDatabaseObjectInfo.ts
@@ -8,10 +8,10 @@
import { useService } from '@dbeaver/core/di';
-import { NodesManagerService } from './NodesManagerService';
+import { DBObjectService } from './DBObjectService';
-export function useDatabaseObjectInfo(nodeId: string) {
- const nodesManagerService = useService(NodesManagerService);
+export function useDatabaseObjectInfo(navNodeId: string) {
+ const dbObjectService = useService(DBObjectService);
- return nodesManagerService.getDatabaseObjectInfo(nodeId);
+ return dbObjectService.getDBObject(navNodeId);
}
diff --git a/webapp/packages/core/src/app/shared/NodesManager/useNode.ts b/webapp/packages/core/src/app/shared/NodesManager/useNode.ts
index bc3121d5d6..8960355d85 100644
--- a/webapp/packages/core/src/app/shared/NodesManager/useNode.ts
+++ b/webapp/packages/core/src/app/shared/NodesManager/useNode.ts
@@ -8,10 +8,19 @@
import { useService } from '@dbeaver/core/di';
-import { NodesManagerService } from './NodesManagerService';
+import { NavNodeManagerService } from './NavNodeManagerService';
-export function useNode(nodeId: string) {
- const nodesManagerService = useService(NodesManagerService);
+export function useNode(navNodeId: string) {
+ const navNodeManagerService = useService(NavNodeManagerService);
+ const node = navNodeManagerService.getNode(navNodeId);
+ const isLoading = navNodeManagerService.navNode.isDataLoading({
+ navNodeId: [navNodeId],
+ });
+ const isLoaded = navNodeManagerService.navNode.isLoaded({
+ navNodeId: [navNodeId],
+ });
- return nodesManagerService.getNode(nodeId);
+ return {
+ node, isLoading, isLoaded,
+ };
}
diff --git a/webapp/packages/core/src/app/shared/useChildren.ts b/webapp/packages/core/src/app/shared/useChildren.ts
index 4bf58116ee..c4b943826e 100644
--- a/webapp/packages/core/src/app/shared/useChildren.ts
+++ b/webapp/packages/core/src/app/shared/useChildren.ts
@@ -8,10 +8,17 @@
import { useService } from '@dbeaver/core/di';
-import { NodesManagerService } from './NodesManager/NodesManagerService';
+import { NavNodeManagerService, ROOT_NODE_PATH } from './NodesManager/NavNodeManagerService';
-export function useChildren(parentId = '/') {
- const nodesManagerService = useService(NodesManagerService);
+export function useChildren(navNodeId = ROOT_NODE_PATH) {
+ const navNodeManagerService = useService(NavNodeManagerService);
+ const children = navNodeManagerService.getTree(navNodeId);
+ const isLoading = navNodeManagerService.navTree.isDataLoading(navNodeId, false);
+ const isLoaded = navNodeManagerService.navTree.isLoaded(navNodeId, false);
- return nodesManagerService.getChildren(parentId);
+ return {
+ children,
+ isLoaded,
+ isLoading,
+ };
}
diff --git a/webapp/packages/core/src/manifest.ts b/webapp/packages/core/src/manifest.ts
index f84c873448..868006d856 100644
--- a/webapp/packages/core/src/manifest.ts
+++ b/webapp/packages/core/src/manifest.ts
@@ -8,7 +8,7 @@
import {
ConnectionDialogsService, ConnectionsManagerService,
- MainMenuService, NavigationService, NavigationTreeService, NodesManagerService,
+ MainMenuService, NavigationService, NavigationTreeService, NavNodeManagerService, DBObjectService,
ConnectionSchemaManagerService, NavigationTabsService,
NavigationTreeContextMenuService,
SettingsMenuService, LogViewerService, LogViewerMenuService, TopNavService,
@@ -64,7 +64,8 @@ export const coreManifest: PluginManifest = {
NavigationTabsService,
NavigationTreeContextMenuService,
NavigationTreeService,
- NodesManagerService,
+ NavNodeManagerService,
+ DBObjectService,
NotificationService,
SessionService,
SettingsMenuService,
diff --git a/webapp/packages/core/src/root/PermissionsService.ts b/webapp/packages/core/src/root/PermissionsService.ts
index 2500e2dbe1..574a37f3e2 100644
--- a/webapp/packages/core/src/root/PermissionsService.ts
+++ b/webapp/packages/core/src/root/PermissionsService.ts
@@ -52,7 +52,7 @@ export class PermissionsService {
}
async update() {
- await this.permissions.refresh();
+ await this.permissions.refresh(true);
this.updateSubject.next();
}
diff --git a/webapp/packages/core/src/root/SessionService.ts b/webapp/packages/core/src/root/SessionService.ts
index 2ebc87b235..37d23a0900 100644
--- a/webapp/packages/core/src/root/SessionService.ts
+++ b/webapp/packages/core/src/root/SessionService.ts
@@ -33,7 +33,7 @@ export class SessionService {
}
async update() {
- await this.session.refresh();
+ await this.session.refresh(true);
}
private async refreshSessionStateAsync(data: SessionState | undefined): Promise {
diff --git a/webapp/packages/core/src/sdk/CachedResource.ts b/webapp/packages/core/src/sdk/CachedResource.ts
index 87cec251fe..d211c6b7ff 100644
--- a/webapp/packages/core/src/sdk/CachedResource.ts
+++ b/webapp/packages/core/src/sdk/CachedResource.ts
@@ -7,6 +7,7 @@
*/
import { observable } from 'mobx';
+import { Subject, Observable } from 'rxjs';
export type Loader = (
current: TData,
@@ -21,13 +22,23 @@ export type IsLoaded = (
...args: TArgs
) => boolean
+export type IsLoading = (
+ current: TData,
+ metadata: TMetadata,
+ ...args: TArgs
+) => boolean
+
export class CachedResource {
@observable data: TData;
@observable private loading = false;
@observable private metadata: TMetadata;
+
+ readonly onDataUpdate: Observable;
+ private dataSubject: Subject;
private loader: Loader;
private isLoadedCheck: IsLoaded;
+ private isLoadingCheck?: IsLoading;
private activePromise: Promise | null = null;
constructor(
@@ -35,11 +46,15 @@ export class CachedResource {
loader: Loader,
isLoadedCheck: IsLoaded,
metadata?: TMetadata,
+ isLoadingCheck?: IsLoading,
) {
this.data = defaultValue;
this.loader = loader;
this.isLoadedCheck = isLoadedCheck;
+ this.isLoadingCheck = isLoadingCheck;
this.metadata = metadata || {} as TMetadata;
+ this.dataSubject = new Subject();
+ this.onDataUpdate = this.dataSubject.asObservable();
}
isLoaded(...args: TArgs): boolean {
@@ -49,36 +64,56 @@ export class CachedResource {
isLoading(): boolean {
return this.loading;
}
+ isDataLoading(...args: TArgs): boolean {
+ if (this.isLoadingCheck) {
+ return this.loading && this.isLoadingCheck(this.data, this.metadata, ...args);
+ }
+ return this.loading;
+ }
- async refresh(...args: TArgs): Promise {
- return this.loadAll(true, args);
+ async refresh(load = false, ...args: TArgs): Promise {
+ return this.loadData(load, true, args);
}
async load(...args: TArgs): Promise {
- return this.loadAll(false, args);
+ return this.loadData(true, false, args);
}
- private async loadAll(update: boolean, args: TArgs) {
+ async refreshUnblocked(load = false, ...args: TArgs): Promise {
+ return this.loadData(load, true, args, true);
+ }
+
+ async loadUnblocked(...args: TArgs): Promise {
+ return this.loadData(true, false, args, true);
+ }
+
+ private async loadData(load: boolean, update: boolean, args: TArgs, unblocked?: boolean) {
+ if (unblocked) {
+ return this.loadingTask(load, update, args);
+ }
await this.waitActive();
- this.activePromise = this.loadingTask(update, args);
+ this.activePromise = this.loadingTask(load, update, args);
try {
return await this.activePromise;
} finally {
this.activePromise = null;
+ this.dataSubject.next(this.data);
}
}
- private async loadingTask(update: boolean, args: TArgs): Promise {
+ private async loadingTask(load: boolean, update: boolean, args: TArgs): Promise {
+ const prevState = this.loading;
this.loading = true;
try {
- if (this.isLoaded(...args) && !update) {
+ // don't load existed data & don't refresh doesn't loaded data
+ if ((this.isLoaded(...args) && !update) || (!this.isLoaded(...args) && update && !load)) {
return this.data;
}
- this.data = await this.loader(this.data, this.metadata, update, ...args);
+ this.data = await this.loader(this.data, this.metadata, load, ...args);
} finally {
- this.loading = false;
+ this.loading = prevState;
}
return this.data;
diff --git a/webapp/packages/core/src/sdk/sdk.ts b/webapp/packages/core/src/sdk/sdk.ts
index f424d9d083..54e6bbfc0b 100644
--- a/webapp/packages/core/src/sdk/sdk.ts
+++ b/webapp/packages/core/src/sdk/sdk.ts
@@ -1,5 +1,6 @@
/* eslint-disable max-len */
import { GraphQLClient } from 'graphql-request';
+
export type Maybe = T;
/** All built-in and custom scalars, mapped to their actual values */
@@ -686,7 +687,7 @@ export type NavGetStructContainersQueryVariables = {
};
-export type NavGetStructContainersQuery = { navGetStructContainers: { catalogList: Array>, schemaList: Array> } };
+export type NavGetStructContainersQuery = { navGetStructContainers: { catalogList: Array>; schemaList: Array> } };
export type CloseConnectionMutationVariables = {
id: Scalars['ID'];
@@ -727,7 +728,7 @@ export type DriverPropertiesQueryVariables = {
export type DriverPropertiesQuery = { driver: Array<(
Pick
& { driverProperties?: Maybe>>> }
- )> };
+ )>; };
export type GetDriverByIdQueryVariables = {
driverId: Scalars['ID'];
@@ -750,6 +751,31 @@ export type TestConnectionMutationVariables = {
export type TestConnectionMutation = { testConnection: Pick };
+export type GetChildrenDbObjectInfoQueryVariables = {
+ navNodeId: Scalars['ID'];
+ filter?: Maybe;
+};
+
+
+export type GetChildrenDbObjectInfoQuery = { dbObjects: Array<(
+ Pick
+ & { object?: Maybe<(
+ Pick
+ & { properties?: Maybe>>> }
+ )>; }
+ )>; };
+
+export type GetDbObjectInfoQueryVariables = {
+ navNodeId: Scalars['ID'];
+ filter?: Maybe;
+};
+
+
+export type GetDbObjectInfoQuery = { objectInfo: { object?: Maybe<(
+ Pick
+ & { properties?: Maybe>>> }
+ )>; }; };
+
export type NavNodeChildrenQueryVariables = {
parentPath: Scalars['ID'];
};
@@ -758,7 +784,7 @@ export type NavNodeChildrenQueryVariables = {
export type NavNodeChildrenQuery = { navNodeChildren: Array<(
Pick
& { object?: Maybe> }
- )> };
+ )>; };
export type NavNodeInfoQueryVariables = {
nodePath: Scalars['ID'];
@@ -768,7 +794,7 @@ export type NavNodeInfoQueryVariables = {
export type NavNodeInfoQuery = { navNodeInfo: (
Pick
& { object?: Maybe> }
- ) };
+ ); };
export type NavRefreshNodeQueryVariables = {
nodePath: Scalars['ID'];
@@ -777,34 +803,6 @@ export type NavRefreshNodeQueryVariables = {
export type NavRefreshNodeQuery = Pick;
-export type QueryChildrenDatabaseObjectInfoQueryVariables = {
- nodePath: Scalars['ID'];
- filter?: Maybe;
-};
-
-
-export type QueryChildrenDatabaseObjectInfoQuery = { childrenDatabaseObjectInfo: Array<(
- Pick
- & { object?: Maybe<(
- Pick
- & { properties?: Maybe>>> }
- )> }
- )> };
-
-export type QueryDatabaseObjectInfoQueryVariables = {
- nodeId: Scalars['ID'];
- filter?: Maybe;
-};
-
-
-export type QueryDatabaseObjectInfoQuery = { objectInfo: (
- Pick
- & { object?: Maybe<(
- Pick
- & { properties?: Maybe>>> }
- )> }
- ) };
-
export type ReadSessionLogQueryVariables = {
maxEntries: Scalars['Int'];
clearEntries: Scalars['Boolean'];
@@ -891,7 +889,7 @@ export type GetAuthProvidersQueryVariables = {};
export type GetAuthProvidersQuery = { providers: Array<(
Pick
& { credentialParameters: Array> }
- )> };
+ )>; };
export type GetSessionUserQueryVariables = {};
@@ -906,7 +904,7 @@ export type AsyncExportTaskStatusMutationVariables = {
export type AsyncExportTaskStatusMutation = { taskInfo: (
Pick
& { error?: Maybe> }
- ) };
+ ); };
export type ExportDataFromContainerQueryVariables = {
connectionId: Scalars['ID'];
@@ -918,7 +916,7 @@ export type ExportDataFromContainerQueryVariables = {
export type ExportDataFromContainerQuery = { taskInfo: (
Pick
& { error?: Maybe> }
- ) };
+ ); };
export type ExportDataFromResultsQueryVariables = {
connectionId: Scalars['ID'];
@@ -931,7 +929,7 @@ export type ExportDataFromResultsQueryVariables = {
export type ExportDataFromResultsQuery = { taskInfo: (
Pick
& { error?: Maybe> }
- ) };
+ ); };
export type GetDataTransferProcessorsQueryVariables = {};
@@ -939,7 +937,7 @@ export type GetDataTransferProcessorsQueryVariables = {};
export type GetDataTransferProcessorsQuery = { processors: Array<(
Pick
& { properties?: Maybe>>> }
- )> };
+ )>; };
export type RemoveDataTransferFileQueryVariables = {
dataFileId: Scalars['String'];
@@ -965,10 +963,10 @@ export type AsyncSqlExecuteQueryMutation = { taskInfo: (
& { resultSet?: Maybe<(
Pick
& { columns?: Maybe>>> }
- )> }
- )> }
- )>, error?: Maybe> }
- ) };
+ )>; }
+ )>; }
+ )>; error?: Maybe>; }
+ ); };
export type AsyncTaskCancelMutationVariables = {
taskId: Scalars['String'];
@@ -991,10 +989,10 @@ export type AsyncTaskStatusMutation = { taskInfo: (
& { resultSet?: Maybe<(
Pick
& { columns?: Maybe>>> }
- )> }
- )> }
- )>, error?: Maybe> }
- ) };
+ )>; }
+ )>; }
+ )>; error?: Maybe>; }
+ ); };
export type ExecuteSqlQueryMutationVariables = {
connectionId: Scalars['ID'];
@@ -1011,9 +1009,9 @@ export type ExecuteSqlQueryMutation = { result?: Maybe<(
& { resultSet?: Maybe<(
Pick
& { columns?: Maybe>>> }
- )> }
- )> }
- )> };
+ )>; }
+ )>; }
+ )>; };
export type MetadataGetNodeDdlQueryVariables = {
nodeId: Scalars['ID'];
@@ -1055,9 +1053,9 @@ export type ReadDataFromContainerMutation = { readDataFromContainer?: Maybe<(
& { resultSet?: Maybe<(
Pick
& { columns?: Maybe>>> }
- )> }
- )> }
- )> };
+ )>; }
+ )>; }
+ )>; };
export type SqlContextCreateMutationVariables = {
connectionId: Scalars['ID'];
@@ -1109,8 +1107,8 @@ export type UpdateResultsDataMutation = { result?: Maybe<(
& { results: Array<(
Pick
& { resultSet?: Maybe> }
- )> }
- )> };
+ )>; }
+ )>; };
export type OpenSessionMutationVariables = {};
@@ -1118,7 +1116,7 @@ export type OpenSessionMutationVariables = {};
export type OpenSessionMutation = { session: (
Pick
& { connections: Array> }
- ) };
+ ); };
export type ServerConfigQueryVariables = {};
@@ -1126,7 +1124,7 @@ export type ServerConfigQueryVariables = {};
export type ServerConfigQuery = { serverConfig: (
Pick
& { supportedLanguages: Array> }
- ) };
+ ); };
export type SessionPermissionsQueryVariables = {};
@@ -1139,7 +1137,7 @@ export type SessionStateQueryVariables = {};
export type SessionStateQuery = { sessionState: (
Pick
& { connections: Array> }
- ) };
+ ); };
export type TouchSessionMutationVariables = {};
@@ -1257,6 +1255,43 @@ export const TestConnectionDocument = `
}
}
`;
+export const GetChildrenDbObjectInfoDocument = `
+ query getChildrenDBObjectInfo($navNodeId: ID!, $filter: ObjectPropertyFilter) {
+ dbObjects: navNodeChildren(parentPath: $navNodeId) {
+ id
+ object {
+ features
+ properties(filter: $filter) {
+ id
+ category
+ dataType
+ description
+ displayName
+ features
+ value
+ }
+ }
+ }
+}
+ `;
+export const GetDbObjectInfoDocument = `
+ query getDBObjectInfo($navNodeId: ID!, $filter: ObjectPropertyFilter) {
+ objectInfo: navNodeInfo(nodePath: $navNodeId) {
+ object {
+ features
+ properties(filter: $filter) {
+ id
+ category
+ dataType
+ description
+ displayName
+ features
+ value
+ }
+ }
+ }
+}
+ `;
export const NavNodeChildrenDocument = `
query navNodeChildren($parentPath: ID!) {
navNodeChildren(parentPath: $parentPath) {
@@ -1298,44 +1333,6 @@ export const NavRefreshNodeDocument = `
navRefreshNode(nodePath: $nodePath)
}
`;
-export const QueryChildrenDatabaseObjectInfoDocument = `
- query queryChildrenDatabaseObjectInfo($nodePath: ID!, $filter: ObjectPropertyFilter) {
- childrenDatabaseObjectInfo: navNodeChildren(parentPath: $nodePath) {
- id
- object {
- features
- properties(filter: $filter) {
- id
- category
- dataType
- description
- displayName
- features
- value
- }
- }
- }
-}
- `;
-export const QueryDatabaseObjectInfoDocument = `
- query queryDatabaseObjectInfo($nodeId: ID!, $filter: ObjectPropertyFilter) {
- objectInfo: navNodeInfo(nodePath: $nodeId) {
- id
- object {
- features
- properties(filter: $filter) {
- id
- category
- dataType
- description
- displayName
- features
- value
- }
- }
- }
-}
- `;
export const ReadSessionLogDocument = `
query readSessionLog($maxEntries: Int!, $clearEntries: Boolean!) {
log: readSessionLog(maxEntries: $maxEntries, clearEntries: $clearEntries) {
@@ -1841,6 +1838,12 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
testConnection(variables: TestConnectionMutationVariables): Promise {
return withWrapper(() => client.request(TestConnectionDocument, variables));
},
+ getChildrenDBObjectInfo(variables: GetChildrenDbObjectInfoQueryVariables): Promise {
+ return withWrapper(() => client.request(GetChildrenDbObjectInfoDocument, variables));
+ },
+ getDBObjectInfo(variables: GetDbObjectInfoQueryVariables): Promise {
+ return withWrapper(() => client.request(GetDbObjectInfoDocument, variables));
+ },
navNodeChildren(variables: NavNodeChildrenQueryVariables): Promise {
return withWrapper(() => client.request(NavNodeChildrenDocument, variables));
},
@@ -1850,12 +1853,6 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
navRefreshNode(variables: NavRefreshNodeQueryVariables): Promise {
return withWrapper(() => client.request(NavRefreshNodeDocument, variables));
},
- queryChildrenDatabaseObjectInfo(variables: QueryChildrenDatabaseObjectInfoQueryVariables): Promise {
- return withWrapper(() => client.request(QueryChildrenDatabaseObjectInfoDocument, variables));
- },
- queryDatabaseObjectInfo(variables: QueryDatabaseObjectInfoQueryVariables): Promise {
- return withWrapper(() => client.request(QueryDatabaseObjectInfoDocument, variables));
- },
readSessionLog(variables: ReadSessionLogQueryVariables): Promise {
return withWrapper(() => client.request(ReadSessionLogDocument, variables));
},
@@ -1963,7 +1960,7 @@ export function getSdk(client: GraphQLClient, withWrapper: SdkFunctionWrapper =
},
touchSession(variables?: TouchSessionMutationVariables): Promise {
return withWrapper(() => client.request(TouchSessionDocument, variables));
- }
+ },
};
}
-export type Sdk = ReturnType;
\ No newline at end of file
+export type Sdk = ReturnType;
diff --git a/webapp/packages/data-export-plugin/src/DataExportMenuService.ts b/webapp/packages/data-export-plugin/src/DataExportMenuService.ts
index ae6885370d..dbb87ca18c 100644
--- a/webapp/packages/data-export-plugin/src/DataExportMenuService.ts
+++ b/webapp/packages/data-export-plugin/src/DataExportMenuService.ts
@@ -7,7 +7,7 @@
*/
import {
- NavigationTreeContextMenuService, NodeManagerUtils, NodeWithParent, EObjectFeature
+ NavigationTreeContextMenuService, NodeManagerUtils, NavNode, EObjectFeature
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import {
@@ -39,13 +39,13 @@ export class DataExportMenuService {
};
this.tableFooterMenuService.registerMenuItem(exportData);
- this.contextMenuService.addMenuItem(
+ this.contextMenuService.addMenuItem(
this.contextMenuService.getRootMenuToken(),
{
id: 'export',
isPresent(context) {
return context.contextType === NavigationTreeContextMenuService.nodeContextType
- && !!context.data.object?.features?.includes(EObjectFeature.dataContainer);
+ && context.data.objectFeatures.includes(EObjectFeature.dataContainer);
},
order: 2,
title: 'Export',
diff --git a/webapp/packages/data-export-plugin/src/ExportNotification/ExportNotificationController.ts b/webapp/packages/data-export-plugin/src/ExportNotification/ExportNotificationController.ts
index 4c060299cf..4076ff9865 100644
--- a/webapp/packages/data-export-plugin/src/ExportNotification/ExportNotificationController.ts
+++ b/webapp/packages/data-export-plugin/src/ExportNotification/ExportNotificationController.ts
@@ -8,7 +8,7 @@
import { observable, computed } from 'mobx';
-import { ErrorDetailsDialog, NodesManagerService } from '@dbeaver/core/app';
+import { ErrorDetailsDialog, NavNodeManagerService } from '@dbeaver/core/app';
import { IInitializableController, injectable } from '@dbeaver/core/di';
import { CommonDialogService } from '@dbeaver/core/dialogs';
import { INotification } from '@dbeaver/core/eventsLog';
@@ -46,7 +46,7 @@ export class ExportNotificationController implements IInitializableController {
return '';
}
if (this.task.context.containerNodePath) {
- const node = this.nodesManagerService.getNode(this.task.context.containerNodePath);
+ const node = this.navNodeManagerService.getNode(this.task.context.containerNodePath);
return `${this.localization.translate('data_transfer_exporting_table')} ${node?.name}`;
}
@@ -76,7 +76,7 @@ export class ExportNotificationController implements IInitializableController {
constructor(
private commonDialogService: CommonDialogService,
private dataExportProcessService: DataExportProcessService,
- private nodesManagerService: NodesManagerService,
+ private navNodeManagerService: NavNodeManagerService,
private localization: LocalizationService,
) {}
diff --git a/webapp/packages/data-viewer-plugin/src/DataViewerPage/DataViewerTab.tsx b/webapp/packages/data-viewer-plugin/src/DataViewerPage/DataViewerTab.tsx
index bd6ba0807a..5956c37f42 100644
--- a/webapp/packages/data-viewer-plugin/src/DataViewerPage/DataViewerTab.tsx
+++ b/webapp/packages/data-viewer-plugin/src/DataViewerPage/DataViewerTab.tsx
@@ -9,7 +9,7 @@
import { observer } from 'mobx-react';
import styled from 'reshadow';
-import { NodesManagerService } from '@dbeaver/core/app';
+import { NavNodeManagerService } from '@dbeaver/core/app';
import { TabIcon, Tab, TabTitle } from '@dbeaver/core/blocks';
import { useService } from '@dbeaver/core/di';
import { useStyles } from '@dbeaver/core/theming';
@@ -18,9 +18,9 @@ import { ObjectPageTabProps } from '@dbeaver/object-viewer-plugin';
export const DataViewerTab = observer(function DataViewerTab({
tab, page, onSelect, style,
}: ObjectPageTabProps) {
- const nodesManagerService = useService(NodesManagerService);
+ const navNodeManagerService = useService(NavNodeManagerService);
- if (!nodesManagerService.isNodeHasData(tab.handlerState.objectId)) {
+ if (!navNodeManagerService.isNodeHasData(tab.handlerState.objectId)) {
return null;
}
diff --git a/webapp/packages/data-viewer-plugin/src/DataViewerTabService.ts b/webapp/packages/data-viewer-plugin/src/DataViewerTabService.ts
index 943969d663..395d88159b 100644
--- a/webapp/packages/data-viewer-plugin/src/DataViewerTabService.ts
+++ b/webapp/packages/data-viewer-plugin/src/DataViewerTabService.ts
@@ -7,7 +7,7 @@
*/
import {
- NodesManagerService,
+ NavNodeManagerService,
INodeNavigationData,
IContextProvider,
ITab,
@@ -29,11 +29,13 @@ import { DataViewerTableService } from './DataViewerTableService';
export class DataViewerTabService {
page: ObjectPage;
- constructor(private nodesManagerService: NodesManagerService,
- private dataViewerTableService: DataViewerTableService,
- private objectViewerTabService: ObjectViewerTabService,
- private dbObjectPageService: DBObjectPageService,
- private notificationService: NotificationService) {
+ constructor(
+ private navNodeManagerService: NavNodeManagerService,
+ private dataViewerTableService: DataViewerTableService,
+ private objectViewerTabService: ObjectViewerTabService,
+ private dbObjectPageService: DBObjectPageService,
+ private notificationService: NotificationService
+ ) {
this.page = this.dbObjectPageService.register({
key: dataViewerHandlerKey,
@@ -49,7 +51,7 @@ export class DataViewerTabService {
}
registerTabHandler() {
- this.nodesManagerService.navigator.addHandler(this.navigationHandler.bind(this));
+ this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this));
}
private async navigationHandler(contexts: IContextProvider) {
@@ -60,14 +62,12 @@ export class DataViewerTabService {
trySwitchPage,
} = await contexts.getContext(this.objectViewerTabService.objectViewerTabContext);
-
if (nodeInfo.type === NavigationType.closeConnection) {
return;
}
- // const tabInfo = await contexts.getContext(this.navigationTabsService.navigationTabContext);
- const objectInfo = await this.nodesManagerService.loadDatabaseObjectInfo(nodeInfo.nodeId);
+ const node = await this.navNodeManagerService.loadNode(nodeInfo);
- if (!this.nodesManagerService.isNodeHasData(objectInfo)) {
+ if (!this.navNodeManagerService.isNodeHasData(node)) {
return;
}
@@ -83,9 +83,12 @@ export class DataViewerTabService {
}
private async handleTabSelect(tab: ITab) {
- const objectInfo = await this.nodesManagerService.loadDatabaseObjectInfo(tab.handlerState.objectId);
+ const node = await this.navNodeManagerService.loadNode({
+ nodeId: tab.handlerState.objectId,
+ parentId: tab.handlerState.parentId,
+ });
- if (!this.nodesManagerService.isNodeHasData(objectInfo)) {
+ if (!this.navNodeManagerService.isNodeHasData(node)) {
return;
}
@@ -93,7 +96,7 @@ export class DataViewerTabService {
return;
}
- const nodeInfo = this.nodesManagerService
+ const nodeInfo = this.navNodeManagerService
.getNodeContainerInfo(tab.handlerState.objectId);
if (!nodeInfo.connectionId) {
@@ -111,11 +114,12 @@ export class DataViewerTabService {
// if (!this.nodesManagerService.isNodeHasData(tab.handlerState.objectId)) {
// return;
// }
- const info = await this.nodesManagerService.loadDatabaseObjectInfo(tab.handlerState.objectId);
- if (info) {
- return true;
- }
- return false;
+ await this.navNodeManagerService.loadNode({
+ nodeId: tab.handlerState.objectId,
+ parentId: tab.handlerState.parentId,
+ });
+ // await this.dbObjectService.load(tab.handlerState.objectId);
+ return true;
}
private handleTabClose(tab: ITab) {
diff --git a/webapp/packages/ddl-viewer-plugin/src/DdlViewer/DdlViewerController.ts b/webapp/packages/ddl-viewer-plugin/src/DdlViewer/DdlViewerController.ts
index 6008061b0c..1afa8a28ce 100644
--- a/webapp/packages/ddl-viewer-plugin/src/DdlViewer/DdlViewerController.ts
+++ b/webapp/packages/ddl-viewer-plugin/src/DdlViewer/DdlViewerController.ts
@@ -8,7 +8,7 @@
import { observable, when } from 'mobx';
-import { NodeManagerUtils, NodesManagerService } from '@dbeaver/core/app';
+import { NodeManagerUtils, NavNodeManagerService } from '@dbeaver/core/app';
import { IDestructibleController, IInitializableController, injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
import { SqlDialectInfo } from '@dbeaver/core/sdk';
@@ -25,11 +25,12 @@ export class DdlViewerController implements IInitializableController, IDestructi
private nodeId!: string;
- constructor(private ddlViewerService: DdlViewerService,
- private nodesManagerService: NodesManagerService,
- private sqlDialectInfoService: SqlDialectInfoService,
- private notificationService: NotificationService) {
- }
+ constructor(
+ private ddlViewerService: DdlViewerService,
+ private navNodeManagerService: NavNodeManagerService,
+ private sqlDialectInfoService: SqlDialectInfoService,
+ private notificationService: NotificationService
+ ) { }
init(nodeId: string) {
this.nodeId = nodeId;
@@ -55,7 +56,7 @@ export class DdlViewerController implements IInitializableController, IDestructi
}
private async loadDialect(nodeId: string): Promise {
- const { connectionId } = this.nodesManagerService.getNodeContainerInfo(nodeId);
+ const { connectionId } = this.navNodeManagerService.getNodeContainerInfo(nodeId);
if (!connectionId) {
return;
}
diff --git a/webapp/packages/ddl-viewer-plugin/src/DdlViewerTabService.ts b/webapp/packages/ddl-viewer-plugin/src/DdlViewerTabService.ts
index 8441f5edcd..8aa6e8a0a6 100644
--- a/webapp/packages/ddl-viewer-plugin/src/DdlViewerTabService.ts
+++ b/webapp/packages/ddl-viewer-plugin/src/DdlViewerTabService.ts
@@ -7,7 +7,7 @@
*/
import {
- EObjectFeature, NodesManagerService, IContextProvider, INodeNavigationData
+ EObjectFeature, NavNodeManagerService, IContextProvider, INodeNavigationData
} from '@dbeaver/core/app';
import { ITab } from '@dbeaver/core/blocks';
import { injectable } from '@dbeaver/core/di';
@@ -22,16 +22,18 @@ const ddlViewerTabId = 'ddl-viewer';
@injectable()
export class DdlViewerTabService {
- constructor(private nodesManagerService: NodesManagerService,
- private objectViewerTabService: ObjectViewerTabService,
- private notificationService: NotificationService,
- private ddlViewerService: DdlViewerService) {
- this.nodesManagerService.navigator.addHandler(this.navigationHandler.bind(this));
+ constructor(
+ private navNodeManagerService: NavNodeManagerService,
+ private objectViewerTabService: ObjectViewerTabService,
+ private notificationService: NotificationService,
+ private ddlViewerService: DdlViewerService
+ ) {
+ this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this));
}
buildTab(nodeId: string): ITab | null {
- const node = this.nodesManagerService.getNode(nodeId);
- const isDdlRequired = node?.object?.features?.includes(EObjectFeature.script);
+ const node = this.navNodeManagerService.getNode(nodeId);
+ const isDdlRequired = node?.objectFeatures.includes(EObjectFeature.script);
if (!isDdlRequired) {
return null;
}
@@ -39,7 +41,7 @@ export class DdlViewerTabService {
tabId: ddlViewerTabId,
title: 'DDL',
icon: 'sql-text',
- onActivate: () => this.activateDDLTab(nodeId),
+ onActivate: () => this.activateDDLTab(nodeId, node?.parentId!),
panel: () => ddlViewer(nodeId),
};
return ddlTab;
@@ -57,7 +59,7 @@ export class DdlViewerTabService {
}
}
- private activateDDLTab(nodeId: string) {
- this.nodesManagerService.navToNode(nodeId, ddlViewerTabId);
+ private activateDDLTab(nodeId: string, parentId: string) {
+ this.navNodeManagerService.navToNode(nodeId, parentId, ddlViewerTabId);
}
}
diff --git a/webapp/packages/object-viewer-plugin/src/IObjectViewerTabState.ts b/webapp/packages/object-viewer-plugin/src/IObjectViewerTabState.ts
index df6d46a8a2..5333288be8 100644
--- a/webapp/packages/object-viewer-plugin/src/IObjectViewerTabState.ts
+++ b/webapp/packages/object-viewer-plugin/src/IObjectViewerTabState.ts
@@ -8,6 +8,8 @@
export interface IObjectViewerTabState {
objectId: string;
+ parentId: string;
+ parents: string[];
folderId: string;
tabIcon?: string;
tabTitle?: string;
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectFoldersTab/ObjectFoldersTabService.ts b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectFoldersTab/ObjectFoldersTabService.ts
index bb62545ca5..bf8d811ef0 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectFoldersTab/ObjectFoldersTabService.ts
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectFoldersTab/ObjectFoldersTabService.ts
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
-import { NodesManagerService, TabEntity } from '@dbeaver/core/app';
+import { NavNodeManagerService, TabEntity } from '@dbeaver/core/app';
import { ITab } from '@dbeaver/core/blocks';
import { injectable } from '@dbeaver/core/di';
@@ -15,7 +15,7 @@ import { ObjectFolderTabModel } from './ObjectFolderTabModel';
@injectable()
export class ObjectFoldersTabService {
- constructor(private nodesManagerService: NodesManagerService) {
+ constructor(private navNodeManagerService: NavNodeManagerService) {
}
createTabEntities(nodeId: string): TabEntity[] {
@@ -24,22 +24,22 @@ export class ObjectFoldersTabService {
private tabBuilder(nodeId: string): ITab[] {
- const children = this.nodesManagerService.getChildren(nodeId)?.children || [];
+ const children = this.navNodeManagerService.getTree(nodeId) || [];
const folderTabs = children.filter((nodeId) => {
- const node = this.nodesManagerService.getNode(nodeId);
+ const node = this.navNodeManagerService.getNode(nodeId);
return node ? node.folder : false;
});
const tabList: ITab[] = folderTabs.map((nodeId) => {
- const node = this.nodesManagerService.getNode(nodeId);
+ const node = this.navNodeManagerService.getNode(nodeId);
if (!node) {
throw Error(`node not found: ${nodeId}`);
}
return new ObjectFolderTabModel(
node,
- () => this.nodesManagerService.navToNode(node.id)
+ () => this.navNodeManagerService.navToNode(node.id, node.parentId)
);
});
return tabList;
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPagePanel.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPagePanel.tsx
index 9472677a9d..3928607375 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPagePanel.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPagePanel.tsx
@@ -35,9 +35,9 @@ const viewerStyles = composes(
export const ObjectPropertiesPagePanel = observer(function ObjectPropertiesPagePanel({
tab,
}: ObjectPagePanelProps) {
- const children = useChildren(tab.handlerState.objectId);
+ const { children, isLoading } = useChildren(tab.handlerState.objectId);
- if (!children?.isLoaded) {
+ if (!children || isLoading) {
return ;
}
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPageService.ts b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPageService.ts
index fc15cd9a47..129b2ea6e2 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPageService.ts
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertiesPageService.ts
@@ -7,7 +7,7 @@
*/
import {
- NodesManagerService, ITab, IContextProvider, INodeNavigationData
+ NavNodeManagerService, ITab, IContextProvider, INodeNavigationData
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
@@ -24,7 +24,7 @@ export class ObjectPropertiesPageService {
page?: ObjectPage;
constructor(
- private nodesManagerService: NodesManagerService,
+ private navNodeManagerService: NavNodeManagerService,
private notificationService: NotificationService,
private objectViewerTabService: ObjectViewerTabService,
private dbObjectPageService: DBObjectPageService
@@ -41,7 +41,7 @@ export class ObjectPropertiesPageService {
getPanelComponent: () => ObjectPropertiesPagePanel,
onSelect: this.handlePageSelect.bind(this),
});
- this.nodesManagerService.navigator.addHandler(this.navigationHandler.bind(this));
+ this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this));
}
private async handlePageSelect(tab: ITab) {
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx
index 0786c8d21f..b654f82bf2 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/Item.tsx
@@ -10,7 +10,7 @@ import { observer } from 'mobx-react';
import { useCallback } from 'react';
import styled, { css } from 'reshadow';
-import { NodesManagerService, useDatabaseObjectInfo, useNode } from '@dbeaver/core/app';
+import { NavNodeManagerService, useDatabaseObjectInfo, useNode } from '@dbeaver/core/app';
import {
StaticImage, TableItem, TableColumnValue, TableItemSelect
} from '@dbeaver/core/blocks';
@@ -51,12 +51,12 @@ type ItemProps = {
export const Item = observer(function Item({
objectId, columns,
}: ItemProps) {
- const nodesManagerService = useService(NodesManagerService);
- const object = useNode(objectId);
+ const navNodeManagerService = useService(NavNodeManagerService);
+ const { node } = useNode(objectId);
const databaseObjectInfo = useDatabaseObjectInfo(objectId);
- const handleOpen = useCallback(() => nodesManagerService.navToNode(objectId), [objectId]);
+ const handleOpen = useCallback(() => navNodeManagerService.navToNode(node!.id, node!.parentId), [node]);
- if (!object || !databaseObjectInfo?.properties) {
+ if (!node || !databaseObjectInfo?.properties) {
return styled(useStyles(itemStyles))(
@@ -81,7 +81,7 @@ export const Item = observer(function Item({
-
+
{databaseObjectInfo.properties.map(property => (
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx
index 3a51618e29..823622753d 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/ObjectPropertyTable/ObjectPropertyTable.tsx
@@ -22,15 +22,18 @@ const styles = css`
`;
type ObjectPropertyTableProps = {
- parentId: string;
objectId: string;
+ parentId: string;
}
-export const ObjectPropertyTable = observer(function ObjectPropertyTable(props: ObjectPropertyTableProps) {
- const children = useChildren(props.objectId);
- const { isLoading } = useObjectFolder(props.parentId, props.objectId);
+export const ObjectPropertyTable = observer(function ObjectPropertyTable({
+ objectId,
+ parentId,
+}: ObjectPropertyTableProps) {
+ const children = useChildren(objectId);
+ const { isLoading } = useObjectFolder(parentId);
- if (!children?.isLoaded || isLoading) {
+ if (!children.children || children.isLoading || isLoading) {
return ;
}
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabMixin.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabMixin.tsx
index 6225f3b44b..6b8b0d9bf1 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabMixin.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabMixin.tsx
@@ -8,7 +8,7 @@
import { observable } from 'mobx';
-import { NodesManagerService } from '@dbeaver/core/app';
+import { DBObjectService } from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { VirtualFolderTabData } from './VirtualFolderTabData';
@@ -18,9 +18,10 @@ export class VirtualFolderTabMixin {
@observable isActivated = false;
- constructor(private virtualFolderTabData: VirtualFolderTabData,
- private nodesManagerService: NodesManagerService) {
- }
+ constructor(
+ private virtualFolderTabData: VirtualFolderTabData,
+ private dbObjectService: DBObjectService
+ ) {}
getChildrenId() {
return this.virtualFolderTabData.childrenIds;
@@ -37,7 +38,7 @@ export class VirtualFolderTabMixin {
try {
for (const nodeId of this.getChildrenId()) {
- await this.nodesManagerService.loadDatabaseObjectInfo(nodeId);
+ await this.dbObjectService.load(nodeId);
}
}
finally {
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabService.tsx b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabService.tsx
index dd949a0df4..e8911af34e 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabService.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectPropertiesPage/VirtualFolderTab/VirtualFolderTabService.tsx
@@ -6,7 +6,7 @@
* you may not use this file except in compliance with the License.
*/
-import { NodesManagerService, NodeWithParent, TabEntity } from '@dbeaver/core/app';
+import { NavNodeManagerService, NavNode, TabEntity } from '@dbeaver/core/app';
import { injectable, MixinProvider } from '@dbeaver/core/di';
import { VirtualFolderTabData } from './VirtualFolderTabData';
@@ -17,15 +17,14 @@ import { VirtualFolderTabModel } from './VirtualFolderTabModel';
@injectable()
export class VirtualFolderTabService {
- constructor(private nodesManagerService: NodesManagerService) {
- }
+ constructor(private navNodeManagerService: NavNodeManagerService) { }
createTabEntities(nodeId: string): TabEntity[] {
- const children = this.nodesManagerService.getChildren(nodeId)?.children || [];
+ const children = this.navNodeManagerService.getTree(nodeId) || [];
- const notFolderNodes = children.reduce(
+ const notFolderNodes = children.reduce(
(nodes, nodeId) => {
- const node = this.nodesManagerService.getNode(nodeId);
+ const node = this.navNodeManagerService.getNode(nodeId);
if (node && !node.folder) {
nodes.push(node);
}
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectViewerPanel.tsx b/webapp/packages/object-viewer-plugin/src/ObjectViewerPanel.tsx
index 68efa898fb..234ff3ed07 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectViewerPanel.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectViewerPanel.tsx
@@ -39,11 +39,11 @@ const stylesArray = [styles];
export const ObjectViewerPanel = observer(function ObjectViewerPanel({
tab, handler,
}: TabHandlerPanelProps) {
- const children = useChildren(tab.handlerState.objectId);
+ const { children, isLoading } = useChildren(tab.handlerState.objectId);
const dbObjectPagesService = useService(DBObjectPageService);
const pages = dbObjectPagesService.orderedPages;
- if (!children?.isLoaded) {
+ if (!children || isLoading) {
return ;
}
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectViewerTab.tsx b/webapp/packages/object-viewer-plugin/src/ObjectViewerTab.tsx
index 687e5c9cb1..baa313aae3 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectViewerTab.tsx
+++ b/webapp/packages/object-viewer-plugin/src/ObjectViewerTab.tsx
@@ -18,7 +18,7 @@ import { IObjectViewerTabState } from './IObjectViewerTabState';
export const ObjectViewerTab = observer(function ObjectViewerTab({
tab, handler, onSelect, onClose, style,
}: TabHandlerTabProps) {
- const node = useNode(tab.handlerState.objectId);
+ const { node } = useNode(tab.handlerState.objectId);
return styled(useStyles(...style))(
diff --git a/webapp/packages/object-viewer-plugin/src/ObjectViewerTabService.ts b/webapp/packages/object-viewer-plugin/src/ObjectViewerTabService.ts
index 1ce1d99241..8f7bb67dcc 100644
--- a/webapp/packages/object-viewer-plugin/src/ObjectViewerTabService.ts
+++ b/webapp/packages/object-viewer-plugin/src/ObjectViewerTabService.ts
@@ -6,10 +6,7 @@
* you may not use this file except in compliance with the License.
*/
-import { observable } from 'mobx';
-
import {
- NodesManagerService,
NavigationTabsService,
INodeNavigationData,
IContextProvider,
@@ -21,6 +18,9 @@ import {
objectCatalogProvider,
objectSchemaProvider,
NavNodeManagerService,
+ DBObjectService,
+ ROOT_NODE_PATH,
+ NavNode,
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { NotificationService } from '@dbeaver/core/eventsLog';
@@ -35,13 +35,15 @@ import { objectViewerTabHandlerKey } from './objectViewerTabHandlerKey';
@injectable()
export class ObjectViewerTabService {
- @observable private propertiesTabLoadingState: Map = new Map();
readonly tabHandler: TabHandler
- constructor(private nodesManagerService: NodesManagerService,
- private dbObjectPageService: DBObjectPageService,
- private notificationService: NotificationService,
- private navigationTabsService: NavigationTabsService) {
+ constructor(
+ private navNodeManagerService: NavNodeManagerService,
+ private dbObjectService: DBObjectService,
+ private dbObjectPageService: DBObjectPageService,
+ private notificationService: NotificationService,
+ private navigationTabsService: NavigationTabsService
+ ) {
this.tabHandler = this.navigationTabsService
.registerTabHandler({
@@ -60,95 +62,9 @@ export class ObjectViewerTabService {
});
}
- getConnection(context: ITab) {
- const nodeInfo = this.nodesManagerService
- .getNodeContainerInfo(context.handlerState.objectId);
-
- if (!nodeInfo.connectionId) {
- return;
- }
- // connection node id differs from connection id
- return NodeManagerUtils.connectionNodeIdToConnectionId(nodeInfo.connectionId);
- }
-
- getDBObjectCatalog(context: ITab) {
- const nodeInfo = this.nodesManagerService
- .getNodeContainerInfo(context.handlerState.objectId);
-
- if (nodeInfo.catalogId) {
- return;
- }
- return nodeInfo.catalogId;
- }
-
- getDBObjectSchema(context: ITab) {
- const nodeInfo = this.nodesManagerService
- .getNodeContainerInfo(context.handlerState.objectId);
-
- if (!nodeInfo.schemaId) {
- return;
- }
- return nodeInfo.schemaId;
- }
-
registerTabHandler() {
- this.nodesManagerService.navigator.addHandler(this.navigationHandler.bind(this));
- }
-
- isTabLoading(id: string): boolean {
- return !this.propertiesTabLoadingState.get(id);
- }
-
- async selectObjectTab(tab: ITab) {
- try {
- const currentPage = this.dbObjectPageService.getPage(tab.handlerState.pageId);
- if (currentPage) {
- await this.dbObjectPageService.selectPage(tab, currentPage);
- }
-
- // TODO: must be loaded by info folder?
- await this.nodesManagerService.loadDatabaseObjectInfo(tab.handlerState.objectId);
- await this.nodesManagerService.loadNodeInfo(tab.handlerState.objectId);
-
- const children = await this.nodesManagerService.loadChildren(tab.handlerState.objectId);
- const folderId = tab.handlerState.folderId;
- const propertiesTabLoadingKey = `${tab.handlerState.objectId}_${folderId}`;
-
- try {
- if (children.length === 0 || !NodeManagerUtils.isDatabaseObject(folderId)) {
- return;
- }
-
- this.propertiesTabLoadingState.set(propertiesTabLoadingKey, false);
- await this.nodesManagerService.loadChildrenDatabaseObjectInfo(folderId);
- } finally {
- this.propertiesTabLoadingState.set(propertiesTabLoadingKey, true);
- }
- } catch (exception) {
- this.notificationService.logException(exception, 'Error in Object Viewer while tab selecting');
- }
- }
-
- async restoreObjectTab(tab: ITab) {
- if (
- typeof tab.handlerState?.folderId === 'string'
- && typeof tab.handlerState?.objectId === 'string'
- && (!tab.handlerState.tabIcon || typeof tab.handlerState.tabIcon === 'string')
- && (!tab.handlerState.tabTitle || typeof tab.handlerState.tabTitle === 'string')
- ) {
- const node = await this.nodesManagerService.loadNodeInfo(tab.handlerState.objectId);
- if (node) {
- tab.handlerState.tabIcon = node.icon;
- tab.handlerState.tabTitle = node.name;
-
- return this.dbObjectPageService.restorePages(tab);
- }
- }
- return false;
- }
-
- async closeObjectTab(tab: ITab) {
- await this.dbObjectPageService.closePages(tab);
+ this.navNodeManagerService.navigator.addHandler(this.navigationHandler.bind(this));
+ this.navNodeManagerService.navNode.onDataUpdate.subscribe(this.updateTabs.bind(this));
}
objectViewerTabContext = async (
@@ -156,7 +72,7 @@ export class ObjectViewerTabService {
data: INodeNavigationData
): Promise => {
const tabInfo = await contexts.getContext(this.navigationTabsService.navigationTabContext);
- const nodeInfo = await contexts.getContext(this.nodesManagerService.navigationNodeContext);
+ const nodeInfo = await contexts.getContext(this.navNodeManagerService.navigationNavNodeContext);
if (data.type !== NavigationType.closeConnection) {
// check if tab already exist for object
@@ -173,6 +89,8 @@ export class ObjectViewerTabService {
handlerId: objectViewerTabHandlerKey,
handlerState: {
objectId: nodeInfo.nodeId,
+ parentId: nodeInfo.parentId,
+ parents: await nodeInfo.getParents(),
folderId: nodeInfo.folderId,
pageId: '',
pagesState: new Map(),
@@ -214,6 +132,115 @@ export class ObjectViewerTabService {
};
}
+ private async updateTabs(data: Map) {
+ for (const tab of this.navigationTabsService.findTabs(isObjectViewerTab(tab => tab.restored))) {
+ if (!data.has(tab.handlerState.objectId)) {
+ await this.navigationTabsService.closeTab(tab.id, true);
+ } else if (tab.id === this.navigationTabsService.currentTabId) {
+ const loaded = this.navNodeManagerService.navNode.isLoaded({
+ nodes: [{ nodeId: tab.handlerState.objectId, parentId: tab.handlerState.parentId }],
+ });
+ const loading = this.navNodeManagerService.navNode.isDataLoading({
+ nodes: [{ nodeId: tab.handlerState.objectId, parentId: tab.handlerState.parentId }],
+ });
+
+ if (!loaded && !loading) {
+ await this.navigationTabsService.selectTab(tab.id);
+ }
+ }
+ }
+ }
+
+ private getConnection(context: ITab) {
+ const nodeInfo = this.navNodeManagerService
+ .getNodeContainerInfo(context.handlerState.objectId);
+
+ if (!nodeInfo.connectionId) {
+ return;
+ }
+ // connection node id differs from connection id
+ return NodeManagerUtils.connectionNodeIdToConnectionId(nodeInfo.connectionId);
+ }
+
+ private getDBObjectCatalog(context: ITab) {
+ const nodeInfo = this.navNodeManagerService
+ .getNodeContainerInfo(context.handlerState.objectId);
+
+ if (nodeInfo.catalogId) {
+ return;
+ }
+ return nodeInfo.catalogId;
+ }
+
+ private getDBObjectSchema(context: ITab) {
+ const nodeInfo = this.navNodeManagerService
+ .getNodeContainerInfo(context.handlerState.objectId);
+
+ if (!nodeInfo.schemaId) {
+ return;
+ }
+ return nodeInfo.schemaId;
+ }
+
+ private async selectObjectTab(tab: ITab) {
+ try {
+ const currentPage = this.dbObjectPageService.getPage(tab.handlerState.pageId);
+ if (currentPage) {
+ await this.dbObjectPageService.selectPage(tab, currentPage);
+ }
+
+ // TODO: must be loaded by info folder?
+ await this.navNodeManagerService.loadNode({
+ nodeId: tab.handlerState.objectId,
+ parentId: tab.handlerState.parentId,
+ });
+ const children = await this.navNodeManagerService.loadTree(tab.handlerState.objectId);
+ await this.dbObjectService.load(tab.handlerState.objectId);
+
+ const folderId = tab.handlerState.folderId;
+
+ if (children.length === 0 || !NodeManagerUtils.isDatabaseObject(folderId)) {
+ return;
+ }
+ const folderChildren = await this.navNodeManagerService.loadTree(folderId);
+
+ await this.dbObjectService.load(folderChildren);
+ } catch (exception) {
+ this.notificationService.logException(exception, 'Error in Object Viewer while tab selecting');
+ }
+ }
+
+ private async restoreObjectTab(tab: ITab) {
+ if (
+ typeof tab.handlerState?.folderId === 'string'
+ && typeof tab.handlerState.parentId === 'string'
+ && Array.isArray(tab.handlerState.parents)
+ && typeof tab.handlerState.objectId === 'string'
+ && (!tab.handlerState.tabIcon || typeof tab.handlerState.tabIcon === 'string')
+ && (!tab.handlerState.tabTitle || typeof tab.handlerState.tabTitle === 'string')
+ ) {
+ for (const nodeId of tab.handlerState.parents) {
+ await this.navNodeManagerService.loadTree(nodeId);
+ }
+
+ const node = await this.navNodeManagerService.loadNode({
+ nodeId: tab.handlerState.objectId,
+ parentId: tab.handlerState.parentId,
+ });
+ if (node) {
+ tab.handlerState.tabIcon = node.icon;
+ tab.handlerState.tabTitle = node.name;
+
+ return this.dbObjectPageService.restorePages(tab);
+ }
+ }
+ return false;
+ }
+
+ private async closeObjectTab(tab: ITab) {
+ await this.dbObjectPageService.closePages(tab);
+ }
+
private async navigationHandler(contexts: IContextProvider) {
try {
const { tab, tabInfo, nodeInfo } = await contexts.getContext(this.objectViewerTabContext);
diff --git a/webapp/packages/object-viewer-plugin/src/useObjectFolder.ts b/webapp/packages/object-viewer-plugin/src/useObjectFolder.ts
index d2e5cc3e47..61d3af9f1e 100644
--- a/webapp/packages/object-viewer-plugin/src/useObjectFolder.ts
+++ b/webapp/packages/object-viewer-plugin/src/useObjectFolder.ts
@@ -6,14 +6,18 @@
* you may not use this file except in compliance with the License.
*/
+import { NavNodeManagerService, DBObjectService } from '@dbeaver/core/app';
import { useService } from '@dbeaver/core/di';
-import { ObjectViewerTabService } from './ObjectViewerTabService';
-export function useObjectFolder(objectId: string, folderId: string) {
- const objectViewerTabService = useService(ObjectViewerTabService);
+export function useObjectFolder(objectId: string) {
+ const navNodeManagerService = useService(NavNodeManagerService);
+ const dbObjectService = useService(DBObjectService);
- const isLoading = objectViewerTabService.isTabLoading(`${objectId}_${folderId}`);
+ const children = navNodeManagerService.getTree(objectId) || [];
+
+ const isLoading = children.some(navNodeId => !dbObjectService.getDBObject(navNodeId))
+ && dbObjectService.dbObject.isDataLoading({ navNodeId: children });
return { isLoading };
}
diff --git a/webapp/packages/sql-editor/src/SqlEditorBootstrap.ts b/webapp/packages/sql-editor/src/SqlEditorBootstrap.ts
index 9898a3b684..6393deb63c 100644
--- a/webapp/packages/sql-editor/src/SqlEditorBootstrap.ts
+++ b/webapp/packages/sql-editor/src/SqlEditorBootstrap.ts
@@ -11,7 +11,7 @@ import {
NavigationTreeContextMenuService,
EObjectFeature,
NodeManagerUtils,
- NodeWithParent, ConnectionsManagerService,
+ NavNode, ConnectionsManagerService,
} from '@dbeaver/core/app';
import { injectable } from '@dbeaver/core/di';
import { ContextMenuService, IContextMenuItem, IMenuContext } from '@dbeaver/core/dialogs';
@@ -21,11 +21,13 @@ import { SqlEditorTabService } from './SqlEditorTabService';
@injectable()
export class SqlEditorBootstrap {
- constructor(private mainMenuService: MainMenuService,
- private contextMenuService: ContextMenuService,
- private connectionsManagerService: ConnectionsManagerService,
- private sqlEditorTabService: SqlEditorTabService,
- private sqlEditorNavigatorService: SqlEditorNavigatorService) {}
+ constructor(
+ private mainMenuService: MainMenuService,
+ private contextMenuService: ContextMenuService,
+ private connectionsManagerService: ConnectionsManagerService,
+ private sqlEditorTabService: SqlEditorTabService,
+ private sqlEditorNavigatorService: SqlEditorNavigatorService
+ ) {}
async bootstrap() {
this.sqlEditorTabService.registerTabHandler();
@@ -40,20 +42,20 @@ export class SqlEditorBootstrap {
}
);
- const openSqlEditor: IContextMenuItem = {
+ const openSqlEditor: IContextMenuItem = {
id: 'open-sql-editor',
isPresent(context) {
return context.contextType === NavigationTreeContextMenuService.nodeContextType
- && Boolean((context.data as NodeWithParent)?.object?.features?.includes(EObjectFeature.dataSource));
+ && context.data.objectFeatures.includes(EObjectFeature.dataSource);
},
title: 'SQL',
order: 2,
- onClick: (context: IMenuContext) => {
+ onClick: (context: IMenuContext) => {
const node = context.data;
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(node.id);
this.sqlEditorNavigatorService.openNewEditor(connectionId);
},
};
- this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), openSqlEditor);
+ this.contextMenuService.addMenuItem(this.contextMenuService.getRootMenuToken(), openSqlEditor);
}
}
diff --git a/webapp/packages/sql-editor/src/SqlEditorNavigatorService.ts b/webapp/packages/sql-editor/src/SqlEditorNavigatorService.ts
index 2ecdf6047c..19b3269214 100644
--- a/webapp/packages/sql-editor/src/SqlEditorNavigatorService.ts
+++ b/webapp/packages/sql-editor/src/SqlEditorNavigatorService.ts
@@ -14,7 +14,7 @@ import {
NavigationService,
IContextProvider,
ITabOptions,
- NodesManagerService,
+ NavNodeManagerService,
INodeNavigationData,
NavigationType,
} from '@dbeaver/core/app';
@@ -57,19 +57,21 @@ export interface SQLEditorAction extends SQLEditorActionContext {
export class SqlEditorNavigatorService {
private readonly navigator: INavigator;
- constructor(private navigationTabsService: NavigationTabsService,
- private connectionsManagerService: ConnectionsManagerService,
- private notificationService: NotificationService,
- private gql: GraphQLService,
- private sqlDialectInfoService: SqlDialectInfoService,
- private nodesManagerService: NodesManagerService,
- private navigationService: NavigationService) {
+ constructor(
+ private navigationTabsService: NavigationTabsService,
+ private connectionsManagerService: ConnectionsManagerService,
+ private notificationService: NotificationService,
+ private gql: GraphQLService,
+ private sqlDialectInfoService: SqlDialectInfoService,
+ private navNodeManagerService: NavNodeManagerService,
+ private navigationService: NavigationService
+ ) {
this.navigator = this.navigationService.createNavigator(
null,
this.navigateHandler.bind(this)
);
- this.nodesManagerService.navigator.addHandler(this.nodeNavigationHandler.bind(this));
+ this.navNodeManagerService.navigator.addHandler(this.nodeNavigationHandler.bind(this));
}
registerTabHandler() {
@@ -100,7 +102,7 @@ export class SqlEditorNavigatorService {
private async nodeNavigationHandler(contexts: IContextProvider) {
try {
- const nodeInfo = await contexts.getContext(this.nodesManagerService.navigationNodeContext);
+ const nodeInfo = await contexts.getContext(this.navNodeManagerService.navigationNavNodeContext);
if (nodeInfo.type === NavigationType.closeConnection) {
for (const tab of this.navigationTabsService.findTabs(