mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #82 from dbeaver/feat-data-refresh
fix(core): nav tree error handling
This commit is contained in:
@@ -63,7 +63,7 @@ export class NavigationTreeContextMenuService {
|
||||
title: 'app_navigationTree_refreshNode',
|
||||
onClick: (context) => {
|
||||
const node = context.data;
|
||||
this.navNodeManagerService.refresh(node.id);
|
||||
this.navNodeManagerService.refreshTree(node.id);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
+15
-7
@@ -32,6 +32,11 @@ export function useNavigationTree(nodeId: string, parentId: string) {
|
||||
|
||||
const isLoaded = children.isLoaded;
|
||||
const isExpandable = isExpandableFilter(node) && (!isLoaded || children.children!.length > 0);
|
||||
const isExpandedFiltered = isExpanded
|
||||
&& (
|
||||
!node.objectFeatures.includes(EObjectFeature.dataSource)
|
||||
|| node.objectFeatures.includes(EObjectFeature.dataSourceConnected)
|
||||
);
|
||||
|
||||
const handleDoubleClick = useCallback(
|
||||
() => navNodeManagerService.navToNode(nodeId, parentId),
|
||||
@@ -40,15 +45,16 @@ export function useNavigationTree(nodeId: string, parentId: string) {
|
||||
|
||||
const handleExpand = useCallback(
|
||||
async () => {
|
||||
if (!isExpanded) {
|
||||
if (!isExpandedFiltered) {
|
||||
const state = await navigationTreeService.loadNestedNodes(nodeId);
|
||||
if (!state) {
|
||||
switchExpand(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
switchExpand(!isExpanded);
|
||||
switchExpand(!isExpandedFiltered);
|
||||
},
|
||||
[isExpanded, nodeId]
|
||||
[isExpandedFiltered, nodeId]
|
||||
);
|
||||
|
||||
const handleSelect = useCallback(
|
||||
@@ -69,10 +75,12 @@ export function useNavigationTree(nodeId: string, parentId: string) {
|
||||
}, [isExpandable && hasChildren]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isExpanded && !children.isLoaded && !children.isLoading && !!children.children && nodeLoaded) {
|
||||
navigationTreeService.loadNestedNodes(nodeId);
|
||||
if (isExpandedFiltered && !children.isLoaded && !children.isLoading && !!children.children && nodeLoaded) {
|
||||
navigationTreeService
|
||||
.loadNestedNodes(nodeId)
|
||||
.then(state => !state && switchExpand(false));
|
||||
}
|
||||
}, [isExpanded, children.isLoaded, children.isLoading, children.children, nodeLoaded, nodeId]);
|
||||
}, [isExpandedFiltered, children.isLoaded, children.isLoading, children.children, nodeLoaded, nodeId]);
|
||||
|
||||
// Here we subscribe to selected nodes if current node selected (mobx)
|
||||
if (isSelected && !navigationTreeService.isNodeSelected(nodeId)) {
|
||||
@@ -90,7 +98,7 @@ export function useNavigationTree(nodeId: string, parentId: string) {
|
||||
node,
|
||||
nodeType,
|
||||
icon,
|
||||
isExpanded,
|
||||
isExpanded: isExpandedFiltered,
|
||||
isLoaded,
|
||||
isLoading: children.isLoading,
|
||||
isExpandable,
|
||||
|
||||
@@ -61,8 +61,7 @@ export class ConnectionDialogsService {
|
||||
title: 'Disconnect',
|
||||
onClick: (context: IMenuContext<NavNode>) => {
|
||||
const node = context.data;
|
||||
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(node.id);
|
||||
this.connectionsManagerService.closeConnectionAsync(connectionId);
|
||||
this.connectionsManagerService.closeNavNodeConnectionAsync(node.id);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+28
-2
@@ -10,6 +10,7 @@ import { computed, observable } from 'mobx';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
import { SessionService } from '@dbeaver/core/root';
|
||||
import {
|
||||
ConnectionInfo,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
} from '@dbeaver/core/sdk';
|
||||
|
||||
import { NavNodeManagerService } from '../NodesManager/NavNodeManagerService';
|
||||
import { NodeManagerUtils } from '../NodesManager/NodeManagerUtils';
|
||||
|
||||
export type DBDriver = Pick<
|
||||
DriverInfo,
|
||||
@@ -68,7 +70,8 @@ export class ConnectionsManagerService {
|
||||
constructor(
|
||||
private graphQLService: GraphQLService,
|
||||
private navNodeManagerService: NavNodeManagerService,
|
||||
private sessionService: SessionService
|
||||
private sessionService: SessionService,
|
||||
private notificationService: NotificationService
|
||||
) {
|
||||
this.sessionService.onUpdate.subscribe(this.restoreConnections.bind(this));
|
||||
}
|
||||
@@ -126,13 +129,36 @@ export class ConnectionsManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
async closeNavNodeConnectionAsync(navNodeId: string): Promise<void> {
|
||||
const node = this.navNodeManagerService.getNode(navNodeId);
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(navNodeId);
|
||||
await this.graphQLService.gql.closeConnection({ id: connectionId });
|
||||
await this.afterConnectionClose(connectionId);
|
||||
this.connectionsMap.delete(connectionId);
|
||||
|
||||
if (node.objectFeatures.includes('dataSourceTemporary')) {
|
||||
await this.navNodeManagerService.removeNode(navNodeId);
|
||||
} else {
|
||||
await this.navNodeManagerService.refreshNode(navNodeId);
|
||||
}
|
||||
await this.navNodeManagerService.removeTree(navNodeId);
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, `Can't close connection: ${navNodeId}`);
|
||||
}
|
||||
}
|
||||
|
||||
async loadObjectContainer(connectionId: string, catalogId?: string): Promise<ObjectContainer[]> {
|
||||
const data = await this.connectionObjectContainers.load(connectionId, catalogId);
|
||||
return data.get(connectionId)!;
|
||||
}
|
||||
|
||||
private async afterConnectionClose(id: string) {
|
||||
await this.navNodeManagerService.remove(id);
|
||||
await this.navNodeManagerService.removeTree(id);
|
||||
this.onCloseConnection.next(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,4 +16,6 @@ export enum EObjectFeature {
|
||||
'schema' = 'schema',
|
||||
'catalog' = 'catalog',
|
||||
'dataSource' = 'dataSource', // connection
|
||||
'dataSourceTemporary' = 'dataSourceTemporary',
|
||||
'dataSourceConnected' = 'dataSourceConnected',
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ export interface NavNodeKey {
|
||||
|
||||
export interface NavNodeValue {
|
||||
node: NavNodeInfo;
|
||||
parentId: string;
|
||||
parentId?: string;
|
||||
}
|
||||
|
||||
export interface INodeContainerInfo {
|
||||
@@ -157,7 +157,7 @@ export class NavNodeManagerService {
|
||||
});
|
||||
}
|
||||
|
||||
async refresh(navNodeId: string) {
|
||||
async refreshTree(navNodeId: string) {
|
||||
await this.graphQLService.gql.navRefreshNode({
|
||||
nodePath: navNodeId,
|
||||
});
|
||||
@@ -191,6 +191,14 @@ export class NavNodeManagerService {
|
||||
return this.getTree(navNodeId)!;
|
||||
}
|
||||
|
||||
async removeTree(path = ROOT_NODE_PATH) {
|
||||
await this.navTree.refresh(true, path, true);
|
||||
}
|
||||
|
||||
async refreshNode(navNodeId: string) {
|
||||
await this.navNode.refresh(true, { navNodeId: [navNodeId] });
|
||||
}
|
||||
|
||||
getNode(navNodeId: string): NavNode | undefined
|
||||
getNode(navNodeKey: NavNodeKey): NavNode | undefined
|
||||
getNode(navNodeKey: NavNodeKey[]): (NavNode | undefined)[]
|
||||
@@ -239,6 +247,13 @@ export class NavNodeManagerService {
|
||||
return this.getNode(nodes);
|
||||
}
|
||||
|
||||
async removeNode(navNodeId = ROOT_NODE_PATH) {
|
||||
await this.navNode.refresh(true, {
|
||||
navNodeId: [navNodeId],
|
||||
remove: true,
|
||||
});
|
||||
}
|
||||
|
||||
getParent(node: NavNode) {
|
||||
return this.navNode.data.get(node.parentId);
|
||||
}
|
||||
@@ -283,10 +298,6 @@ export class NavNodeManagerService {
|
||||
return scanParents(initial, nodeId);
|
||||
}
|
||||
|
||||
async remove(path = ROOT_NODE_PATH) {
|
||||
await this.navTree.refresh(true, path, true);
|
||||
}
|
||||
|
||||
navigationNavNodeContext = async (
|
||||
contexts: IContextProvider<INodeNavigationData>,
|
||||
data: INodeNavigationData
|
||||
@@ -369,10 +380,12 @@ export class NavNodeManagerService {
|
||||
if (data.nodesValue) {
|
||||
for (const nodeValue of data.nodesValue) {
|
||||
const itemMetadata = metadata.get(nodeValue.node.id);
|
||||
const parentId = navNode.get(nodeValue.node.id)?.parentId || nodeValue.parentId || ROOT_NODE_PATH;
|
||||
|
||||
navNode.set(nodeValue.node.id, {
|
||||
...nodeValue.node,
|
||||
objectFeatures: nodeValue.node.object?.features || [],
|
||||
parentId: nodeValue.parentId,
|
||||
parentId,
|
||||
});
|
||||
itemMetadata.loaded = true;
|
||||
}
|
||||
@@ -448,12 +461,12 @@ export class NavNodeManagerService {
|
||||
if (load) {
|
||||
itemMetadata.loading = true;
|
||||
|
||||
const { navNodeChildren } = await this.graphQLService.gql.navNodeChildren({
|
||||
const { navNodeChildren, navNodeInfo } = await this.graphQLService.gql.navNodeChildren({
|
||||
parentPath: parentId,
|
||||
});
|
||||
|
||||
await this.navNode.refresh(true, {
|
||||
nodesValue: navNodeChildren.map(node => ({ node, parentId })),
|
||||
nodesValue: [{ node: navNodeInfo }, ...navNodeChildren.map(node => ({ node, parentId }))],
|
||||
});
|
||||
navTree.set(parentId, navNodeChildren.map(node => node.id));
|
||||
itemMetadata.loaded = true;
|
||||
@@ -480,7 +493,10 @@ export class NavNodeManagerService {
|
||||
}
|
||||
|
||||
const nestedChildren = this.getNestedChildren(childrenToRemove);
|
||||
await this.navNode.refresh(true, { navNodeId: nestedChildren, remove: true });
|
||||
await this.navNode.refresh(true, {
|
||||
navNodeId: nestedChildren.filter(navNodeId => navNodeId !== parentId),
|
||||
remove: true,
|
||||
});
|
||||
for (const navNodeId of nestedChildren) {
|
||||
navTree.delete(navNodeId);
|
||||
metadata.delete(navNodeId);
|
||||
|
||||
@@ -13,4 +13,19 @@ query navNodeChildren($parentPath: ID!) {
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
navNodeInfo(nodePath: $parentPath) {
|
||||
id
|
||||
name
|
||||
hasChildren
|
||||
nodeType
|
||||
icon
|
||||
folder
|
||||
inline
|
||||
navigable
|
||||
features
|
||||
object {
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,7 +784,10 @@ export type NavNodeChildrenQueryVariables = {
|
||||
export type NavNodeChildrenQuery = { navNodeChildren: Array<(
|
||||
Pick<NavigatorNodeInfo, 'id' | 'name' | 'hasChildren' | 'nodeType' | 'icon' | 'folder' | 'inline' | 'navigable' | 'features'>
|
||||
& { object?: Maybe<Pick<DatabaseObjectInfo, 'features'>> }
|
||||
)>; };
|
||||
)>; navNodeInfo: (
|
||||
Pick<NavigatorNodeInfo, 'id' | 'name' | 'hasChildren' | 'nodeType' | 'icon' | 'folder' | 'inline' | 'navigable' | 'features'>
|
||||
& { object?: Maybe<Pick<DatabaseObjectInfo, 'features'>> }
|
||||
); };
|
||||
|
||||
export type NavNodeInfoQueryVariables = {
|
||||
nodePath: Scalars['ID'];
|
||||
@@ -1308,6 +1311,20 @@ export const NavNodeChildrenDocument = `
|
||||
features
|
||||
}
|
||||
}
|
||||
navNodeInfo(nodePath: $parentPath) {
|
||||
id
|
||||
name
|
||||
hasChildren
|
||||
nodeType
|
||||
icon
|
||||
folder
|
||||
inline
|
||||
navigable
|
||||
features
|
||||
object {
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const NavNodeInfoDocument = `
|
||||
|
||||
Reference in New Issue
Block a user