Merge pull request #82 from dbeaver/feat-data-refresh

fix(core): nav tree error handling
This commit is contained in:
Serge Rider
2020-06-12 16:24:22 +03:00
committed by GitHub
8 changed files with 106 additions and 23 deletions
@@ -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);
},
}
);
@@ -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);
},
};
@@ -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
}
}
}
+18 -1
View File
@@ -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 = `