mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #83 from dbeaver/feat-data-refresh
fix(core): connection info loading behavior
This commit is contained in:
+12
-5
@@ -10,9 +10,12 @@ import { useState, useCallback, useEffect } from 'react';
|
||||
|
||||
import { useService } from '@dbeaver/core/di';
|
||||
|
||||
import { EConnectionFeature } from '../../shared/ConnectionsManager/EConnectionFeature';
|
||||
import { useConnectionInfo } from '../../shared/ConnectionsManager/useConnectionInfo';
|
||||
import { NavNode } from '../../shared/NodesManager/EntityTypes';
|
||||
import { EObjectFeature } from '../../shared/NodesManager/EObjectFeature';
|
||||
import { NavNodeManagerService } from '../../shared/NodesManager/NavNodeManagerService';
|
||||
import { NodeManagerUtils } from '../../shared/NodesManager/NodeManagerUtils';
|
||||
import { useNode } from '../../shared/NodesManager/useNode';
|
||||
import { useChildren } from '../../shared/useChildren';
|
||||
import { NavigationTreeService } from '../NavigationTreeService';
|
||||
@@ -32,11 +35,15 @@ 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)
|
||||
);
|
||||
let isExpandedFiltered = isExpanded;
|
||||
|
||||
if (node.objectFeatures.includes(EObjectFeature.dataSource)) {
|
||||
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(nodeId);
|
||||
const { connectionInfo } = useConnectionInfo(connectionId);
|
||||
if (!connectionInfo?.features.includes(EConnectionFeature.connected)) {
|
||||
isExpandedFiltered = false;
|
||||
}
|
||||
}
|
||||
|
||||
const handleDoubleClick = useCallback(
|
||||
() => navNodeManagerService.navToNode(nodeId, parentId),
|
||||
|
||||
@@ -12,7 +12,10 @@ import { Subject, Observable } from 'rxjs';
|
||||
import { injectable } from '@dbeaver/core/di';
|
||||
import { NotificationService } from '@dbeaver/core/eventsLog';
|
||||
|
||||
import { ConnectionsManagerService } from '../shared/ConnectionsManager/ConnectionsManagerService';
|
||||
import { EObjectFeature } from '../shared/NodesManager/EObjectFeature';
|
||||
import { NavNodeManagerService, ROOT_NODE_PATH } from '../shared/NodesManager/NavNodeManagerService';
|
||||
import { NodeManagerUtils } from '../shared/NodesManager/NodeManagerUtils';
|
||||
|
||||
@injectable()
|
||||
export class NavigationTreeService {
|
||||
@@ -22,8 +25,9 @@ export class NavigationTreeService {
|
||||
private nodeSelectSubject: Subject<[string, boolean]>;
|
||||
|
||||
constructor(
|
||||
private NavNodeManagerService: NavNodeManagerService,
|
||||
private notificationService: NotificationService
|
||||
private navNodeManagerService: NavNodeManagerService,
|
||||
private notificationService: NotificationService,
|
||||
private connectionsManagerService: ConnectionsManagerService
|
||||
) {
|
||||
this.nodeSelectSubject = new Subject();
|
||||
this.onNodeSelect = this.nodeSelectSubject.asObservable();
|
||||
@@ -31,7 +35,14 @@ export class NavigationTreeService {
|
||||
|
||||
async loadNestedNodes(id = ROOT_NODE_PATH) {
|
||||
try {
|
||||
await this.NavNodeManagerService.loadTree(id);
|
||||
await this.navNodeManagerService.loadTree(id);
|
||||
const node = this.navNodeManagerService.getNode(id);
|
||||
|
||||
if (node?.objectFeatures.includes(EObjectFeature.dataSource)) {
|
||||
await this.connectionsManagerService.refreshConnectionInfoAsync(
|
||||
NodeManagerUtils.connectionNodeIdToConnectionId(id)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, `Can't load tree node: ${id}`);
|
||||
|
||||
+6
@@ -177,6 +177,12 @@ export class ConnectionSchemaManagerService {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.connectionsManagerService.loadConnectionInfoAsync(connectionId);
|
||||
} catch (exception) {
|
||||
this.notificationService.logException(exception, 'Can\'t load connection info');
|
||||
}
|
||||
|
||||
try {
|
||||
await this.connectionsManagerService.dbDrivers.load();
|
||||
} catch (exception) {
|
||||
|
||||
@@ -37,9 +37,11 @@ export * from './shared/TabEntity/TabEntity';
|
||||
// Enums
|
||||
export * from './shared/NodesManager/ENodeFeature';
|
||||
export * from './shared/NodesManager/EObjectFeature';
|
||||
export * from './shared/ConnectionsManager/EConnectionFeature';
|
||||
|
||||
// hooks
|
||||
export * from './shared/useChildren';
|
||||
export * from './shared/ConnectionsManager/useConnectionInfo';
|
||||
export * from './shared/NavigationTabs/NavigationTabsBar/Tabs/useTab';
|
||||
export * from './shared/NodesManager/useDatabaseObjectInfo';
|
||||
export * from './shared/NodesManager/useNode';
|
||||
|
||||
+105
-17
@@ -20,9 +20,11 @@ import {
|
||||
CachedResource,
|
||||
DatabaseObjectInfo,
|
||||
} from '@dbeaver/core/sdk';
|
||||
import { MetadataMap } from '@dbeaver/core/utils';
|
||||
|
||||
import { NavNodeManagerService } from '../NodesManager/NavNodeManagerService';
|
||||
import { NodeManagerUtils } from '../NodesManager/NodeManagerUtils';
|
||||
import { EConnectionFeature } from './EConnectionFeature';
|
||||
|
||||
export type DBDriver = Pick<
|
||||
DriverInfo,
|
||||
@@ -37,23 +39,47 @@ export type DBDriver = Pick<
|
||||
| 'promotedScore'
|
||||
>
|
||||
export type DBSource = Pick<DataSourceInfo, 'id' | 'name' | 'driverId' | 'description'>
|
||||
export type Connection = Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId'>
|
||||
export type Connection = Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId' | 'features'>
|
||||
export type ObjectContainer = Pick<DatabaseObjectInfo, 'name' | 'description' | 'type' | 'features'>
|
||||
|
||||
type ConnectionLoadArgs = {
|
||||
connectionId: string;
|
||||
close?: boolean;
|
||||
remove?: boolean;
|
||||
connection?: never;
|
||||
}
|
||||
|
||||
type ConnectionSetArgs = {
|
||||
connection: Connection;
|
||||
connectionId?: never;
|
||||
remove?: never;
|
||||
close?: never;
|
||||
}
|
||||
|
||||
type DBDriversMetadata = {
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
type ConnectionInfoMetadata = {
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class ConnectionsManagerService {
|
||||
|
||||
@observable private connectionsMap: Map<string, Connection> = new Map();
|
||||
readonly dbDrivers = new CachedResource(
|
||||
new Map(),
|
||||
this.refreshDriversAsync.bind(this),
|
||||
(_, { loaded }) => loaded,
|
||||
{ loaded: false }
|
||||
);
|
||||
readonly connectionInfo = new CachedResource(
|
||||
new Map(),
|
||||
this.loadConnectionInfo.bind(this),
|
||||
(_, metadata, args) => metadata.get(args.connectionId || args.connection!.id).loaded,
|
||||
new MetadataMap<string, ConnectionInfoMetadata>(() => ({ loaded: false, loading: false })),
|
||||
(_, metadata, args) => metadata.get(args.connectionId || args.connection!.id).loading
|
||||
);
|
||||
readonly connectionObjectContainers = new CachedResource(
|
||||
new Map(),
|
||||
this.refreshObjectContainersAsync.bind(this),
|
||||
@@ -61,7 +87,7 @@ export class ConnectionsManagerService {
|
||||
);
|
||||
|
||||
@computed get connections(): Connection[] {
|
||||
return Array.from(this.connectionsMap.values());
|
||||
return Array.from(this.connectionInfo.data.values());
|
||||
}
|
||||
|
||||
onOpenConnection = new Subject<Connection>();
|
||||
@@ -80,18 +106,35 @@ export class ConnectionsManagerService {
|
||||
return this.dbDrivers.data;
|
||||
}
|
||||
|
||||
async loadConnectionInfoAsync(connectionId: string): Promise<Connection> {
|
||||
const connections = await this.connectionInfo.load({ connectionId });
|
||||
|
||||
return connections.get(connectionId)!;
|
||||
}
|
||||
|
||||
async refreshConnectionInfoAsync(connectionId: string): Promise<Connection> {
|
||||
const connections = await this.connectionInfo.refresh(true, { connectionId });
|
||||
|
||||
return connections.get(connectionId)!;
|
||||
}
|
||||
|
||||
async loadDriversAsync(): Promise<Map<string, DBDriver>> {
|
||||
return this.dbDrivers.load();
|
||||
}
|
||||
|
||||
async addOpenedConnection(connection: Connection) {
|
||||
this.connectionsMap.set(connection.id, connection);
|
||||
await this.connectionInfo.refresh(
|
||||
true,
|
||||
{
|
||||
connection,
|
||||
}
|
||||
);
|
||||
this.onOpenConnection.next(connection);
|
||||
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 {
|
||||
return this.connectionsMap.get(connectionId);
|
||||
return this.connectionInfo.data.get(connectionId);
|
||||
}
|
||||
|
||||
getObjectContainerById(
|
||||
@@ -122,7 +165,7 @@ export class ConnectionsManagerService {
|
||||
async closeConnectionAsync(id: string, skipNodesRefresh?: boolean): Promise<void> {
|
||||
await this.graphQLService.gql.closeConnection({ id });
|
||||
await this.afterConnectionClose(id);
|
||||
this.connectionsMap.delete(id);
|
||||
await this.connectionInfo.refresh(true, { connectionId: id, close: true });
|
||||
|
||||
if (!skipNodesRefresh) {
|
||||
await this.navNodeManagerService.updateRootChildren(); // Update connections list, probably here we must just remove nodes from nodes manager
|
||||
@@ -139,10 +182,9 @@ export class ConnectionsManagerService {
|
||||
const connectionId = NodeManagerUtils.connectionNodeIdToConnectionId(navNodeId);
|
||||
await this.graphQLService.gql.closeConnection({ id: connectionId });
|
||||
await this.afterConnectionClose(connectionId);
|
||||
this.connectionsMap.delete(connectionId);
|
||||
await this.connectionInfo.refresh(true, { connectionId, close: true });
|
||||
|
||||
if (node.objectFeatures.includes('dataSourceTemporary')) {
|
||||
await this.navNodeManagerService.removeNode(navNodeId);
|
||||
} else {
|
||||
await this.navNodeManagerService.refreshNode(navNodeId);
|
||||
}
|
||||
@@ -177,7 +219,10 @@ export class ConnectionsManagerService {
|
||||
|
||||
for (const connection of connectionsToRemove) {
|
||||
await this.afterConnectionClose(connection.id);
|
||||
this.connectionsMap.delete(connection.id);
|
||||
await this.connectionInfo.refresh(true, {
|
||||
connectionId: connection.id,
|
||||
remove: true,
|
||||
});
|
||||
}
|
||||
|
||||
await this.navNodeManagerService.updateRootChildren();
|
||||
@@ -224,14 +269,57 @@ export class ConnectionsManagerService {
|
||||
return data;
|
||||
}
|
||||
|
||||
private async restoreConnection(connection: Connection) {
|
||||
// TODO: Must be loaded based on connection id
|
||||
// const { connection } = await this.graphQLService.gql.connectionState({ id });
|
||||
|
||||
if (this.connectionsMap.has(connection.id)) {
|
||||
return;
|
||||
private async loadConnectionInfo(
|
||||
data: Map<string, Connection>,
|
||||
metadata: MetadataMap<string, ConnectionInfoMetadata>,
|
||||
load: boolean,
|
||||
args: ConnectionLoadArgs | ConnectionSetArgs,
|
||||
): Promise<Map<string, Connection>> {
|
||||
let connectionId: string;
|
||||
if (args.connection) {
|
||||
connectionId = args.connection.id;
|
||||
} else {
|
||||
connectionId = args.connectionId;
|
||||
}
|
||||
this.connectionsMap.set(connection.id, connection);
|
||||
const connectionInfo = data.get(connectionId);
|
||||
|
||||
const itemMetadata = metadata.get(connectionId);
|
||||
|
||||
if (args.connection) {
|
||||
data.set(connectionId, args.connection);
|
||||
itemMetadata.loaded = true;
|
||||
return data;
|
||||
}
|
||||
|
||||
if (args.remove || (args.close && connectionInfo?.features.includes(EConnectionFeature.temporary))) {
|
||||
data.delete(connectionId);
|
||||
metadata.delete(connectionId);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
if (load) {
|
||||
try {
|
||||
itemMetadata.loading = true;
|
||||
const { connection } = await this.graphQLService.gql.connectionState({ id: connectionId });
|
||||
|
||||
data.set(connectionId, connection);
|
||||
itemMetadata.loaded = true;
|
||||
} finally {
|
||||
itemMetadata.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private async restoreConnection(connection: Connection) {
|
||||
await this.connectionInfo.refresh(
|
||||
true,
|
||||
{
|
||||
connection,
|
||||
}
|
||||
);
|
||||
this.onOpenConnection.next(connection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export enum EConnectionFeature {
|
||||
connected = 'connected',
|
||||
virtual = 'virtual',
|
||||
temporary = 'temporary',
|
||||
readOnly = 'readOnly',
|
||||
provided = 'provided'
|
||||
}
|
||||
@@ -4,5 +4,6 @@ query connectionState($id: ID!) {
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@ mutation createConnection($config: ConnectionConfig!) {
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,6 @@ mutation openConnection($config: ConnectionConfig!) {
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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 { useCallback } from 'react';
|
||||
|
||||
import { useService } from '@dbeaver/core/di';
|
||||
|
||||
import { ConnectionsManagerService } from './ConnectionsManagerService';
|
||||
|
||||
export function useConnectionInfo(connectionId: string) {
|
||||
const service = useService(ConnectionsManagerService);
|
||||
|
||||
const connectionInfo = service.getConnectionById(connectionId);
|
||||
const load = useCallback(() => service.loadConnectionInfoAsync(connectionId), [service, connectionId]);
|
||||
const refresh = useCallback(() => service.refreshConnectionInfoAsync(connectionId), [service, connectionId]);
|
||||
const isLoading = useCallback(() => service.connectionInfo.isDataLoading({ connectionId }), [service, connectionId]);
|
||||
const isLoaded = useCallback(() => service.connectionInfo.isLoaded({ connectionId }), [service, connectionId]);
|
||||
|
||||
return {
|
||||
connectionInfo,
|
||||
isLoading,
|
||||
isLoaded,
|
||||
load,
|
||||
refresh,
|
||||
};
|
||||
}
|
||||
@@ -166,6 +166,7 @@ export class NavNodeManagerService {
|
||||
|
||||
async updateRootChildren() {
|
||||
if (!await this.permissionsService.hasAsync(EPermission.public)) {
|
||||
await this.navTree.refresh(true, ROOT_NODE_PATH, true);
|
||||
return;
|
||||
}
|
||||
await this.navTree.refresh(true, ROOT_NODE_PATH, false);
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { SessionSettingsService } from '../settings/SessionSettingsService';
|
||||
|
||||
export type SessionState = Pick<SessionInfo, 'createTime' | 'cacheExpired' | 'lastAccessTime' | 'locale'> & {
|
||||
connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId'>>;
|
||||
connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'connected' | 'driverId' | 'features'>>;
|
||||
};
|
||||
|
||||
@injectable()
|
||||
|
||||
@@ -9,6 +9,7 @@ mutation openSession {
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ query sessionState {
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -701,14 +701,14 @@ export type ConnectionStateQueryVariables = {
|
||||
};
|
||||
|
||||
|
||||
export type ConnectionStateQuery = { connection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected'> };
|
||||
export type ConnectionStateQuery = { connection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features'> };
|
||||
|
||||
export type CreateConnectionMutationVariables = {
|
||||
config: ConnectionConfig;
|
||||
};
|
||||
|
||||
|
||||
export type CreateConnectionMutation = { createConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected'> };
|
||||
export type CreateConnectionMutation = { createConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features'> };
|
||||
|
||||
export type DataSourceListQueryVariables = {};
|
||||
|
||||
@@ -742,7 +742,7 @@ export type OpenConnectionMutationVariables = {
|
||||
};
|
||||
|
||||
|
||||
export type OpenConnectionMutation = { openConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected'> };
|
||||
export type OpenConnectionMutation = { openConnection: Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features'> };
|
||||
|
||||
export type TestConnectionMutationVariables = {
|
||||
config: ConnectionConfig;
|
||||
@@ -1118,7 +1118,7 @@ export type OpenSessionMutationVariables = {};
|
||||
|
||||
export type OpenSessionMutation = { session: (
|
||||
Pick<SessionInfo, 'createTime' | 'lastAccessTime' | 'cacheExpired' | 'locale'>
|
||||
& { connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected'>> }
|
||||
& { connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features'>> }
|
||||
); };
|
||||
|
||||
export type ServerConfigQueryVariables = {};
|
||||
@@ -1139,7 +1139,7 @@ export type SessionStateQueryVariables = {};
|
||||
|
||||
export type SessionStateQuery = { sessionState: (
|
||||
Pick<SessionInfo, 'createTime' | 'lastAccessTime' | 'locale' | 'cacheExpired'>
|
||||
& { connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected'>> }
|
||||
& { connections: Array<Pick<ConnectionInfo, 'id' | 'name' | 'driverId' | 'connected' | 'features'>> }
|
||||
); };
|
||||
|
||||
export type TouchSessionMutationVariables = {};
|
||||
@@ -1178,6 +1178,7 @@ export const ConnectionStateDocument = `
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1188,6 +1189,7 @@ export const CreateConnectionDocument = `
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1248,6 +1250,7 @@ export const OpenConnectionDocument = `
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -1766,6 +1769,7 @@ export const OpenSessionDocument = `
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1809,6 +1813,7 @@ export const SessionStateDocument = `
|
||||
name
|
||||
driverId
|
||||
connected
|
||||
features
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user