Add AsyncServerTree (#11838)

* wip

* Fixes

* More fixes

* more fixes

* Disable when preview features disabled

* remove unused imports

* Handle promises

* PR feedback

* Single default ServerGroup color value
This commit is contained in:
Charles Gagnon
2020-08-19 14:01:10 -07:00
committed by GitHub
parent d2e4eeac88
commit 3c538d1c2d
37 changed files with 1654 additions and 506 deletions

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>

Before

Width:  |  Height:  |  Size: 139 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#212121" d="M1.29.07V16h9.59a3.31,3.31,0,0,1-1.94-1H2.29V11h6a3.31,3.31,0,0,1,.54-.8A1.81,1.81,0,0,1,9,10H2.29v-9h8.53v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path class="cls-1" fill="#212121" d="M3.3,1.8V4.25H5.75V1.8Zm2,2H3.8V2.3H5.25Z"/><circle fill="#3bb44a" cx="11.24" cy="12.52" r="3.47"/></svg>

Before

Width:  |  Height:  |  Size: 373 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#fff" d="M1.29.07V16h9.59a3.31,3.31,0,0,1-1.94-1H2.29V11h6a3.31,3.31,0,0,1,.54-.8A1.81,1.81,0,0,1,9,10H2.29v-9h8.53v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#fff" d="M3.3,1.8V4.25H5.75V1.8Zm2,2H3.8V2.3H5.25Z"/><circle fill="#3bb44a" cx="11.24" cy="12.52" r="3.47"/></svg>

Before

Width:  |  Height:  |  Size: 353 B

View File

@@ -1,138 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* --- Registered servers tree viewlet --- */
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group {
cursor: default;
width: 100%;
display: flex;
align-items: center;
}
/* Bold font style does not go well with CJK fonts */
.server-explorer-viewlet:lang(zh-Hans) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(zh-Hant) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(ja) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group { font-weight: normal; }
/* High Contrast Theming */
.monaco-workbench.hc-black .server-explorer-viewlet .server-group {
line-height: 20px;
}
.monaco-workbench > .activitybar .monaco-action-bar .action-label.serverTree {
background-size: 22px;
background-repeat: no-repeat;
background-position: 50% !important;
}
.server-explorer-viewlet .object-explorer-view {
height: calc(100% - 36px);
}
.server-explorer-viewlet .server-group {
height: 38px;
line-height: 38px;
color: #ffffff;
}
.server-explorer-viewlet .monaco-action-bar .action-label {
margin-right: 0.3em;
margin-left: 0.3em;
line-height: 15px;
width: 10px !important;
height: 10px !important;
}
/* Add space beneath the button */
.new-connection .monaco-text-button {
margin-bottom: 2px;
}
/* display action buttons on hover */
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content {
display: flex;
}
/* Added to display the tree in connection dialog */
.server-explorer-viewlet {
height: 100%;
}
.explorer-servers {
height: 100%;
}
/* search box */
.server-explorer-viewlet .search-box {
padding-bottom: 4px;
margin: auto;
width: 95%;
}
/* OE and connection element group */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group {
padding: 5px;
overflow: hidden;
}
/* OE and connection label */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .label,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label {
text-overflow: ellipsis;
overflow: hidden;
}
/* OE and connection icon */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon {
float: left;
height: 16px;
width: 16px;
padding-right: 10px;
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected {
background: url('connected_active_server.svg') center center no-repeat;
}
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected,
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected{
background: url('connected_active_server_inverse.svg') center center no-repeat;
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected {
background: url('disconnected_server.svg') center center no-repeat;
}
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected,
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected{
background: url('disconnected_server_inverse.svg') center center no-repeat;
}
/* loading for OE node */
.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .codicon.in-progress .connection-tile:before,
.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .codicon.in-progress .object-element-group:before {
position: absolute;
display: block;
width: 36px;
height: 100%;
top: 0;
left: -35px;
}
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before {
background: url('expanded-dark.svg') 50% 50% no-repeat;
}
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before {
background: url('collapsed-dark.svg') 50% 50% no-repeat;
}
/* Add connection button */
.server-explorer-viewlet .button-section {
padding: 20px;
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#212121" d="M1.42.06V16H11a3.31,3.31,0,0,1-1.94-1H2.42V11h6a3.31,3.31,0,0,1,.54-.8,1.81,1.81,0,0,1,.19-.2H2.42v-9H11v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#212121" d="M3.43,1.79V4.24H5.89V1.79Zm2,2H3.93V2.29H5.39Z"/><path fill="#d02e00" d="M11.08,16a2.22,2.22,0,0,0,.45,0,2.59,2.59,0,0,0,.4,0Z"/><path fill="#d02e00" d="M12,9.08h0l-.39,0a3.68,3.68,0,0,0-.58,0A3.41,3.41,0,0,0,9.14,10a1.81,1.81,0,0,0-.19.2,3.46,3.46,0,0,0-.89,2.3,3.4,3.4,0,0,0,.85,2.26,1.29,1.29,0,0,0,.16.17A3.31,3.31,0,0,0,11,16H12a3.46,3.46,0,0,0,2.17-1.13,3.41,3.41,0,0,0,.88-2.3A3.47,3.47,0,0,0,12,9.08Zm0,5.72a1.72,1.72,0,0,1-.39,0,2.23,2.23,0,0,1-.77-.14,2.29,2.29,0,0,1-1.54-2.17A2.22,2.22,0,0,1,9.79,11a2.29,2.29,0,0,1,1-.67,2.23,2.23,0,0,1,.77-.14,1.72,1.72,0,0,1,.39,0h0a2.3,2.3,0,0,1,0,4.53Z"/></svg>

Before

Width:  |  Height:  |  Size: 864 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#fff" d="M1.42.06V16H11a3.31,3.31,0,0,1-1.94-1H2.42V11h6a3.31,3.31,0,0,1,.54-.8,1.81,1.81,0,0,1,.19-.2H2.42v-9H11v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#fff" d="M3.43,1.79V4.24H5.89V1.79Zm2,2H3.93V2.29H5.39Z"/><path fill="#d02e00" d="M11.08,16a2.22,2.22,0,0,0,.45,0,2.59,2.59,0,0,0,.4,0Z"/><path fill="#d02e00" d="M12,9.08h0l-.39,0a3.68,3.68,0,0,0-.58,0A3.41,3.41,0,0,0,9.14,10a1.81,1.81,0,0,0-.19.2,3.46,3.46,0,0,0-.89,2.3,3.4,3.4,0,0,0,.85,2.26,1.29,1.29,0,0,0,.16.17A3.31,3.31,0,0,0,11,16H12a3.46,3.46,0,0,0,2.17-1.13,3.41,3.41,0,0,0,.88-2.3A3.47,3.47,0,0,0,12,9.08Zm0,5.72a1.72,1.72,0,0,1-.39,0,2.23,2.23,0,0,1-.77-.14,2.29,2.29,0,0,1-1.54-2.17A2.22,2.22,0,0,1,9.79,11a2.29,2.29,0,0,1,1-.67,2.23,2.23,0,0,1,.77-.14,1.72,1.72,0,0,1,.39,0h0a2.3,2.3,0,0,1,0,4.53Z"/></svg>

Before

Width:  |  Height:  |  Size: 858 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>

Before

Width:  |  Height:  |  Size: 118 B

View File

@@ -14,7 +14,6 @@ import { Action } from 'vs/base/common/actions';
import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler';
import { ObjectExplorerActionsContext, getTreeNode } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
@@ -91,7 +90,7 @@ export class OEManageConnectionAction extends Action {
private async doManage(actionContext: ObjectExplorerActionsContext): Promise<boolean> {
let treeNode: TreeNode = undefined;
let connectionProfile: IConnectionProfile = undefined;
let connectionProfile: ConnectionProfile = undefined;
if (actionContext instanceof ObjectExplorerActionsContext) {
// Must use a real connection profile for this action due to lookup
connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, actionContext.connectionProfile);

View File

@@ -18,13 +18,14 @@ import {
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IViewDescriptorService } from 'vs/workbench/common/views';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ILogService } from 'vs/platform/log/common/log';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
export class ConnectionViewletPanel extends ViewPane {
@@ -82,7 +83,7 @@ export class ConnectionViewletPanel extends ViewPane {
this._root = container;
}
get serversTree(): ITree {
get serversTree(): ITree | AsyncServerTree {
return this._serverTreeView.tree;
}

View File

@@ -4,7 +4,8 @@
*--------------------------------------------------------------------------------------------*/
/* --- Registered servers tree viewlet --- */
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group {
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group,
.server-explorer-viewlet .monaco-list .monaco-list-row .content .server-group {
cursor: default;
width: 100%;
display: flex;
@@ -15,7 +16,13 @@
.server-explorer-viewlet:lang(zh-Hans) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(zh-Hant) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(ja) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group { font-weight: normal; }
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group,
.server-explorer-viewlet:lang(zh-Hans) .monaco-list .monaco-list-row .server-group,
.server-explorer-viewlet:lang(zh-Hant) .monaco-list .monaco-list-row .server-group,
.server-explorer-viewlet:lang(ja) .monaco-list .monaco-list-row .server-group,
.server-explorer-viewlet:lang(ko) .monaco-list .monaco-list-row .server-group {
font-weight: normal;
}
/* High Contrast Theming */
.monaco-workbench.hc-black .server-explorer-viewlet .server-group {
@@ -48,14 +55,19 @@
/* Add space beneath the button */
.new-connection .monaco-text-button {
margin-bottom: 2px;
margin-bottom: 2px;
}
/* display action buttons on hover */
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content {
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content,
.server-explorer-viewlet .monaco-list .monaco-list-row {
display: flex;
}
.server-explorer-viewlet .monaco-tl-row {
width: 100%;
}
/* Added to display the tree in connection dialog */
.server-explorer-viewlet {
height: 100%;
@@ -74,7 +86,9 @@ margin-bottom: 2px;
/* OE and connection element group */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group {
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group,
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile,
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group {
padding-left: 5px;
padding-right: 5px;
padding-top: 3px;
@@ -84,26 +98,33 @@ margin-bottom: 2px;
/* OE and connection label */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .label,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label {
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label,
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .label,
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group > .label {
text-overflow: ellipsis;
overflow: hidden;
}
/* OE and connection icon */
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon,
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon {
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon,
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon,
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group > .icon {
float: left;
height: 16px;
width: 16px;
padding-right: 10px;
}
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page {
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page {
background: url('default_server.svg') center center no-repeat;
}
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page{
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
.vs-dark .monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page,
.hc-black .monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page {
background: url('default_server_inverse.svg') center center no-repeat;
}
@@ -118,11 +139,40 @@ margin-bottom: 2px;
left: -35px;
}
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before {
.monaco-list .connection-tile > .icon.server-page::after {
position: absolute;
height: 0.25rem;
width: 0.25rem;
top: 14px;
left: 45px;
border-radius: 100%;
content:"";
font-size: 100%;
line-height: 100%;
color:white;
text-align:center;
vertical-align:middle;
}
/* Connected badge */
.monaco-list .connection-tile > .icon.server-page.connected::after {
border: 0.12rem solid rgba(59, 180, 74, 100%);
background: rgba(59, 180, 74, 100%);
}
/* Disconnected badge */
.monaco-list .connection-tile > .icon.server-page.disconnected::after {
border: 0.12rem solid rgba(208, 46, 0, 100%);
background: rgba(255, 255, 255, 80%);
}
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before,
.monaco-list .monaco-list-rows.show-twisties > .monaco-list-row.expanded.has-children > .content.server-group:before {
background: url('expanded-dark.svg') 50% 50% no-repeat;
}
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before {
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before,
.monaco-list .monaco-list-rows.show-twisties > .monaco-list-row.has-children > .content.server-group:before {
background: url('collapsed-dark.svg') 50% 50% no-repeat;
}

View File

@@ -38,6 +38,11 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { startsWith } from 'vs/base/common/strings';
import { SERVER_GROUP_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
import { horizontalScrollingKey } from 'vs/platform/list/browser/listService';
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
/**
* ServerTreeview implements the dynamic tree view.
@@ -48,7 +53,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
private _buttonSection: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler;
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
private _tree: ITree;
private _tree: ITree | AsyncServerTree;
private _onSelectionOrFocusChange: Emitter<void>;
private _actionProvider: ServerTreeActionProvider;
@@ -59,7 +64,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
@IThemeService private _themeService: IThemeService,
@IErrorMessageService private _errorMessageService: IErrorMessageService,
@IConfigurationService private _configurationService: IConfigurationService,
@ICapabilitiesService capabilitiesService: ICapabilitiesService
@ICapabilitiesService capabilitiesService: ICapabilitiesService,
@IContextMenuService private _contextMenuService: IContextMenuService,
@IKeybindingService private _keybindingService: IKeybindingService
) {
super();
this._activeConnectionsFilterAction = this._instantiationService.createInstance(
@@ -71,9 +78,17 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._onSelectionOrFocusChange = new Emitter();
this._actionProvider = this._instantiationService.createInstance(ServerTreeActionProvider);
capabilitiesService.onCapabilitiesRegistered(async () => {
if (this._connectionManagementService.hasRegisteredServers()) {
await this.refreshTree();
if (this._tree instanceof AsyncServerTree) {
// Refresh the tree input now that the capabilities are registered so that we can
// get the full ConnectionProfiles with the server info updated properly
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
await this._tree.setInput(treeInput);
this._treeSelectionHandler.onTreeActionStateChange(false);
} else {
if (this._connectionManagementService.hasRegisteredServers()) {
await this.refreshTree();
this._treeSelectionHandler.onTreeActionStateChange(false);
}
}
});
this.registerCommands();
@@ -97,7 +112,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return this._actionProvider;
}
public get tree(): ITree {
public get tree(): ITree | AsyncServerTree {
return this._tree;
}
@@ -143,11 +158,30 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
const horizontalScrollEnabled: boolean = this._configurationService.getValue(horizontalScrollingKey) || false;
this._tree = this._register(TreeCreationUtils.createRegisteredServersTree(container, this._instantiationService, horizontalScrollEnabled));
//this._tree.setInput(undefined);
this._tree = this._register(TreeCreationUtils.createServersTree(container, this._instantiationService, this._configurationService, horizontalScrollEnabled));
this._register(this._tree.onDidChangeSelection((event) => this.onSelected(event)));
this._register(this._tree.onDidBlur(() => this._onSelectionOrFocusChange.fire()));
this._register(this._tree.onDidChangeFocus(() => this._onSelectionOrFocusChange.fire()));
if (this._tree instanceof AsyncServerTree) {
this._register(this._tree.onContextMenu(e => this.onContextMenu(e)));
this._register(this._tree.onMouseDblClick(e => {
// Open dashboard on double click for server and database nodes
let connectionProfile: ConnectionProfile;
if (e.element instanceof ConnectionProfile) {
connectionProfile = e.element;
} else if (e.element instanceof TreeNode) {
if (TreeUpdateUtils.isAvailableDatabaseNode(e.element)) {
connectionProfile = TreeUpdateUtils.getConnectionProfile(e.element);
}
}
if (connectionProfile) {
this._connectionManagementService.showDashboard(connectionProfile);
}
}));
this._register(this._connectionManagementService.onConnectionChanged(() => {
this.refreshTree().catch(err => errors.onUnexpectedError);
}));
}
// Theme styler
this._register(attachListStyler(this._tree, this._themeService));
@@ -171,7 +205,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this.showError(args.errorMessage);
}
if (args.connection) {
this.onObjectExplorerSessionCreated(args.connection);
this.onObjectExplorerSessionCreated(args.connection).catch(err => errors.onUnexpectedError);
}
}));
}
@@ -182,7 +216,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const expandGroups: boolean = this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
if (expandGroups) {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(root).map(subgroup => {
return this._tree.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
}
}
if (root && !root.hasValidConnections) {
@@ -200,30 +241,55 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return uri && startsWith(uri, ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri;
}
private async handleAddConnectionProfile(newProfile: IConnectionProfile): Promise<void> {
if (newProfile) {
const groups = this._connectionManagementService.getConnectionGroups();
const profile = ConnectionUtils.findProfileInGroup(newProfile, groups);
if (profile) {
newProfile = profile;
}
}
private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise<void> {
if (this._buttonSection) {
hide(this._buttonSection);
this._activeConnectionsFilterAction.enabled = true;
}
const currentSelections = this._tree.getSelection();
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
if (newProfile && currentSelectedElement && !newProfileIsSelected) {
this._tree.clearSelection();
}
await this.refreshTree();
if (newProfile && !newProfileIsSelected) {
await this._tree.reveal(newProfile);
this._tree.select(newProfile);
if (this._tree instanceof AsyncServerTree) {
// When new connection groups are added the event is fired with undefined so
// we still want to refresh the tree in that case to pick up the changes
await this.refreshTree();
if (newProfile) {
const currentSelections = this._tree.getSelection();
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
const newProfileIsSelected = currentSelectedElement && currentSelectedElement.id === newProfile.id;
// Clear any other selected elements first
if (currentSelectedElement && !newProfileIsSelected) {
this._tree.setSelection([]);
}
const newConnectionProfile = this.getConnectionInTreeInput(newProfile.id);
if (newConnectionProfile) {
// Re-render to update the connection status badge
this._tree.rerender(newConnectionProfile);
this._tree.setSelection([newConnectionProfile]);
this._tree.expand(newConnectionProfile);
}
}
} else {
if (newProfile) {
const groups = this._connectionManagementService.getConnectionGroups();
const profile = ConnectionUtils.findProfileInGroup(newProfile, groups);
if (profile) {
newProfile = profile;
}
}
const currentSelections = this._tree.getSelection();
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
if (newProfile && currentSelectedElement && !newProfileIsSelected) {
this._tree.clearSelection();
}
await this.refreshTree();
if (newProfile && !newProfileIsSelected) {
await this._tree.reveal(newProfile);
this._tree.select(newProfile);
}
}
}
private showError(errorMessage: string) {
@@ -232,66 +298,83 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
}
}
private getConnectionInTreeInput(connectionId: string): ConnectionProfile {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => {
if (connectionId === con.id) {
return true;
} else {
return false;
/**
* Gets the ConnectionProfile object in the tree for the specified ID, or undefined if it doesn't exist.
* @param connectionId The connection ID to search for
*/
private getConnectionInTreeInput(connectionId: string): ConnectionProfile | undefined {
if (this._tree instanceof AsyncServerTree) {
const root = this._tree.getInput();
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
return connections.find(conn => conn.id === connectionId);
} else {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => {
if (connectionId === con.id) {
return true;
} else {
return false;
}
});
if (results && results.length > 0) {
return results[0];
}
});
if (results && results.length > 0) {
return results[0];
return undefined;
}
return null;
}
private onObjectExplorerSessionCreated(connection: IConnectionProfile) {
const conn = this.getConnectionInTreeInput(connection.id);
if (conn) {
this._tree.refresh(conn).then(() => {
return this._tree.expand(conn).then(() => {
return this._tree.reveal(conn, 0.5).then(() => {
this._treeSelectionHandler.onTreeActionStateChange(false);
});
});
}).then(null, errors.onUnexpectedError);
private async onObjectExplorerSessionCreated(connection: IConnectionProfile): Promise<void> {
const element = this.getConnectionInTreeInput(connection.id);
if (element) {
if (this._tree instanceof AsyncServerTree) {
this._tree.rerender(element);
} else {
await this._tree.refresh(element);
}
await this._tree.expand(element);
await this._tree.reveal(element, 0.5);
this._treeSelectionHandler.onTreeActionStateChange(false);
}
}
public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void {
hide(this.messages);
if (!this._objectExplorerService.getObjectExplorerNode(connection)) {
this._objectExplorerService.updateObjectExplorerNodes(connection).then(() => {
// The oe request is sent. an event will be raised when the session is created
}, error => {
});
this._objectExplorerService.updateObjectExplorerNodes(connection).catch(e => errors.onUnexpectedError(e));
}
}
public deleteObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): Promise<void> {
public async deleteObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): Promise<void> {
if (connection) {
const conn = this.getConnectionInTreeInput(connection.id);
if (conn) {
return this._objectExplorerService.deleteObjectExplorerNode(conn).then(async () => {
await this._objectExplorerService.deleteObjectExplorerNode(conn);
if (this._tree instanceof AsyncServerTree) {
// Collapse the node before refreshing so the refresh doesn't try to fetch
// the children again (which causes it to try and connect)
this._tree.collapse(conn);
await this.refreshTree();
} else {
await this._tree.collapse(conn);
return this._tree.refresh(conn);
});
}
}
}
return Promise.resolve();
}
public refreshTree(): Promise<void> {
public async refreshTree(): Promise<void> {
hide(this.messages);
this.clearOtherActions();
return TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
}
public refreshElement(element: any): Promise<void> {
return this._tree.refresh(element);
public async refreshElement(element: ServerTreeElement): Promise<void> {
if (this._tree instanceof AsyncServerTree) {
return this._tree.updateChildren(element);
} else {
return this._tree.refresh(element);
}
}
/**
@@ -350,9 +433,19 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.setInput(treeInput).then(async () => {
if (isHidden(this.messages)) {
this._tree.getFocus();
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
this._tree.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
}
} else {
this._tree.clearFocus();
if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]);
} else {
this._tree.clearFocus();
}
}
}, errors.onUnexpectedError);
} else {
@@ -382,9 +475,19 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.setInput(treeInput).then(async () => {
if (isHidden(this.messages)) {
this._tree.getFocus();
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
this._tree.expand(subgroup);
}));
} else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
}
} else {
this._tree.clearFocus();
if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]);
} else {
this._tree.clearFocus();
}
}
}, errors.onUnexpectedError);
}
@@ -450,17 +553,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.layout(height);
}
/**
* set the visibility of the view
*/
public setVisible(visible: boolean): void {
if (visible) {
this._tree.onVisible();
} else {
this._tree.onHidden();
}
}
/**
* Get the list of selected nodes in the tree
*/
@@ -472,48 +564,108 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Get whether the tree view currently has focus
*/
public isFocused(): boolean {
return this._tree.isDOMFocused();
return this._tree.getHTMLElement() === document.activeElement;
}
/**
* Set whether the given element is expanded or collapsed
*/
public setExpandedState(element: TreeNode | ConnectionProfile, expandedState: TreeItemCollapsibleState): Promise<void> {
public async setExpandedState(element: ServerTreeElement, expandedState: TreeItemCollapsibleState): Promise<void> {
if (expandedState === TreeItemCollapsibleState.Collapsed) {
return this._tree.collapse(element);
} else if (expandedState === TreeItemCollapsibleState.Expanded) {
return this._tree.expand(element);
}
return Promise.resolve();
}
/**
* Reveal the given element in the tree
*/
public reveal(element: TreeNode | ConnectionProfile): Promise<void> {
public async reveal(element: ServerTreeElement): Promise<void> {
return this._tree.reveal(element);
}
/**
* Select the given element in the tree and clear any other selections
*/
public setSelected(element: TreeNode | ConnectionProfile, selected: boolean, clearOtherSelections: boolean): Promise<void> {
public async setSelected(element: ServerTreeElement, selected: boolean, clearOtherSelections: boolean): Promise<void> {
if (clearOtherSelections || (selected && clearOtherSelections !== false)) {
this._tree.clearSelection();
if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection([]);
} else {
this._tree.clearSelection();
}
}
if (selected) {
this._tree.select(element);
return this._tree.reveal(element);
if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection(this._tree.getSelection().concat(element));
this._tree.reveal(element);
} else {
this._tree.select(element);
return this._tree.reveal(element);
}
} else {
this._tree.deselect(element);
return Promise.resolve();
if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection(this._tree.getSelection().filter(item => item !== element));
} else {
this._tree.deselect(element);
}
}
}
/**
* Check if the given element in the tree is expanded
*/
public isExpanded(element: TreeNode | ConnectionProfile): boolean {
return this._tree.isExpanded(element);
public isExpanded(element: ServerTreeElement): boolean {
if (this._tree instanceof AsyncServerTree) {
return !this._tree.getNode(element).collapsed;
} else {
return this._tree.isExpanded(element);
}
}
/**
* Return actions in the context menu
*/
private onContextMenu(e: ITreeContextMenuEvent<ServerTreeElement>): boolean {
e.browserEvent.preventDefault();
e.browserEvent.stopPropagation();
this._tree.setSelection([e.element]);
let actionContext: any;
if (e.element instanceof TreeNode) {
let context = new ObjectExplorerActionsContext();
context.nodeInfo = e.element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces
// the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = e.element.getConnectionProfile().toIConnectionProfile();
context.connectionProfile.databaseName = e.element.getDatabaseName();
actionContext = context;
} else if (e.element instanceof ConnectionProfile) {
let context = new ObjectExplorerActionsContext();
context.connectionProfile = e.element.toIConnectionProfile();
context.isConnectionNode = true;
actionContext = context;
} else {
// TODO: because the connection group is used as a context object and isn't serializable,
// the Group-level context menu is not currently extensible
actionContext = e.element;
}
this._contextMenuService.showContextMenu({
getAnchor: () => e.anchor,
getActions: () => this._actionProvider.getActions(this._tree, e.element),
getKeyBinding: (action) => this._keybindingService.lookupKeybinding(action.id),
onHide: (wasCancelled?: boolean) => {
if (wasCancelled) {
this._tree.domFocus();
}
},
getActionsContext: () => (actionContext)
});
return true;
}
}

View File

@@ -8,6 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { localize } from 'vs/nls';
import { SERVER_GROUP_CONFIG, SERVER_GROUP_COLORS_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
@@ -29,7 +30,7 @@ const serverGroupConfig: IConfigurationNode = {
'#98AFC7',
'#4452A6',
'#6A6599',
'#515151'
DefaultServerGroupColor
]
},
[SERVER_GROUP_CONFIG + '.' + SERVER_GROUP_AUTOEXPAND_CONFIG]: {
@@ -40,4 +41,18 @@ const serverGroupConfig: IConfigurationNode = {
}
};
const serverTreeConfig: IConfigurationNode = {
'id': 'serverTree',
'title': 'Server Tree',
'type': 'object',
'properties': {
'serverTree.useAsyncServerTree': {
'type': 'boolean',
'default': true,
'description': localize('serverTree.useAsyncServerTree', "(Preview) Use the new async server tree for the Servers view and Connection Dialog with support for new features such as dynamic node filtering.")
}
}
};
configurationRegistry.registerConfiguration(serverGroupConfig);
configurationRegistry.registerConfiguration(serverTreeConfig);

View File

@@ -20,8 +20,6 @@ import * as LocalizedConstants from 'sql/workbench/services/connection/browser/
import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { ServerTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/serverTreeDataSource';
import { Emitter, Event } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
@@ -35,6 +33,12 @@ import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneCo
import { ConsoleLogService } from 'vs/platform/log/common/log';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
import { TestAccessibilityService, TestListService } from 'vs/workbench/test/browser/workbenchTestServices';
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
import { ServerTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/serverTreeDataSource';
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
suite('SQL Connection Tree Action tests', () => {
let errorMessageService: TypeMoq.Mock<TestErrorMessageService>;
@@ -152,8 +156,14 @@ suite('SQL Connection Tree Action tests', () => {
}
};
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(OEManageConnectionAction.ID,
OEManageConnectionAction.LABEL, connectionManagementService.object, capabilitiesService, instantiationService.object, objectExplorerService.object, viewsService);
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(
OEManageConnectionAction.ID,
OEManageConnectionAction.LABEL,
connectionManagementService.object,
capabilitiesService,
instantiationService.object,
objectExplorerService.object,
viewsService);
let actionContext = new ObjectExplorerActionsContext();
actionContext.connectionProfile = connection.toIConnectionProfile();
@@ -190,8 +200,14 @@ suite('SQL Connection Tree Action tests', () => {
return treeSelectionMock.object;
});
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(OEManageConnectionAction.ID,
OEManageConnectionAction.LABEL, connectionManagementService.object, capabilitiesService, instantiationService.object, objectExplorerService.object, undefined);
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(
OEManageConnectionAction.ID,
OEManageConnectionAction.LABEL,
connectionManagementService.object,
capabilitiesService,
instantiationService.object,
objectExplorerService.object,
undefined);
let actionContext = new ObjectExplorerActionsContext();
actionContext.connectionProfile = connection.toIConnectionProfile();
@@ -444,7 +460,7 @@ suite('SQL Connection Tree Action tests', () => {
tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
tree.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID,
let refreshAction: RefreshAction = new RefreshAction(RefreshAction.ID,
RefreshAction.LABEL,
tree.object,
connection,
@@ -453,7 +469,7 @@ suite('SQL Connection Tree Action tests', () => {
undefined,
logService);
return connectionAction.run().then((value) => {
return refreshAction.run().then((value) => {
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
@@ -547,6 +563,200 @@ suite('SQL Connection Tree Action tests', () => {
});
});
// chgagnon TODO - skipping for now since mocking and instanceof don't work well together. Will re-enable once old tree is removed
test.skip('RefreshConnectionAction - AsyncServerTree - refresh should be called if connection status is connect', () => {
let isConnectedReturnValue: boolean = true;
let sqlProvider = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: [],
};
capabilitiesService.capabilities[mssqlProviderName] = { connection: sqlProvider };
let connection = new ConnectionProfile(capabilitiesService, {
connectionName: 'Test',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'testServerName',
databaseName: 'testDatabaseName',
authenticationType: 'inetgrated',
password: 'test',
userName: 'testUsername',
groupId: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: 'testID'
});
let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
conProfGroup.connections = [connection];
let connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
resolve(connection);
}));
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
let objectExplorerSession = {
success: true,
sessionId: '1234',
rootNode: {
nodePath: 'testServerName\tables',
nodeType: NodeType.Folder,
label: 'Tables',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
},
errorMessage: ''
};
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined);
tablesNode.connection = connection;
tablesNode.session = objectExplorerSession;
let table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
let table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
tablesNode.children = [table1Node, table2Node];
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
objectExplorerService.callBase = true;
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode);
objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node]));
const treeMock = TypeMoq.Mock.ofType(AsyncServerTree, TypeMoq.MockBehavior.Strict,
'ConnectionTreeActionsTest', // user
$('div'), // container
{}, // delegate
[], // renderers
{}, // data source
{}, // options
new MockContextKeyService(), // IContextKeyService
new TestListService(), // IListService,
undefined, // IThemeService,
new TestConfigurationService(), // IConfigurationService,
undefined, // IKeybindingService,
new TestAccessibilityService()); // IAccessibilityService
treeMock.callBase = true;
treeMock.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
treeMock.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => true);
treeMock.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve());
let refreshAction: RefreshAction = new RefreshAction(RefreshAction.ID,
RefreshAction.LABEL,
treeMock.instance,
connection,
connectionManagementService.object,
objectExplorerService.object,
undefined,
logService);
return refreshAction.run().then((value) => {
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
treeMock.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
});
});
test('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', () => {
let isConnectedReturnValue: boolean = false;
let sqlProvider = {
providerId: mssqlProviderName,
displayName: 'MSSQL',
connectionOptions: []
};
capabilitiesService.capabilities[mssqlProviderName] = { connection: sqlProvider };
let connection = new ConnectionProfile(capabilitiesService, {
connectionName: 'Test',
savePassword: false,
groupFullName: 'testGroup',
serverName: 'testServerName',
databaseName: 'testDatabaseName',
authenticationType: 'inetgrated',
password: 'test',
userName: 'testUsername',
groupId: undefined,
providerName: mssqlProviderName,
options: {},
saveProfile: true,
id: 'testID'
});
let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
conProfGroup.connections = [connection];
let connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
resolve(connection);
}));
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
let objectExplorerSession = {
success: true,
sessionId: '1234',
rootNode: {
nodePath: 'testServerName\tables',
nodeType: NodeType.Folder,
label: 'Tables',
isLeaf: false,
metadata: null,
nodeSubType: '',
nodeStatus: '',
errorMessage: ''
},
errorMessage: ''
};
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined);
tablesNode.connection = connection;
tablesNode.session = objectExplorerSession;
let table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
let table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
tablesNode.children = [table1Node, table2Node];
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
objectExplorerService.callBase = true;
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode);
objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node]));
let tree = TypeMoq.Mock.ofType<AsyncServerTree>(AsyncServerTree, TypeMoq.MockBehavior.Loose,
'ConnectionTreeActionsTest', // user
$('div'), // container
{}, // delegate
[], // renderers
{}, // data source
{}, // options
new MockContextKeyService(), // IContextKeyService
new TestListService(), // IListService,
undefined, // IThemeService,
new TestConfigurationService(), // IConfigurationService,
undefined, // IKeybindingService,
new TestAccessibilityService()); // IAccessibilityService
tree.callBase = true;
tree.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve());
tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID,
RefreshAction.LABEL,
tree.object,
connection,
connectionManagementService.object,
objectExplorerService.object,
undefined,
logService);
return connectionAction.run().then((value) => {
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
tree.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
});
});
test('EditConnectionAction - test if show connection dialog is called', () => {
let connectionManagementService = createConnectionManagementService(true, undefined);

View File

@@ -39,7 +39,7 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
);
mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []);
mockConnectionManagementService.setup(x => x.hasRegisteredServers()).returns(() => true);
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService);
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService, undefined, undefined);
mockTree = TypeMoq.Mock.ofType(TestTree);
(serverTreeView as any)._tree = mockTree.object;
mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function);
@@ -64,15 +64,6 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
mockTree.verify(x => x.layout(TypeMoq.It.isAnyNumber()), TypeMoq.Times.once());
});
test('setVisibility', async () => {
mockTree.setup(x => x.onVisible());
mockTree.setup(x => x.onHidden());
serverTreeView.setVisible(true);
mockTree.verify(x => x.onVisible(), TypeMoq.Times.once());
serverTreeView.setVisible(false);
mockTree.verify(x => x.onHidden(), TypeMoq.Times.once());
});
test('getSelection', async () => {
mockTree.setup(x => x.getSelection());
@@ -81,9 +72,9 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
});
test('isFocused', async () => {
mockTree.setup(x => x.isDOMFocused());
mockTree.setup(x => x.getHTMLElement());
serverTreeView.isFocused();
mockTree.verify(x => x.isDOMFocused(), TypeMoq.Times.once());
mockTree.verify(x => x.getHTMLElement(), TypeMoq.Times.once());
});
test('reveal', async () => {

View File

@@ -27,6 +27,7 @@ import { getErrorMessage } from 'vs/base/common/errors';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
//#region -- Data Explorer
export const SCRIPT_AS_CREATE_COMMAND_ID = 'dataExplorer.scriptAsCreate';
@@ -322,7 +323,11 @@ export async function handleOeRefreshCommand(accessor: ServicesAccessor, args: O
const tree = objectExplorerService.getServerTreeView().tree;
try {
await objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode);
await tree.refresh(treeNode);
if (tree instanceof AsyncServerTree) {
await tree.updateChildren(treeNode);
} else {
await tree.refresh(treeNode);
}
} catch (err) {
// Display message to the user but also log the entire error to the console for the stack trace
notificationService.error(localize('refreshError', "An error occurred refreshing node '{0}': {1}", args.nodeInfo.label, getErrorMessage(err)));