Strict null scripting (#12126)

* strict null scripting

* fix compile

* fix tests

* fix icon
This commit is contained in:
Anthony Dresser
2020-09-04 18:04:08 -07:00
committed by GitHub
parent bbe5b98a2c
commit 503090856a
35 changed files with 427 additions and 403 deletions

6
src/sql/azdata.d.ts vendored
View File

@@ -322,7 +322,7 @@ declare module 'azdata' {
export interface IConnectionProfile extends ConnectionInfo { export interface IConnectionProfile extends ConnectionInfo {
connectionName?: string; connectionName?: string;
serverName: string; serverName: string;
databaseName: string; databaseName?: string;
userName: string; userName: string;
password: string; password: string;
authenticationType: string; authenticationType: string;
@@ -413,7 +413,7 @@ declare module 'azdata' {
/** /**
* database name * database name
*/ */
databaseName: string; databaseName?: string;
/** /**
* user name * user name
*/ */
@@ -749,7 +749,7 @@ declare module 'azdata' {
} }
export interface ScriptingParamDetails { export interface ScriptingParamDetails {
filePath: string; filePath?: string;
scriptCompatibilityOption: string; scriptCompatibilityOption: string;
targetDatabaseEngineEdition: string; targetDatabaseEngineEdition: string;
targetDatabaseEngineType: string; targetDatabaseEngineType: string;

View File

@@ -8,6 +8,8 @@ import * as azdata from 'azdata';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import type { IDisposable } from 'vs/base/common/lifecycle'; import type { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IconPath } from 'sql/platform/connection/common/connectionProfile';
export const SERVICE_ID = 'capabilitiesService'; export const SERVICE_ID = 'capabilitiesService';
export const HOST_NAME = 'azdata'; export const HOST_NAME = 'azdata';
@@ -20,6 +22,7 @@ export const clientCapabilities = {
export interface ConnectionProviderProperties { export interface ConnectionProviderProperties {
providerId: string; providerId: string;
iconPath?: URI | IconPath | { id: string, path: IconPath }[]
displayName: string; displayName: string;
notebookKernelAlias?: string; notebookKernelAlias?: string;
azureResource?: string; azureResource?: string;

View File

@@ -13,7 +13,13 @@ import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilit
import { isString } from 'vs/base/common/types'; import { isString } from 'vs/base/common/types';
import { deepClone } from 'vs/base/common/objects'; import { deepClone } from 'vs/base/common/objects';
import * as Constants from 'sql/platform/connection/common/constants'; import * as Constants from 'sql/platform/connection/common/constants';
import { find } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri';
export interface IconPath {
light: URI;
dark: URI;
}
// Concrete implementation of the IConnectionProfile interface // Concrete implementation of the IConnectionProfile interface
@@ -29,6 +35,8 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
public groupId?: string; public groupId?: string;
public saveProfile: boolean; public saveProfile: boolean;
public iconPath?: IconPath;
public isDisconnecting: boolean = false; public isDisconnecting: boolean = false;
public constructor( public constructor(
@@ -49,7 +57,7 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
let capabilities = this.capabilitiesService.getCapabilities(model.providerName); let capabilities = this.capabilitiesService.getCapabilities(model.providerName);
if (capabilities && capabilities.connection && capabilities.connection.connectionOptions) { if (capabilities && capabilities.connection && capabilities.connection.connectionOptions) {
const options = capabilities.connection.connectionOptions; const options = capabilities.connection.connectionOptions;
let appNameOption = find(options, option => option.specialValueType === interfaces.ConnectionOptionSpecialType.appName); let appNameOption = options.find(option => option.specialValueType === interfaces.ConnectionOptionSpecialType.appName);
if (appNameOption) { if (appNameOption) {
let appNameKey = appNameOption.name; let appNameKey = appNameOption.name;
this.options[appNameKey] = Constants.applicationName; this.options[appNameKey] = Constants.applicationName;
@@ -87,9 +95,13 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
} }
private static nullCheckEqualsIgnoreCase(a: string, b: string) { private static nullCheckEqualsIgnoreCase(a?: string, b?: string) {
let bothNull: boolean = !a && !b; if (a && !b || b && !a) {
return bothNull ? bothNull : equalsIgnoreCase(a, b); return false;
} else {
let bothNull: boolean = !a && !b;
return bothNull ? bothNull : equalsIgnoreCase(a!, b!);
}
} }
public generateNewId() { public generateNewId() {

View File

@@ -152,7 +152,7 @@ export class ConnectionStatusManager {
//Check if the existing connection database name is different the one in the summary //Check if the existing connection database name is different the one in the summary
if (connection.connectionProfile.databaseName !== summary.connectionSummary.databaseName) { if (connection.connectionProfile.databaseName !== summary.connectionSummary.databaseName) {
//Add the ownerUri with database name to the map if not already exists //Add the ownerUri with database name to the map if not already exists
connection.connectionProfile.databaseName = summary.connectionSummary.databaseName; connection.connectionProfile.databaseName = summary.connectionSummary.databaseName!;
let prefix = Utils.getUriPrefix(summary.ownerUri); let prefix = Utils.getUriPrefix(summary.ownerUri);
let ownerUriWithDbName = Utils.generateUriWithPrefix(connection.connectionProfile, prefix); let ownerUriWithDbName = Utils.generateUriWithPrefix(connection.connectionProfile, prefix);
if (!(ownerUriWithDbName in this._connections)) { if (!(ownerUriWithDbName in this._connections)) {
@@ -186,7 +186,7 @@ export class ConnectionStatusManager {
let connection = this._connections[changedConnInfo.connectionUri]; let connection = this._connections[changedConnInfo.connectionUri];
if (connection && connection.connectionProfile) { if (connection && connection.connectionProfile) {
connection.connectionProfile.serverName = changedConnInfo.connection.serverName; connection.connectionProfile.serverName = changedConnInfo.connection.serverName;
connection.connectionProfile.databaseName = changedConnInfo.connection.databaseName; connection.connectionProfile.databaseName = changedConnInfo.connection.databaseName!;
connection.connectionProfile.userName = changedConnInfo.connection.userName; connection.connectionProfile.userName = changedConnInfo.connection.userName;
return connection.connectionProfile; return connection.connectionProfile;
} }

View File

@@ -137,6 +137,6 @@ export function findProfileInGroup(og: IConnectionProfile, groups: ConnectionPro
export function isMaster(profile: IConnectionProfile): boolean { export function isMaster(profile: IConnectionProfile): boolean {
// TODO: the connection profile should have a property to indicate whether the connection is a server connection or database connection // TODO: the connection profile should have a property to indicate whether the connection is a server connection or database connection
// created issue to track the problem: https://github.com/Microsoft/azuredatastudio/issues/5193. // created issue to track the problem: https://github.com/Microsoft/azuredatastudio/issues/5193.
return (profile.providerName === mssqlProviderName && profile.databaseName.toLowerCase() === 'master') return (profile.providerName === mssqlProviderName && profile.databaseName?.toLowerCase() === 'master')
|| (profile.providerName.toLowerCase() === 'pgsql' && profile.databaseName.toLowerCase() === 'postgres'); || (profile.providerName.toLowerCase() === 'pgsql' && profile.databaseName?.toLowerCase() === 'postgres');
} }

View File

@@ -138,7 +138,7 @@ suite('SQL ConnectionProfileInfo tests', () => {
assert.equal(conn.serverName, undefined); assert.equal(conn.serverName, undefined);
conn.connectionName = connectionProfile.connectionName!; conn.connectionName = connectionProfile.connectionName!;
conn.serverName = connectionProfile.serverName; conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName; conn.databaseName = connectionProfile.databaseName!;
conn.authenticationType = connectionProfile.authenticationType; conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password; conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName; conn.userName = connectionProfile.userName;

View File

@@ -141,7 +141,7 @@ suite('SQL ProviderConnectionInfo tests', () => {
assert.equal(conn.serverName, undefined); assert.equal(conn.serverName, undefined);
conn.connectionName = connectionProfile.connectionName!; conn.connectionName = connectionProfile.connectionName!;
conn.serverName = connectionProfile.serverName; conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName; conn.databaseName = connectionProfile.databaseName!;
conn.authenticationType = connectionProfile.authenticationType; conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password; conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName; conn.userName = connectionProfile.userName;
@@ -157,7 +157,7 @@ suite('SQL ProviderConnectionInfo tests', () => {
let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName); let conn = new ProviderConnectionInfo(capabilitiesService, mssqlProviderName);
assert.equal(conn.serverName, undefined); assert.equal(conn.serverName, undefined);
conn.serverName = connectionProfile.serverName; conn.serverName = connectionProfile.serverName;
conn.databaseName = connectionProfile.databaseName; conn.databaseName = connectionProfile.databaseName!;
conn.authenticationType = connectionProfile.authenticationType; conn.authenticationType = connectionProfile.authenticationType;
conn.password = connectionProfile.password; conn.password = connectionProfile.password;
conn.userName = connectionProfile.userName; conn.userName = connectionProfile.userName;

View File

@@ -95,7 +95,7 @@ export class DashboardInput extends EditorInput {
private isMasterMssql(): boolean { private isMasterMssql(): boolean {
return this.connectionProfile.providerName === mssqlProviderName return this.connectionProfile.providerName === mssqlProviderName
&& this.connectionProfile.databaseName.toLowerCase() === 'master'; && this.connectionProfile.databaseName?.toLowerCase() === 'master';
} }
public get uri(): string | undefined { public get uri(): string | undefined {

View File

@@ -27,8 +27,8 @@ export class ScriptSelectAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return scriptSelect( return scriptSelect(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService this._scriptingService
@@ -52,8 +52,8 @@ export class ScriptExecuteAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return script( return script(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService, this._scriptingService,
@@ -79,8 +79,8 @@ export class ScriptAlterAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return script( return script(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService, this._scriptingService,
@@ -105,8 +105,8 @@ export class EditDataAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return scriptEditSelect( return scriptEditSelect(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService this._scriptingService
@@ -130,8 +130,8 @@ export class ScriptCreateAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return script( return script(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService, this._scriptingService,
@@ -157,8 +157,8 @@ export class ScriptDeleteAction extends Action {
public async run(actionContext: BaseActionContext): Promise<boolean> { public async run(actionContext: BaseActionContext): Promise<boolean> {
return script( return script(
actionContext.profile, actionContext.profile!,
actionContext.object, actionContext.object!,
this._connectionManagementService, this._connectionManagementService,
this._queryEditorService, this._queryEditorService,
this._scriptingService, this._scriptingService,

View File

@@ -42,7 +42,7 @@ const targetDatabaseEngineEditionMap = {
*/ */
export async function scriptSelect(connectionProfile: IConnectionProfile, metadata: azdata.ObjectMetadata, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService, scriptingService: IScriptingService): Promise<boolean> { export async function scriptSelect(connectionProfile: IConnectionProfile, metadata: azdata.ObjectMetadata, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService, scriptingService: IScriptingService): Promise<boolean> {
const connectionResult = await connectionService.connectIfNotConnected(connectionProfile); const connectionResult = await connectionService.connectIfNotConnected(connectionProfile);
let paramDetails: azdata.ScriptingParamDetails = getScriptingParamDetails(connectionService, connectionResult, metadata); let paramDetails = getScriptingParamDetails(connectionService, connectionResult, metadata)!;
const result = await scriptingService.script(connectionResult, metadata, ScriptOperation.Select, paramDetails); const result = await scriptingService.script(connectionResult, metadata, ScriptOperation.Select, paramDetails);
if (result && result.script) { if (result && result.script) {
const owner = await queryEditorService.newSqlEditor({ initalContent: result.script }, connectionProfile?.providerName); const owner = await queryEditorService.newSqlEditor({ initalContent: result.script }, connectionProfile?.providerName);
@@ -68,8 +68,8 @@ export async function scriptSelect(connectionProfile: IConnectionProfile, metada
*/ */
export async function scriptEditSelect(connectionProfile: IConnectionProfile, metadata: azdata.ObjectMetadata, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService, scriptingService: IScriptingService): Promise<boolean> { export async function scriptEditSelect(connectionProfile: IConnectionProfile, metadata: azdata.ObjectMetadata, connectionService: IConnectionManagementService, queryEditorService: IQueryEditorService, scriptingService: IScriptingService): Promise<boolean> {
const connectionResult = await connectionService.connectIfNotConnected(connectionProfile); const connectionResult = await connectionService.connectIfNotConnected(connectionProfile);
let paramDetails: azdata.ScriptingParamDetails = getScriptingParamDetails(connectionService, connectionResult, metadata); let paramDetails = getScriptingParamDetails(connectionService, connectionResult, metadata);
const result = await scriptingService.script(connectionResult, metadata, ScriptOperation.Select, paramDetails); const result = await scriptingService.script(connectionResult, metadata, ScriptOperation.Select, paramDetails!);
if (result && result.script) { if (result && result.script) {
const owner = await queryEditorService.newEditDataEditor(metadata.schema, metadata.name, result.script); const owner = await queryEditorService.newEditDataEditor(metadata.schema, metadata.name, result.script);
// Connect our editor // Connect our editor
@@ -120,7 +120,7 @@ export async function script(connectionProfile: IConnectionProfile, metadata: az
operation: ScriptOperation, operation: ScriptOperation,
errorMessageService: IErrorMessageService): Promise<boolean> { errorMessageService: IErrorMessageService): Promise<boolean> {
const connectionResult = await connectionService.connectIfNotConnected(connectionProfile); const connectionResult = await connectionService.connectIfNotConnected(connectionProfile);
let paramDetails = getScriptingParamDetails(connectionService, connectionResult, metadata); let paramDetails = getScriptingParamDetails(connectionService, connectionResult, metadata)!;
const result = await scriptingService.script(connectionResult, metadata, operation, paramDetails); const result = await scriptingService.script(connectionResult, metadata, operation, paramDetails);
if (result) { if (result) {
let script: string = result.script; let script: string = result.script;
@@ -160,18 +160,21 @@ export async function script(connectionProfile: IConnectionProfile, metadata: az
} }
} }
function getScriptingParamDetails(connectionService: IConnectionManagementService, ownerUri: string, metadata: azdata.ObjectMetadata): azdata.ScriptingParamDetails { function getScriptingParamDetails(connectionService: IConnectionManagementService, ownerUri: string, metadata: azdata.ObjectMetadata): azdata.ScriptingParamDetails | undefined {
let serverInfo: azdata.ServerInfo = getServerInfo(connectionService, ownerUri); let serverInfo: azdata.ServerInfo | undefined = getServerInfo(connectionService, ownerUri);
let paramDetails: azdata.ScriptingParamDetails = { if (serverInfo) {
filePath: undefined, let paramDetails: azdata.ScriptingParamDetails = {
scriptCompatibilityOption: scriptCompatibilityOptionMap[serverInfo.serverMajorVersion], filePath: undefined,
targetDatabaseEngineEdition: targetDatabaseEngineEditionMap[serverInfo.engineEditionId], scriptCompatibilityOption: scriptCompatibilityOptionMap[serverInfo.serverMajorVersion as keyof typeof scriptCompatibilityOptionMap],
targetDatabaseEngineType: serverInfo.isCloud ? 'SqlAzure' : 'SingleInstance' targetDatabaseEngineEdition: targetDatabaseEngineEditionMap[serverInfo.engineEditionId as keyof typeof targetDatabaseEngineEditionMap],
}; targetDatabaseEngineType: serverInfo.isCloud ? 'SqlAzure' : 'SingleInstance'
return paramDetails; };
return paramDetails;
}
return undefined;
} }
function getServerInfo(connectionService: IConnectionManagementService, ownerUri: string): azdata.ServerInfo { function getServerInfo(connectionService: IConnectionManagementService, ownerUri: string): azdata.ServerInfo | undefined {
let connection: ConnectionManagementInfo = connectionService.getConnectionInfo(ownerUri); let connection: ConnectionManagementInfo = connectionService.getConnectionInfo(ownerUri);
return connection.serverInfo; return connection.serverInfo;
} }

View File

@@ -31,7 +31,7 @@ export class ConnectionViewletPanel extends ViewPane {
public static readonly ID = 'dataExplorer.servers'; public static readonly ID = 'dataExplorer.servers';
private _root: HTMLElement; private _root?: HTMLElement;
private _serverTreeView: ServerTreeView; private _serverTreeView: ServerTreeView;
private _addServerAction: IAction; private _addServerAction: IAction;
private _addServerGroupAction: IAction; private _addServerGroupAction: IAction;
@@ -89,7 +89,7 @@ export class ConnectionViewletPanel extends ViewPane {
layoutBody(size: number): void { layoutBody(size: number): void {
this._serverTreeView.layout(size); this._serverTreeView.layout(size);
DOM.toggleClass(this._root, 'narrow', this._root.clientWidth < 300); DOM.toggleClass(this._root!, 'narrow', this._root!.clientWidth < 300);
} }
show(): void { show(): void {

View File

@@ -87,9 +87,9 @@ export class DataExplorerViewlet extends Viewlet {
} }
export class DataExplorerViewPaneContainer extends ViewPaneContainer { export class DataExplorerViewPaneContainer extends ViewPaneContainer {
private root: HTMLElement; private root?: HTMLElement;
private dataSourcesBox: HTMLElement; private dataSourcesBox?: HTMLElement;
constructor( constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@@ -125,7 +125,7 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer {
} }
layout(dimension: Dimension): void { layout(dimension: Dimension): void {
toggleClass(this.root, 'narrow', dimension.width <= 300); toggleClass(this.root!, 'narrow', dimension.width <= 300);
super.layout(new Dimension(dimension.width, dimension.height)); super.layout(new Dimension(dimension.width, dimension.height));
} }
@@ -135,7 +135,7 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer {
getSecondaryActions(): IAction[] { getSecondaryActions(): IAction[] {
let menu = this.menuService.createMenu(MenuId.DataExplorerAction, this.contextKeyService); let menu = this.menuService.createMenu(MenuId.DataExplorerAction, this.contextKeyService);
let actions = []; let actions: IAction[] = [];
menu.getActions({}).forEach(group => { menu.getActions({}).forEach(group => {
if (group[0] === 'secondary') { if (group[0] === 'secondary') {
actions.push(...group[1]); actions.push(...group[1]);

View File

@@ -9,7 +9,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import Severity from 'vs/base/common/severity'; import Severity from 'vs/base/common/severity';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachListStyler } from 'vs/platform/theme/common/styler'; import { attachListStyler } from 'vs/platform/theme/common/styler';
import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ISelectionEvent, ITree } from 'vs/base/parts/tree/browser/tree';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -29,31 +29,30 @@ import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { Button } from 'sql/base/browser/ui/button/button'; import { Button } from 'sql/base/browser/ui/button/button';
import { attachButtonStyler } from 'sql/platform/theme/common/styler'; import { attachButtonStyler } from 'sql/platform/theme/common/styler';
import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { SERVER_GROUP_AUTOEXPAND_CONFIG } from 'sql/workbench/contrib/objectExplorer/common/serverGroup.contribution';
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService'; import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider'; import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { isHidden } from 'sql/base/browser/dom'; import { isHidden } from 'sql/base/browser/dom';
import { CommandsRegistry } from 'vs/platform/commands/common/commands'; 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 { SERVER_GROUP_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
import { horizontalScrollingKey } from 'vs/platform/list/browser/listService'; import { horizontalScrollingKey } from 'vs/platform/list/browser/listService';
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ITreeContextMenuEvent, ITreeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions'; import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
import { coalesce } from 'vs/base/common/arrays';
/** /**
* ServerTreeview implements the dynamic tree view. * ServerTreeview implements the dynamic tree view.
*/ */
export class ServerTreeView extends Disposable implements IServerTreeView { export class ServerTreeView extends Disposable implements IServerTreeView {
public messages: HTMLElement; public messages?: HTMLElement;
private _buttonSection: HTMLElement; private _buttonSection?: HTMLElement;
private _treeSelectionHandler: TreeSelectionHandler; private _treeSelectionHandler: TreeSelectionHandler;
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction; private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
private _tree: ITree | AsyncServerTree; private _tree?: ITree | AsyncServerTree;
private _onSelectionOrFocusChange: Emitter<void>; private _onSelectionOrFocusChange: Emitter<void>;
private _actionProvider: ServerTreeActionProvider; private _actionProvider: ServerTreeActionProvider;
@@ -81,7 +80,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
// Refresh the tree input now that the capabilities are registered so that we can // Refresh the tree input now that the capabilities are registered so that we can
// get the full ConnectionProfiles with the server info updated properly // get the full ConnectionProfiles with the server info updated properly
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService); const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
await this._tree.setInput(treeInput); await this._tree.setInput(treeInput);
this._treeSelectionHandler.onTreeActionStateChange(false); this._treeSelectionHandler.onTreeActionStateChange(false);
} else { } else {
@@ -113,7 +112,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
} }
public get tree(): ITree | AsyncServerTree { public get tree(): ITree | AsyncServerTree {
return this._tree; return this._tree!;
} }
/** /**
@@ -159,14 +158,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const horizontalScrollEnabled: boolean = this._configurationService.getValue(horizontalScrollingKey) || false; const horizontalScrollEnabled: boolean = this._configurationService.getValue(horizontalScrollingKey) || false;
this._tree = this._register(TreeCreationUtils.createServersTree(container, this._instantiationService, this._configurationService, horizontalScrollEnabled)); 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.onDidChangeSelection((event: ISelectionEvent | ITreeEvent<ServerTreeElement>) => this.onSelected(event)));
this._register(this._tree.onDidBlur(() => this._onSelectionOrFocusChange.fire())); this._register(this._tree.onDidBlur(() => this._onSelectionOrFocusChange.fire()));
this._register(this._tree.onDidChangeFocus(() => this._onSelectionOrFocusChange.fire())); this._register(this._tree.onDidChangeFocus(() => this._onSelectionOrFocusChange.fire()));
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._register(this._tree.onContextMenu(e => this.onContextMenu(e))); this._register(this._tree.onContextMenu(e => this.onContextMenu(e)));
this._register(this._tree.onMouseDblClick(e => { this._register(this._tree.onMouseDblClick(e => {
// Open dashboard on double click for server and database nodes // Open dashboard on double click for server and database nodes
let connectionProfile: ConnectionProfile; let connectionProfile: ConnectionProfile | undefined;
if (e.element instanceof ConnectionProfile) { if (e.element instanceof ConnectionProfile) {
connectionProfile = e.element; connectionProfile = e.element;
} else if (e.element instanceof TreeNode) { } else if (e.element instanceof TreeNode) {
@@ -212,16 +211,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
await this.refreshTree(); await this.refreshTree();
const root = <ConnectionProfileGroup>this._tree.getInput(); const root = <ConnectionProfileGroup>this._tree!.getInput();
const expandGroups: boolean = this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG]; const expandGroups = this._configurationService.getValue<{ autoExpand: boolean }>(SERVER_GROUP_CONFIG).autoExpand;
if (expandGroups) { if (expandGroups) {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(root).map(subgroup => { await Promise.all(ConnectionProfileGroup.getSubgroups(root).map(subgroup => {
return this._tree.expand(subgroup); return this._tree!.expand(subgroup);
})); }));
} else { } else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root)); await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(root));
} }
} }
@@ -238,7 +237,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
public isObjectExplorerConnectionUri(uri: string): boolean { public isObjectExplorerConnectionUri(uri: string): boolean {
let isBackupRestoreUri: boolean = uri.indexOf(ConnectionUtils.ConnectionUriBackupIdAttributeName) >= 0 || let isBackupRestoreUri: boolean = uri.indexOf(ConnectionUtils.ConnectionUriBackupIdAttributeName) >= 0 ||
uri.indexOf(ConnectionUtils.ConnectionUriRestoreIdAttributeName) >= 0; uri.indexOf(ConnectionUtils.ConnectionUriRestoreIdAttributeName) >= 0;
return uri && startsWith(uri, ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri; return !!uri && uri.startsWith(ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri;
} }
private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise<void> { private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise<void> {
@@ -277,16 +276,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
} }
} }
const currentSelections = this._tree.getSelection(); const currentSelections = this._tree!.getSelection();
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined; const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false; const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
if (newProfile && currentSelectedElement && !newProfileIsSelected) { if (newProfile && currentSelectedElement && !newProfileIsSelected) {
this._tree.clearSelection(); this._tree!.clearSelection();
} }
await this.refreshTree(); await this.refreshTree();
if (newProfile && !newProfileIsSelected) { if (newProfile && !newProfileIsSelected) {
await this._tree.reveal(newProfile); await this._tree!.reveal(newProfile);
this._tree.select(newProfile); this._tree!.select(newProfile);
} }
} }
@@ -304,11 +303,11 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/ */
private getConnectionInTreeInput(connectionId: string): ConnectionProfile | undefined { private getConnectionInTreeInput(connectionId: string): ConnectionProfile | undefined {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
const root = this._tree.getInput(); const root = this._tree.getInput()!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root); const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
return connections.find(conn => conn.id === connectionId); return connections.find(conn => conn.id === connectionId);
} else { } else {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root); const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => { const results = connections.filter(con => {
if (connectionId === con.id) { if (connectionId === con.id) {
@@ -330,16 +329,16 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._tree.rerender(element); this._tree.rerender(element);
} else { } else {
await this._tree.refresh(element); await this._tree!.refresh(element);
} }
await this._tree.expand(element); await this._tree!.expand(element);
await this._tree.reveal(element, 0.5); await this._tree!.reveal(element, 0.5);
this._treeSelectionHandler.onTreeActionStateChange(false); this._treeSelectionHandler.onTreeActionStateChange(false);
} }
} }
public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void { public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void {
hide(this.messages); hide(this.messages!);
if (!this._objectExplorerService.getObjectExplorerNode(connection)) { if (!this._objectExplorerService.getObjectExplorerNode(connection)) {
this._objectExplorerService.updateObjectExplorerNodes(connection).catch(e => errors.onUnexpectedError(e)); this._objectExplorerService.updateObjectExplorerNodes(connection).catch(e => errors.onUnexpectedError(e));
} }
@@ -356,35 +355,35 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.collapse(conn); this._tree.collapse(conn);
await this.refreshTree(); await this.refreshTree();
} else { } else {
await this._tree.collapse(conn); await this._tree!.collapse(conn);
return this._tree.refresh(conn); return this._tree!.refresh(conn);
} }
} }
} }
} }
public async refreshTree(): Promise<void> { public async refreshTree(): Promise<void> {
hide(this.messages); hide(this.messages!);
this.clearOtherActions(); this.clearOtherActions();
return TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService); return TreeUpdateUtils.registeredServerUpdate(this._tree!, this._connectionManagementService);
} }
public async refreshElement(element: ServerTreeElement): Promise<void> { public async refreshElement(element: ServerTreeElement): Promise<void> {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
return this._tree.updateChildren(element); return this._tree.updateChildren(element);
} else { } else {
return this._tree.refresh(element); return this._tree!.refresh(element);
} }
} }
/** /**
* Filter connections based on view (recent/active) * Filter connections based on view (recent/active)
*/ */
private filterConnections(treeInput: ConnectionProfileGroup[], view: string): ConnectionProfileGroup[] { private filterConnections(treeInput: ConnectionProfileGroup[] | undefined, view: string): ConnectionProfileGroup[] | undefined {
if (!treeInput || treeInput.length === 0) { if (!treeInput || treeInput.length === 0) {
return undefined; return undefined;
} }
const result = treeInput.map(group => { const result = coalesce(treeInput.map(group => {
// Keep active/recent connections and remove the rest // Keep active/recent connections and remove the rest
if (group.connections) { if (group.connections) {
group.connections = group.connections.filter(con => { group.connections = group.connections.filter(con => {
@@ -408,7 +407,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return group; return group;
} }
return undefined; return undefined;
}); }));
return result; return result;
} }
@@ -416,35 +415,35 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Set tree elements based on the view (recent/active) * Set tree elements based on the view (recent/active)
*/ */
public showFilteredTree(view: string): void { public showFilteredTree(view: string): void {
hide(this.messages); hide(this.messages!);
// Clear other action views if user switched between two views // Clear other action views if user switched between two views
this.clearOtherActions(view); this.clearOtherActions(view);
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
let treeInput: ConnectionProfileGroup = null; let treeInput: ConnectionProfileGroup | undefined = undefined;
if (root) { if (root) {
// Filter results based on view // Filter results based on view
const filteredResults = this.filterConnections([root], view); const filteredResults = this.filterConnections([root], view);
if (!filteredResults || !filteredResults[0]) { if (!filteredResults || !filteredResults[0]) {
show(this.messages); show(this.messages!);
this.messages.focus(); this.messages!.focus();
} else { } else {
treeInput = filteredResults[0]; treeInput = filteredResults[0];
} }
this._tree.setInput(treeInput).then(async () => { this._tree!.setInput(treeInput!).then(async () => {
if (isHidden(this.messages)) { if (isHidden(this.messages!)) {
this._tree.getFocus(); this._tree!.getFocus();
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => { await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput!).map(subgroup => {
this._tree.expand(subgroup); this._tree!.expand(subgroup);
})); }));
} else { } else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput)); await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(treeInput!));
} }
} else { } else {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]); this._tree.setFocus([]);
} else { } else {
this._tree.clearFocus(); this._tree!.clearFocus();
} }
} }
}, errors.onUnexpectedError); }, errors.onUnexpectedError);
@@ -460,33 +459,33 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (!searchString) { if (!searchString) {
return; return;
} }
hide(this.messages); hide(this.messages!);
// Clear other actions if user searched during other views // Clear other actions if user searched during other views
this.clearOtherActions(); this.clearOtherActions();
// Filter connections based on search // Filter connections based on search
const filteredResults = this.searchConnections(searchString); const filteredResults = this.searchConnections(searchString);
if (!filteredResults || filteredResults.length === 0) { if (!filteredResults || filteredResults.length === 0) {
show(this.messages); show(this.messages!);
this.messages.focus(); this.messages!.focus();
} }
// Add all connections to tree root and set tree input // Add all connections to tree root and set tree input
const treeInput = new ConnectionProfileGroup('searchroot', undefined, 'searchroot', undefined, undefined); const treeInput = new ConnectionProfileGroup('searchroot', undefined, 'searchroot', undefined, undefined);
treeInput.addConnections(filteredResults); treeInput.addConnections(filteredResults);
this._tree.setInput(treeInput).then(async () => { this._tree!.setInput(treeInput).then(async () => {
if (isHidden(this.messages)) { if (isHidden(this.messages!)) {
this._tree.getFocus(); this._tree!.getFocus();
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => { await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
this._tree.expand(subgroup); this._tree!.expand(subgroup);
})); }));
} else { } else {
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput)); await this._tree!.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
} }
} else { } else {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._tree.setFocus([]); this._tree.setFocus([]);
} else { } else {
this._tree.clearFocus(); this._tree!.clearFocus();
} }
} }
}, errors.onUnexpectedError); }, errors.onUnexpectedError);
@@ -497,7 +496,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/ */
private searchConnections(searchString: string): ConnectionProfile[] { private searchConnections(searchString: string): ConnectionProfile[] {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root); const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
const results = connections.filter(con => { const results = connections.filter(con => {
if (searchString && (searchString.length > 0)) { if (searchString && (searchString.length > 0)) {
@@ -542,7 +541,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
} }
private onSelected(event: any): void { private onSelected(event: any): void {
this._treeSelectionHandler.onTreeSelect(event, this._tree, this._connectionManagementService, this._objectExplorerService, () => this._onSelectionOrFocusChange.fire()); this._treeSelectionHandler.onTreeSelect(event, this._tree!, this._connectionManagementService, this._objectExplorerService, () => this._onSelectionOrFocusChange.fire());
this._onSelectionOrFocusChange.fire(); this._onSelectionOrFocusChange.fire();
} }
@@ -550,21 +549,21 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* set the layout of the view * set the layout of the view
*/ */
public layout(height: number): void { public layout(height: number): void {
this._tree.layout(height); this._tree!.layout(height);
} }
/** /**
* Get the list of selected nodes in the tree * Get the list of selected nodes in the tree
*/ */
public getSelection(): any[] { public getSelection(): any[] {
return this._tree.getSelection(); return this._tree!.getSelection();
} }
/** /**
* Get whether the tree view currently has focus * Get whether the tree view currently has focus
*/ */
public isFocused(): boolean { public isFocused(): boolean {
return this._tree.getHTMLElement() === document.activeElement; return this._tree!.getHTMLElement() === document.activeElement;
} }
/** /**
@@ -572,9 +571,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
*/ */
public async setExpandedState(element: ServerTreeElement, expandedState?: TreeItemCollapsibleState): Promise<void> { public async setExpandedState(element: ServerTreeElement, expandedState?: TreeItemCollapsibleState): Promise<void> {
if (expandedState === TreeItemCollapsibleState.Collapsed) { if (expandedState === TreeItemCollapsibleState.Collapsed) {
return this._tree.collapse(element); return this._tree!.collapse(element);
} else if (expandedState === TreeItemCollapsibleState.Expanded) { } else if (expandedState === TreeItemCollapsibleState.Expanded) {
return this._tree.expand(element); return this._tree!.expand(element);
} }
} }
@@ -582,7 +581,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Reveal the given element in the tree * Reveal the given element in the tree
*/ */
public async reveal(element: ServerTreeElement): Promise<void> { public async reveal(element: ServerTreeElement): Promise<void> {
return this._tree.reveal(element); return this._tree!.reveal(element);
} }
/** /**
@@ -593,7 +592,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection([]); this._tree.setSelection([]);
} else { } else {
this._tree.clearSelection(); this._tree!.clearSelection();
} }
} }
@@ -602,14 +601,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
this._tree.setSelection(this._tree.getSelection().concat(element)); this._tree.setSelection(this._tree.getSelection().concat(element));
this._tree.reveal(element); this._tree.reveal(element);
} else { } else {
this._tree.select(element); this._tree!.select(element);
return this._tree.reveal(element); return this._tree!.reveal(element);
} }
} else { } else {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
this._tree.setSelection(this._tree.getSelection().filter(item => item !== element)); this._tree.setSelection(this._tree.getSelection().filter(item => item !== element));
} else { } else {
this._tree.deselect(element); this._tree!.deselect(element);
} }
} }
} }
@@ -621,7 +620,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (this._tree instanceof AsyncServerTree) { if (this._tree instanceof AsyncServerTree) {
return !this._tree.getNode(element).collapsed; return !this._tree.getNode(element).collapsed;
} else { } else {
return this._tree.isExpanded(element); return this._tree!.isExpanded(element);
} }
} }
@@ -630,42 +629,45 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
* Return actions in the context menu * Return actions in the context menu
*/ */
private onContextMenu(e: ITreeContextMenuEvent<ServerTreeElement>): boolean { private onContextMenu(e: ITreeContextMenuEvent<ServerTreeElement>): boolean {
e.browserEvent.preventDefault(); if (e.element) {
e.browserEvent.stopPropagation(); e.browserEvent.preventDefault();
this._tree.setSelection([e.element]); e.browserEvent.stopPropagation();
this._tree!.setSelection([e.element]);
let actionContext: any; let actionContext: any;
if (e.element instanceof TreeNode) { if (e.element instanceof TreeNode) {
let context = new ObjectExplorerActionsContext(); let context = new ObjectExplorerActionsContext();
context.nodeInfo = e.element.toNodeInfo(); context.nodeInfo = e.element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces // 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 // the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = e.element.getConnectionProfile().toIConnectionProfile(); context.connectionProfile = e.element.getConnectionProfile()!.toIConnectionProfile();
context.connectionProfile.databaseName = e.element.getDatabaseName(); context.connectionProfile.databaseName = e.element.getDatabaseName();
actionContext = context; actionContext = context;
} else if (e.element instanceof ConnectionProfile) { } else if (e.element instanceof ConnectionProfile) {
let context = new ObjectExplorerActionsContext(); let context = new ObjectExplorerActionsContext();
context.connectionProfile = e.element.toIConnectionProfile(); context.connectionProfile = e.element.toIConnectionProfile();
context.isConnectionNode = true; context.isConnectionNode = true;
actionContext = context; actionContext = context;
} else { } else {
// TODO: because the connection group is used as a context object and isn't serializable, // TODO: because the connection group is used as a context object and isn't serializable,
// the Group-level context menu is not currently extensible // the Group-level context menu is not currently extensible
actionContext = e.element; 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;
} }
return false;
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

@@ -40,130 +40,148 @@ export const EDIT_DATA_COMMAND_ID = 'dataExplorer.scriptAsEdit';
// Script as Create // Script as Create
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: SCRIPT_AS_CREATE_COMMAND_ID, id: SCRIPT_AS_CREATE_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const errorMessageService = accessor.get(IErrorMessageService); const scriptingService = accessor.get(IScriptingService);
const progressService = accessor.get(IProgressService); const errorMessageService = accessor.get(IErrorMessageService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const progressService = accessor.get(IProgressService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const scriptCreateAction = new ScriptCreateAction(ScriptCreateAction.ID, ScriptCreateAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService, errorMessageService); const scriptCreateAction = new ScriptCreateAction(ScriptCreateAction.ID, ScriptCreateAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptCreateAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService, errorMessageService);
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptCreateAction.run(baseContext));
}
return undefined;
} }
}); });
// Script as Delete // Script as Delete
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: SCRIPT_AS_DELETE_COMMAND_ID, id: SCRIPT_AS_DELETE_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const errorMessageService = accessor.get(IErrorMessageService); const scriptingService = accessor.get(IScriptingService);
const progressService = accessor.get(IProgressService); const errorMessageService = accessor.get(IErrorMessageService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const progressService = accessor.get(IProgressService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const scriptDeleteAction = new ScriptDeleteAction(ScriptDeleteAction.ID, ScriptDeleteAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService, errorMessageService); const scriptDeleteAction = new ScriptDeleteAction(ScriptDeleteAction.ID, ScriptDeleteAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptDeleteAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService, errorMessageService);
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptDeleteAction.run(baseContext));
}
return undefined;
} }
}); });
// Script as Select // Script as Select
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: SCRIPT_AS_SELECT_COMMAND_ID, id: SCRIPT_AS_SELECT_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const progressService = accessor.get(IProgressService); const scriptingService = accessor.get(IScriptingService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const progressService = accessor.get(IProgressService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const scriptSelectAction = new ScriptSelectAction(ScriptSelectAction.ID, ScriptSelectAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService); const scriptSelectAction = new ScriptSelectAction(ScriptSelectAction.ID, ScriptSelectAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptSelectAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService);
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptSelectAction.run(baseContext));
}
return undefined;
} }
}); });
// Script as Execute // Script as Execute
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: SCRIPT_AS_EXECUTE_COMMAND_ID, id: SCRIPT_AS_EXECUTE_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const progressService = accessor.get(IProgressService); const scriptingService = accessor.get(IScriptingService);
const errorMessageService = accessor.get(IErrorMessageService); const progressService = accessor.get(IProgressService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const errorMessageService = accessor.get(IErrorMessageService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const scriptExecuteAction = new ScriptExecuteAction(ScriptExecuteAction.ID, ScriptExecuteAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService, errorMessageService); const scriptExecuteAction = new ScriptExecuteAction(ScriptExecuteAction.ID, ScriptExecuteAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptExecuteAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService, errorMessageService);
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptExecuteAction.run(baseContext));
}
return undefined;
} }
}); });
// Script as Alter // Script as Alter
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: SCRIPT_AS_ALTER_COMMAND_ID, id: SCRIPT_AS_ALTER_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const progressService = accessor.get(IProgressService); const scriptingService = accessor.get(IScriptingService);
const errorMessageService = accessor.get(IErrorMessageService); const progressService = accessor.get(IProgressService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const errorMessageService = accessor.get(IErrorMessageService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const scriptAlterAction = new ScriptAlterAction(ScriptAlterAction.ID, ScriptAlterAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService, errorMessageService); const scriptAlterAction = new ScriptAlterAction(ScriptAlterAction.ID, ScriptAlterAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptAlterAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService, errorMessageService);
return progressService.withProgress({ location: VIEWLET_ID }, () => scriptAlterAction.run(baseContext));
}
return undefined;
} }
}); });
// Edit Data // Edit Data
CommandsRegistry.registerCommand({ CommandsRegistry.registerCommand({
id: EDIT_DATA_COMMAND_ID, id: EDIT_DATA_COMMAND_ID,
handler: async (accessor, args: TreeViewItemHandleArg) => { handler: async (accessor, args: TreeViewItemHandleArg): Promise<boolean | undefined> => {
const capabilitiesService = accessor.get(ICapabilitiesService); if (args.$treeItem?.payload) {
const oeShimService = accessor.get(IOEShimService); const capabilitiesService = accessor.get(ICapabilitiesService);
const queryEditorService = accessor.get(IQueryEditorService); const oeShimService = accessor.get(IOEShimService);
const connectionManagementService = accessor.get(IConnectionManagementService); const queryEditorService = accessor.get(IQueryEditorService);
const scriptingService = accessor.get(IScriptingService); const connectionManagementService = accessor.get(IConnectionManagementService);
const progressService = accessor.get(IProgressService); const scriptingService = accessor.get(IScriptingService);
const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload); const progressService = accessor.get(IProgressService);
const baseContext: BaseActionContext = { const profile = new ConnectionProfile(capabilitiesService, args.$treeItem.payload);
profile: profile, const baseContext: BaseActionContext = {
object: oeShimService.getNodeInfoForTreeItem(args.$treeItem).metadata profile: profile,
}; object: oeShimService.getNodeInfoForTreeItem(args.$treeItem)!.metadata
const editDataAction = new EditDataAction(EditDataAction.ID, EditDataAction.LABEL, };
queryEditorService, connectionManagementService, scriptingService); const editDataAction = new EditDataAction(EditDataAction.ID, EditDataAction.LABEL,
return progressService.withProgress({ location: VIEWLET_ID }, () => editDataAction.run(baseContext)); queryEditorService, connectionManagementService, scriptingService);
return progressService.withProgress({ location: VIEWLET_ID }, () => editDataAction.run(baseContext));
}
return undefined;
} }
}); });
//#endregion //#endregion
@@ -186,9 +204,9 @@ CommandsRegistry.registerCommand({
const connectionManagementService = accessor.get(IConnectionManagementService); const connectionManagementService = accessor.get(IConnectionManagementService);
const objectExplorerService = accessor.get(IObjectExplorerService); const objectExplorerService = accessor.get(IObjectExplorerService);
const selectionHandler = instantiationService.createInstance(TreeSelectionHandler); const selectionHandler = instantiationService.createInstance(TreeSelectionHandler);
const node = await getTreeNode(args, objectExplorerService); const node = (await getTreeNode(args, objectExplorerService))!;
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
let connectionProfile = TreeUpdateUtils.getConnectionProfile(node); let connectionProfile = TreeUpdateUtils.getConnectionProfile(node)!;
let ownerUri = connectionManagementService.getConnectionUri(connectionProfile); let ownerUri = connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
let metadata = node.metadata; let metadata = node.metadata;
@@ -207,7 +225,7 @@ CommandsRegistry.registerCommand({
const instantiationService = accessor.get(IInstantiationService); const instantiationService = accessor.get(IInstantiationService);
const objectExplorerService = accessor.get(IObjectExplorerService); const objectExplorerService = accessor.get(IObjectExplorerService);
const selectionHandler = instantiationService.createInstance(TreeSelectionHandler); const selectionHandler = instantiationService.createInstance(TreeSelectionHandler);
const node = await getTreeNode(args, objectExplorerService); const node = (await getTreeNode(args, objectExplorerService))!;
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
let connectionProfile = TreeUpdateUtils.getConnectionProfile(node); let connectionProfile = TreeUpdateUtils.getConnectionProfile(node);
let metadata = node.metadata; let metadata = node.metadata;
@@ -227,9 +245,9 @@ CommandsRegistry.registerCommand({
const connectionManagementService = accessor.get(IConnectionManagementService); const connectionManagementService = accessor.get(IConnectionManagementService);
const objectExplorerService = accessor.get(IObjectExplorerService); const objectExplorerService = accessor.get(IObjectExplorerService);
const selectionHandler = instantiationService.createInstance(TreeSelectionHandler); const selectionHandler = instantiationService.createInstance(TreeSelectionHandler);
const node = await getTreeNode(args, objectExplorerService); const node = (await getTreeNode(args, objectExplorerService))!;
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
let connectionProfile = TreeUpdateUtils.getConnectionProfile(node); let connectionProfile = TreeUpdateUtils.getConnectionProfile(node)!;
let metadata = node.metadata; let metadata = node.metadata;
let ownerUri = connectionManagementService.getConnectionUri(connectionProfile); let ownerUri = connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
@@ -249,9 +267,9 @@ CommandsRegistry.registerCommand({
const connectionManagementService = accessor.get(IConnectionManagementService); const connectionManagementService = accessor.get(IConnectionManagementService);
const objectExplorerService = accessor.get(IObjectExplorerService); const objectExplorerService = accessor.get(IObjectExplorerService);
const selectionHandler = instantiationService.createInstance(TreeSelectionHandler); const selectionHandler = instantiationService.createInstance(TreeSelectionHandler);
const node = await getTreeNode(args, objectExplorerService); const node = (await getTreeNode(args, objectExplorerService))!;
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
let connectionProfile = TreeUpdateUtils.getConnectionProfile(node); let connectionProfile = TreeUpdateUtils.getConnectionProfile(node)!;
let metadata = node.metadata; let metadata = node.metadata;
let ownerUri = connectionManagementService.getConnectionUri(connectionProfile); let ownerUri = connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
@@ -271,9 +289,9 @@ CommandsRegistry.registerCommand({
const connectionManagementService = accessor.get(IConnectionManagementService); const connectionManagementService = accessor.get(IConnectionManagementService);
const objectExplorerService = accessor.get(IObjectExplorerService); const objectExplorerService = accessor.get(IObjectExplorerService);
const selectionHandler = instantiationService.createInstance(TreeSelectionHandler); const selectionHandler = instantiationService.createInstance(TreeSelectionHandler);
const node = await getTreeNode(args, objectExplorerService); const node = (await getTreeNode(args, objectExplorerService))!;
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
let connectionProfile = TreeUpdateUtils.getConnectionProfile(node); let connectionProfile = TreeUpdateUtils.getConnectionProfile(node)!;
let metadata = node.metadata; let metadata = node.metadata;
let ownerUri = connectionManagementService.getConnectionUri(connectionProfile); let ownerUri = connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
@@ -297,8 +315,8 @@ CommandsRegistry.registerCommand({
//set objectExplorerTreeNode for context menu clicks //set objectExplorerTreeNode for context menu clicks
const node = await getTreeNode(args, objectExplorerService); const node = await getTreeNode(args, objectExplorerService);
selectionHandler.onTreeActionStateChange(true); selectionHandler.onTreeActionStateChange(true);
const connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>node); const connectionProfile = TreeUpdateUtils.getConnectionProfile(<TreeNode>node)!;
const metadata = node.metadata; const metadata = node!.metadata;
let ownerUri = connectionManagementService.getConnectionUri(connectionProfile); let ownerUri = connectionManagementService.getConnectionUri(connectionProfile);
ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile); ownerUri = connectionManagementService.getFormattedUri(ownerUri, connectionProfile);
@@ -320,9 +338,9 @@ export async function handleOeRefreshCommand(accessor: ServicesAccessor, args: O
const logService = accessor.get(ILogService); const logService = accessor.get(ILogService);
const notificationService = accessor.get(INotificationService); const notificationService = accessor.get(INotificationService);
const treeNode = await getTreeNode(args, objectExplorerService); const treeNode = await getTreeNode(args, objectExplorerService);
const tree = objectExplorerService.getServerTreeView().tree; const tree = objectExplorerService.getServerTreeView()!.tree;
try { try {
await objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode); await objectExplorerService.refreshTreeNode(treeNode!.getSession()!, treeNode!);
if (tree instanceof AsyncServerTree) { if (tree instanceof AsyncServerTree) {
await tree.updateChildren(treeNode); await tree.updateChildren(treeNode);
} else { } else {
@@ -330,7 +348,7 @@ export async function handleOeRefreshCommand(accessor: ServicesAccessor, args: O
} }
} catch (err) { } catch (err) {
// Display message to the user but also log the entire error to the console for the stack trace // 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))); notificationService.error(localize('refreshError', "An error occurred refreshing node '{0}': {1}", args.nodeInfo?.label, getErrorMessage(err)));
logService.error(err); logService.error(err);
} }
} }

View File

@@ -20,7 +20,6 @@ import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType'
import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView'; import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView';
import { createObjectExplorerServiceMock } from 'sql/workbench/services/objectExplorer/test/browser/testObjectExplorerService'; import { createObjectExplorerServiceMock } from 'sql/workbench/services/objectExplorer/test/browser/testObjectExplorerService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { TestTree } from 'sql/workbench/test/treeMock'; import { TestTree } from 'sql/workbench/test/treeMock';
import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService'; import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService';
@@ -56,7 +55,7 @@ const oeActionArgs: ObjectExplorerActionsContext = { connectionProfile: connecti
let instantiationService: IInstantiationService; let instantiationService: IInstantiationService;
let logServiceMock: TypeMoq.Mock<ILogService>; let logServiceMock: TypeMoq.Mock<ILogService>;
let treeMock: TypeMoq.Mock<ITree>; let treeMock: TypeMoq.Mock<TestTree>;
suite('Scripting Actions', () => { suite('Scripting Actions', () => {
@@ -68,7 +67,7 @@ suite('Scripting Actions', () => {
const serverTreeViewMock = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Loose, connectionManagementServiceMock.object, instantiationService, undefined, undefined, undefined, undefined, capabilitiesService); const serverTreeViewMock = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Loose, connectionManagementServiceMock.object, instantiationService, undefined, undefined, undefined, undefined, capabilitiesService);
treeMock = TypeMoq.Mock.ofType(TestTree); treeMock = TypeMoq.Mock.ofType(TestTree);
serverTreeViewMock.setup(x => x.tree).returns(() => treeMock.object); serverTreeViewMock.setup(x => x.tree).returns(() => treeMock.object);
collection.set(IObjectExplorerService, createObjectExplorerServiceMock({ serverTreeView: serverTreeViewMock.object, treeNode: treeNode }).object); collection.set(IObjectExplorerService, createObjectExplorerServiceMock({ serverTreeView: serverTreeViewMock.object, treeNode: treeNode }));
logServiceMock = TypeMoq.Mock.ofInstance(new NullLogService()); logServiceMock = TypeMoq.Mock.ofInstance(new NullLogService());
collection.set(ILogService, logServiceMock.object); collection.set(ILogService, logServiceMock.object);
collection.set(INotificationService, new TestNotificationService()); collection.set(INotificationService, new TestNotificationService());

View File

@@ -18,7 +18,7 @@ export class ConnectionContextKey implements IContextKey<IConnectionProfile> {
private _providerKey: IContextKey<string>; private _providerKey: IContextKey<string>;
private _serverKey: IContextKey<string>; private _serverKey: IContextKey<string>;
private _databaseKey: IContextKey<string>; private _databaseKey: IContextKey<string | undefined>;
private _connectionKey: IContextKey<IConnectionProfile>; private _connectionKey: IContextKey<IConnectionProfile>;
private _isQueryProviderKey: IContextKey<boolean>; private _isQueryProviderKey: IContextKey<boolean>;
private _canOpenInAzurePortal: IContextKey<boolean>; private _canOpenInAzurePortal: IContextKey<boolean>;

View File

@@ -52,7 +52,7 @@ export class AsyncServerTreeDataSource implements IAsyncDataSource<ConnectionPro
if (element.children) { if (element.children) {
return element.children; return element.children;
} else { } else {
return await this._objectExplorerService.resolveTreeNodeChildren(element.getSession(), element); return await this._objectExplorerService.resolveTreeNodeChildren(element.getSession()!, element);
} }
} }
} catch (err) { } catch (err) {

View File

@@ -37,15 +37,15 @@ export class AsyncServerTreeDragAndDrop implements ITreeDragAndDrop<ServerTreeEl
* Returns a uri if the given element should be allowed to drag. * Returns a uri if the given element should be allowed to drag.
* Returns null, otherwise. * Returns null, otherwise.
*/ */
public getDragURI(element: ServerTreeElement): string { public getDragURI(element: ServerTreeElement): string | null {
return this._dragAndDrop.getDragURI(this._tree, element); return this._dragAndDrop.getDragURI(this._tree!, element);
} }
/** /**
* Returns a label(name) to display when dragging the element. * Returns a label(name) to display when dragging the element.
*/ */
public getDragLabel(elements: ServerTreeElement[]): string { public getDragLabel(elements: ServerTreeElement[]): string {
return this._dragAndDrop.getDragLabel(this._tree, elements); return this._dragAndDrop.getDragLabel(this._tree!, elements);
} }
/** /**
@@ -54,17 +54,17 @@ export class AsyncServerTreeDragAndDrop implements ITreeDragAndDrop<ServerTreeEl
public onDragStart(dragAndDropData: IDragAndDropData, originalEvent: DragEvent): void { public onDragStart(dragAndDropData: IDragAndDropData, originalEvent: DragEvent): void {
// Force the event cast while in preview - we don't use any of the mouse properties on the // Force the event cast while in preview - we don't use any of the mouse properties on the
// implementation so this is fine for now // implementation so this is fine for now
return this._dragAndDrop.onDragStart(this._tree, dragAndDropData, <any>originalEvent); return this._dragAndDrop.onDragStart(this._tree!, dragAndDropData, <any>originalEvent);
} }
public onDragOver(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { public onDragOver(data: IDragAndDropData, targetElement: ServerTreeElement | undefined, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
// Dropping onto an empty space (undefined targetElement) we treat as wanting to move into the root connection group // Dropping onto an empty space (undefined targetElement) we treat as wanting to move into the root connection group
if (!targetElement) { if (!targetElement) {
targetElement = this._tree?.getInput(); targetElement = this._tree?.getInput();
} }
// Force the event cast while in preview - we don't use any of the mouse properties on the // Force the event cast while in preview - we don't use any of the mouse properties on the
// implementation so this is fine for now // implementation so this is fine for now
const canDragOver = this._dragAndDrop.onDragOver(this._tree, data, targetElement, <any>originalEvent); const canDragOver = this._dragAndDrop.onDragOver(this._tree!, data, targetElement, <any>originalEvent);
if (canDragOver.accept) { if (canDragOver.accept) {
return TreeDragOverReactions.acceptBubbleDown(canDragOver.autoExpand); return TreeDragOverReactions.acceptBubbleDown(canDragOver.autoExpand);
@@ -76,14 +76,14 @@ export class AsyncServerTreeDragAndDrop implements ITreeDragAndDrop<ServerTreeEl
/** /**
* Handle a drop in the server tree. * Handle a drop in the server tree.
*/ */
public drop(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): void { public drop(data: IDragAndDropData, targetElement: ServerTreeElement | undefined, targetIndex: number, originalEvent: DragEvent): void {
// Dropping onto an empty space (undefined targetElement) we treat as wanting to move into the root connection group // Dropping onto an empty space (undefined targetElement) we treat as wanting to move into the root connection group
if (!targetElement) { if (!targetElement) {
targetElement = this._tree?.getInput(); targetElement = this._tree?.getInput();
} }
// Force the event cast while in preview - we don't use any of the mouse properties on the // Force the event cast while in preview - we don't use any of the mouse properties on the
// implementation so this is fine for now // implementation so this is fine for now
this._dragAndDrop.drop(this._tree, data, targetElement, <any>originalEvent); this._dragAndDrop.drop(this._tree!, data, targetElement, <any>originalEvent);
} }
public onDragEnd(originalEvent: DragEvent): void { public onDragEnd(originalEvent: DragEvent): void {
@@ -102,7 +102,7 @@ export class AsyncRecentConnectionsDragAndDrop implements ITreeDragAndDrop<Serve
return (<ConnectionProfile>element).id; return (<ConnectionProfile>element).id;
} }
else if (element instanceof ConnectionProfileGroup) { else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).id; return (<ConnectionProfileGroup>element).id ?? null;
} }
return null; return null;
} }
@@ -117,7 +117,7 @@ export class AsyncRecentConnectionsDragAndDrop implements ITreeDragAndDrop<Serve
else if (elements[0] instanceof ConnectionProfileGroup) { else if (elements[0] instanceof ConnectionProfileGroup) {
return elements[0].name; return elements[0].name;
} }
return undefined; return '';
} }
/** /**

View File

@@ -8,6 +8,6 @@ import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser
export class AsyncServerTreeIdentityProvider implements IIdentityProvider<ServerTreeElement> { export class AsyncServerTreeIdentityProvider implements IIdentityProvider<ServerTreeElement> {
getId(element: ServerTreeElement): { toString(): string; } { getId(element: ServerTreeElement): { toString(): string; } {
return element.id; return element.id!;
} }
} }

View File

@@ -8,7 +8,7 @@ import 'vs/css!./media/objectTypes/objecttypes';
import * as dom from 'vs/base/browser/dom'; import * as dom from 'vs/base/browser/dom';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { ConnectionProfile, IconPath } from 'sql/platform/connection/common/connectionProfile';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
import { iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer'; import { iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer';
@@ -22,6 +22,7 @@ import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
import { ServerTreeRenderer } from 'sql/workbench/services/objectExplorer/browser/serverTreeRenderer'; import { ServerTreeRenderer } from 'sql/workbench/services/objectExplorer/browser/serverTreeRenderer';
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel'; import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
import { withNullAsUndefined } from 'vs/base/common/types';
class ConnectionProfileGroupTemplate extends Disposable { class ConnectionProfileGroupTemplate extends Disposable {
private _root: HTMLElement; private _root: HTMLElement;
@@ -31,7 +32,7 @@ class ConnectionProfileGroupTemplate extends Disposable {
container: HTMLElement container: HTMLElement
) { ) {
super(); super();
container.parentElement.classList.add('server-group'); container.parentElement!.classList.add('server-group');
container.classList.add('server-group'); container.classList.add('server-group');
this._root = dom.append(container, dom.$('.server-group')); this._root = dom.append(container, dom.$('.server-group'));
this._nameContainer = dom.append(this._root, dom.$('span.name')); this._nameContainer = dom.append(this._root, dom.$('span.name'));
@@ -89,7 +90,7 @@ class ConnectionProfileTemplate extends Disposable {
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService @IConnectionManagementService private _connectionManagementService: IConnectionManagementService
) { ) {
super(); super();
container.parentElement.classList.add('connection-profile'); container.parentElement!.classList.add('connection-profile');
this._root = dom.append(container, dom.$('.connection-tile')); this._root = dom.append(container, dom.$('.connection-tile'));
this._icon = dom.append(this._root, dom.$('div.icon server-page')); this._icon = dom.append(this._root, dom.$('div.icon server-page'));
this._connectionStatusBadge = dom.append(this._icon, dom.$('div.connection-status-badge')); this._connectionStatusBadge = dom.append(this._icon, dom.$('div.connection-status-badge'));
@@ -98,16 +99,15 @@ class ConnectionProfileTemplate extends Disposable {
set(element: ConnectionProfile) { set(element: ConnectionProfile) {
if (!this._isCompact) { if (!this._isCompact) {
let iconPath: IconPath = getIconPath(element, this._connectionManagementService); let iconPath: IconPath | undefined = getIconPath(element, this._connectionManagementService);
if (this._connectionManagementService.isConnected(undefined, element)) { if (this._connectionManagementService.isConnected(undefined, element)) {
this._connectionStatusBadge.classList.remove('disconnected'); this._connectionStatusBadge.classList.remove('disconnected');
this._connectionStatusBadge.classList.add('connected'); this._connectionStatusBadge.classList.add('connected');
renderServerIcon(this._icon, iconPath);
} else { } else {
this._connectionStatusBadge.classList.remove('connected'); this._connectionStatusBadge.classList.remove('connected');
this._connectionStatusBadge.classList.add('disconnected'); this._connectionStatusBadge.classList.add('disconnected');
renderServerIcon(this._icon, iconPath);
} }
renderServerIcon(this._icon, iconPath);
} }
let label = element.title; let label = element.title;
@@ -156,7 +156,7 @@ class TreeNodeTemplate extends Disposable {
set(element: TreeNode) { set(element: TreeNode) {
// Use an explicitly defined iconType first. If not defined, fall back to using nodeType and // Use an explicitly defined iconType first. If not defined, fall back to using nodeType and
// other compount indicators instead. // other compount indicators instead.
let iconName: string = undefined; let iconName: string | undefined = undefined;
if (element.iconType) { if (element.iconType) {
iconName = (typeof element.iconType === 'string') ? element.iconType : element.iconType.id; iconName = (typeof element.iconType === 'string') ? element.iconType : element.iconType.id;
} else { } else {
@@ -171,7 +171,7 @@ class TreeNodeTemplate extends Disposable {
let tokens: string[] = []; let tokens: string[] = [];
for (let index = 1; index < this._icon.classList.length; index++) { for (let index = 1; index < this._icon.classList.length; index++) {
tokens.push(this._icon.classList.item(index)); tokens.push(this._icon.classList.item(index)!);
} }
this._icon.classList.remove(...tokens); this._icon.classList.remove(...tokens);
this._icon.classList.add('icon'); this._icon.classList.add('icon');
@@ -229,7 +229,7 @@ export class ServerTreeAccessibilityProvider implements IListAccessibilityProvid
getAriaLabel(element: ServerTreeElement): string | null { getAriaLabel(element: ServerTreeElement): string | null {
if (element instanceof ConnectionProfileGroup) { if (element instanceof ConnectionProfileGroup) {
return element.fullName; return element.fullName ?? null;
} else if (element instanceof ConnectionProfile) { } else if (element instanceof ConnectionProfile) {
return element.title; return element.title;
} }
@@ -240,22 +240,22 @@ export class ServerTreeAccessibilityProvider implements IListAccessibilityProvid
/** /**
* Returns the first parent which contains the className * Returns the first parent which contains the className
*/ */
function findParentElement(container: HTMLElement, className: string): HTMLElement { function findParentElement(container: HTMLElement, className: string): HTMLElement | undefined {
let currentElement = container; let currentElement: HTMLElement | null = container;
while (currentElement) { while (currentElement) {
if (currentElement.className.indexOf(className) > -1) { if (currentElement.className.indexOf(className) > -1) {
break; break;
} }
currentElement = currentElement.parentElement; currentElement = currentElement.parentElement;
} }
return currentElement; return withNullAsUndefined(currentElement);
} }
function getIconPath(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService): IconPath { function getIconPath(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService): IconPath | undefined {
if (!connection) { return undefined; } if (!connection) { return undefined; }
if (connection['iconPath']) { if (connection.iconPath) {
return connection['iconPath']; return connection.iconPath;
} }
let iconId = connectionManagementService.getConnectionIconId(connection.id); let iconId = connectionManagementService.getConnectionIconId(connection.id);
@@ -264,8 +264,8 @@ function getIconPath(connection: ConnectionProfile, connectionManagementService:
let providerProperties = connectionManagementService.getProviderProperties(connection.providerName); let providerProperties = connectionManagementService.getProviderProperties(connection.providerName);
if (!providerProperties) { return undefined; } if (!providerProperties) { return undefined; }
let iconPath: IconPath = undefined; let iconPath: IconPath | undefined = undefined;
let pathConfig: URI | IconPath | { id: string, path: IconPath }[] = providerProperties['iconPath']; let pathConfig: URI | IconPath | { id: string, path: IconPath }[] | undefined = providerProperties['iconPath'];
if (Array.isArray(pathConfig)) { if (Array.isArray(pathConfig)) {
for (const e of pathConfig) { for (const e of pathConfig) {
if (!e.id || e.id === iconId) { if (!e.id || e.id === iconId) {
@@ -274,25 +274,18 @@ function getIconPath(connection: ConnectionProfile, connectionManagementService:
break; break;
} }
} }
} else if (pathConfig['light']) { } else if (URI.isUri(pathConfig)) {
iconPath = pathConfig as IconPath; iconPath = { light: pathConfig, dark: pathConfig };
connection['iconPath'] = iconPath; connection.iconPath = iconPath;
} else { } else {
let singlePath = pathConfig as URI; connection.iconPath = pathConfig;
iconPath = { light: singlePath, dark: singlePath };
connection['iconPath'] = iconPath;
} }
return iconPath; return iconPath;
} }
function renderServerIcon(element: HTMLElement, iconPath: IconPath): void { function renderServerIcon(element: HTMLElement, iconPath?: IconPath): void {
if (!element) { return; } if (!element) { return; }
if (iconPath) { if (iconPath) {
iconRenderer.putIcon(element, iconPath); iconRenderer.putIcon(element, iconPath);
} }
} }
interface IconPath {
light: URI;
dark: URI;
}

View File

@@ -28,6 +28,8 @@ export function supportsFolderNodeNameDrop(nodeId: string, label: string): boole
return false; return false;
} }
function escapeString(input: string): string;
function escapeString(input: undefined): undefined;
function escapeString(input: string | undefined): string | undefined { function escapeString(input: string | undefined): string | undefined {
return input?.replace(/]/g, ']]'); return input?.replace(/]/g, ']]');
} }
@@ -46,22 +48,22 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
* Returns a uri if the given element should be allowed to drag. * Returns a uri if the given element should be allowed to drag.
* Returns null, otherwise. * Returns null, otherwise.
*/ */
public getDragURI(tree: AsyncServerTree | ITree, element: any): string { public getDragURI(tree: AsyncServerTree | ITree, element: any): string | null {
if (element) { if (element) {
if (element instanceof ConnectionProfile) { if (element instanceof ConnectionProfile) {
return (<ConnectionProfile>element).id; return element.id;
} else if (element instanceof ConnectionProfileGroup) { } else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).id; return element.id ?? null;
} else if (supportsNodeNameDrop(element.nodeTypeId)) { } else if (supportsNodeNameDrop(element.nodeTypeId)) {
return (<TreeNode>element).id; return (<TreeNode>element).id;
} else if (supportsFolderNodeNameDrop(element.nodeTypeId, element.label) && element.children) { } else if (supportsFolderNodeNameDrop(element.nodeTypeId, element.label) && element.children) {
return (<TreeNode>element).id; return (<TreeNode>element).id;
} else { } else {
return undefined; return null;
} }
} }
else { else {
return undefined; return null;
} }
} }
@@ -78,11 +80,11 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
return elements[0].label; return elements[0].label;
} }
else { else {
return undefined; return '';
} }
} }
else { else {
return undefined; return '';
} }
} }
@@ -125,7 +127,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
return element.connection.providerName; return element.connection.providerName;
} }
return this.getProviderNameFromElement(element.parent); return this.getProviderNameFromElement(element.parent!);
} }
@@ -133,7 +135,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
let canDragOver: boolean = true; let canDragOver: boolean = true;
if (source instanceof ConnectionProfile) { if (source instanceof ConnectionProfile) {
if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id)) { if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id!)) {
canDragOver = false; canDragOver = false;
} }
} else if (source instanceof ConnectionProfileGroup) { } else if (source instanceof ConnectionProfileGroup) {
@@ -158,7 +160,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
// Verify if the connection can be moved to the target group // Verify if the connection can be moved to the target group
const source = data.getData()[0]; const source = data.getData()[0];
if (source instanceof ConnectionProfile) { if (source instanceof ConnectionProfile) {
if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id)) { if (!this._connectionManagementService.canChangeConnectionConfig(source, targetConnectionProfileGroup.id!)) {
canDragOver = false; canDragOver = false;
} }
} else if (source instanceof ConnectionProfileGroup) { } else if (source instanceof ConnectionProfileGroup) {
@@ -201,7 +203,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
if (source instanceof ConnectionProfile) { if (source instanceof ConnectionProfile) {
// Change group id of profile // Change group id of profile
this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id).then(() => { this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id!).then(() => {
if (tree) { if (tree) {
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup); TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup);
} }
@@ -224,13 +226,13 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
TreeUpdateUtils.isInDragAndDrop = false; TreeUpdateUtils.isInDragAndDrop = false;
} }
private getTargetGroup(targetElement: any): ConnectionProfileGroup { private getTargetGroup(targetElement: ConnectionProfileGroup | ConnectionProfile): ConnectionProfileGroup {
let targetConnectionProfileGroup: ConnectionProfileGroup; let targetConnectionProfileGroup: ConnectionProfileGroup;
if (targetElement instanceof ConnectionProfile) { if (targetElement instanceof ConnectionProfile) {
targetConnectionProfileGroup = (<ConnectionProfile>targetElement).getParent(); targetConnectionProfileGroup = targetElement.getParent()!;
} }
else { else {
targetConnectionProfileGroup = <ConnectionProfileGroup>targetElement; targetConnectionProfileGroup = targetElement;
} }
return targetConnectionProfileGroup; return targetConnectionProfileGroup;
@@ -256,12 +258,12 @@ export class RecentConnectionsDragAndDrop implements IDragAndDrop {
* Returns a uri if the given element should be allowed to drag. * Returns a uri if the given element should be allowed to drag.
* Returns null, otherwise. * Returns null, otherwise.
*/ */
public getDragURI(tree: ITree, element: any): string { public getDragURI(tree: ITree, element: any): string | null {
if (element instanceof ConnectionProfile) { if (element instanceof ConnectionProfile) {
return (<ConnectionProfile>element).id; return (<ConnectionProfile>element).id;
} }
else if (element instanceof ConnectionProfileGroup) { else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).id; return (<ConnectionProfileGroup>element).id ?? null;
} }
return null; return null;
} }
@@ -276,7 +278,7 @@ export class RecentConnectionsDragAndDrop implements IDragAndDrop {
else if (elements[0] instanceof ConnectionProfileGroup) { else if (elements[0] instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>elements[0]).name; return (<ConnectionProfileGroup>elements[0]).name;
} }
return undefined; return '';
} }
/** /**

View File

@@ -10,11 +10,11 @@ import { URI } from 'vs/base/common/uri';
class IconRenderer { class IconRenderer {
private iconRegistered: Set<string> = new Set<string>(); private iconRegistered: Set<string> = new Set<string>();
public registerIcon(path: URI | IconPath): string { public registerIcon(path: URI | IconPath): string | undefined {
if (!path) { return undefined; } if (!path) { return undefined; }
let iconPath: IconPath = this.toIconPath(path); let iconPath: IconPath = this.toIconPath(path);
let iconUid: string = this.getIconUid(iconPath); let iconUid: string | undefined = this.getIconUid(iconPath);
if (!this.iconRegistered.has(iconUid)) { if (iconUid && !this.iconRegistered.has(iconUid)) {
createCSSRule(`.icon#${iconUid}`, `background: ${asCSSUrl(iconPath.light || iconPath.dark)} center center no-repeat`); createCSSRule(`.icon#${iconUid}`, `background: ${asCSSUrl(iconPath.light || iconPath.dark)} center center no-repeat`);
createCSSRule(`.vs-dark .icon#${iconUid}, .hc-black .icon#${iconUid}`, `background: ${asCSSUrl(iconPath.dark)} center center no-repeat`); createCSSRule(`.vs-dark .icon#${iconUid}, .hc-black .icon#${iconUid}`, `background: ${asCSSUrl(iconPath.dark)} center center no-repeat`);
this.iconRegistered.add(iconUid); this.iconRegistered.add(iconUid);
@@ -22,30 +22,30 @@ class IconRenderer {
return iconUid; return iconUid;
} }
public getIconUid(path: URI | IconPath): string { public getIconUid(path: URI | IconPath): string | undefined {
if (!path) { return undefined; } if (!path) { return undefined; }
let iconPath: IconPath = this.toIconPath(path); let iconPath: IconPath = this.toIconPath(path);
return `icon${hash(iconPath.light.toString() + iconPath.dark.toString())}`; return `icon${hash(iconPath.light.toString() + iconPath.dark.toString())}`;
} }
private toIconPath(path: URI | IconPath): IconPath { private toIconPath(path: URI | IconPath): IconPath {
if (path['light']) { if (URI.isUri(path)) {
return path as IconPath; let singlePath = path;
} else {
let singlePath = path as URI;
return { light: singlePath, dark: singlePath }; return { light: singlePath, dark: singlePath };
} else {
return path;
} }
} }
public putIcon(element: HTMLElement, path: URI | IconPath): void { public putIcon(element: HTMLElement, path: URI | IconPath): void {
if (!element || !path) { return undefined; } if (!element || !path) { return undefined; }
let iconUid: string = this.registerIcon(path); let iconUid: string | undefined = this.registerIcon(path);
element.id = iconUid; element.id = iconUid ?? '';
} }
public removeIcon(element: HTMLElement): void { public removeIcon(element: HTMLElement): void {
if (!element) { return undefined; } if (!element) { return undefined; }
element.id = undefined; element.id = '';
} }
} }
@@ -117,9 +117,9 @@ class BadgeRenderer {
public removeBadge(element: HTMLElement, badgeClass: string): void { public removeBadge(element: HTMLElement, badgeClass: string): void {
let children: HTMLCollection = element.children; let children: HTMLCollection = element.children;
let current = children[0]; let current: Element | null = children[0];
while (current) { while (current) {
let next = current.nextElementSibling; let next: Element | null = current.nextElementSibling;
if (current.classList.contains(badgeClass)) { if (current.classList.contains(badgeClass)) {
current.remove(); current.remove();
break; break;

View File

@@ -19,10 +19,8 @@ export class RecentConnectionDataSource implements IDataSource {
public getId(tree: ITree, element: any): string { public getId(tree: ITree, element: any): string {
if (element instanceof ConnectionProfile) { if (element instanceof ConnectionProfile) {
return (<ConnectionProfile>element).id; return (<ConnectionProfile>element).id;
} else if (element instanceof ConnectionProfileGroup) {
return (<ConnectionProfileGroup>element).id;
} else { } else {
return undefined; return (<ConnectionProfileGroup>element).id!;
} }
} }

View File

@@ -74,7 +74,7 @@ export class ServerTreeController extends treedefaults.DefaultController {
context.nodeInfo = element.toNodeInfo(); context.nodeInfo = element.toNodeInfo();
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces // 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 // the connection ID with a new one. This breaks a number of internal tasks
context.connectionProfile = element.getConnectionProfile().toIConnectionProfile(); context.connectionProfile = element.getConnectionProfile()!.toIConnectionProfile();
context.connectionProfile.databaseName = element.getDatabaseName(); context.connectionProfile.databaseName = element.getDatabaseName();
actionContext = context; actionContext = context;
} else if (element instanceof ConnectionProfile) { } else if (element instanceof ConnectionProfile) {

View File

@@ -30,13 +30,7 @@ export class ServerTreeDataSource implements IDataSource {
* No more than one element may use a given identifier. * No more than one element may use a given identifier.
*/ */
public getId(tree: ITree, element: any): string { public getId(tree: ITree, element: any): string {
if (element instanceof ConnectionProfile return element.id;
|| element instanceof ConnectionProfileGroup
|| element instanceof TreeNode) {
return element.id;
} else {
return undefined;
}
} }
/** /**
@@ -67,7 +61,7 @@ export class ServerTreeDataSource implements IDataSource {
return node.children; return node.children;
} else { } else {
try { try {
return this._objectExplorerService.resolveTreeNodeChildren(node.getSession(), node); return this._objectExplorerService.resolveTreeNodeChildren(node.getSession()!, node);
} catch (expandError) { } catch (expandError) {
await node.setExpandedState(TreeItemCollapsibleState.Collapsed); await node.setExpandedState(TreeItemCollapsibleState.Collapsed);
node.errorStateMessage = expandError; node.errorStateMessage = expandError;

View File

@@ -16,6 +16,7 @@ import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { badgeRenderer, iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer'; import { badgeRenderer, iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel'; import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IConnectionTemplateData { export interface IConnectionTemplateData {
root: HTMLElement; root: HTMLElement;
@@ -132,7 +133,7 @@ export class ServerTreeRenderer implements IRenderer {
private renderObjectExplorer(treeNode: TreeNode, templateData: IObjectExplorerTemplateData): void { private renderObjectExplorer(treeNode: TreeNode, templateData: IObjectExplorerTemplateData): void {
// Use an explicitly defined iconType first. If not defined, fall back to using nodeType and // Use an explicitly defined iconType first. If not defined, fall back to using nodeType and
// other compount indicators instead. // other compount indicators instead.
let iconName: string = undefined; let iconName: string | undefined = undefined;
if (treeNode.iconType) { if (treeNode.iconType) {
iconName = (typeof treeNode.iconType === 'string') ? treeNode.iconType : treeNode.iconType.id; iconName = (typeof treeNode.iconType === 'string') ? treeNode.iconType : treeNode.iconType.id;
} else { } else {
@@ -147,7 +148,7 @@ export class ServerTreeRenderer implements IRenderer {
let tokens: string[] = []; let tokens: string[] = [];
for (let index = 1; index < templateData.icon.classList.length; index++) { for (let index = 1; index < templateData.icon.classList.length; index++) {
tokens.push(templateData.icon.classList.item(index)); tokens.push(templateData.icon.classList.item(index)!);
} }
templateData.icon.classList.remove(...tokens); templateData.icon.classList.remove(...tokens);
templateData.icon.classList.add('icon'); templateData.icon.classList.add('icon');
@@ -162,11 +163,11 @@ export class ServerTreeRenderer implements IRenderer {
templateData.root.title = treeNode.label; templateData.root.title = treeNode.label;
} }
private getIconPath(connection: ConnectionProfile): IconPath { private getIconPath(connection: ConnectionProfile): IconPath | undefined {
if (!connection) { return undefined; } if (!connection) { return undefined; }
if (connection['iconPath']) { if (connection.iconPath) {
return connection['iconPath']; return connection.iconPath;
} }
let iconId = this._connectionManagementService.getConnectionIconId(connection.id); let iconId = this._connectionManagementService.getConnectionIconId(connection.id);
@@ -175,8 +176,8 @@ export class ServerTreeRenderer implements IRenderer {
let providerProperties = this._connectionManagementService.getProviderProperties(connection.providerName); let providerProperties = this._connectionManagementService.getProviderProperties(connection.providerName);
if (!providerProperties) { return undefined; } if (!providerProperties) { return undefined; }
let iconPath: IconPath = undefined; let iconPath: IconPath | undefined = undefined;
let pathConfig: URI | IconPath | { id: string, path: IconPath }[] = providerProperties['iconPath']; let pathConfig: URI | IconPath | { id: string, path: IconPath }[] | undefined = providerProperties.iconPath;
if (Array.isArray(pathConfig)) { if (Array.isArray(pathConfig)) {
for (const e of pathConfig) { for (const e of pathConfig) {
if (!e.id || e.id === iconId) { if (!e.id || e.id === iconId) {
@@ -185,18 +186,18 @@ export class ServerTreeRenderer implements IRenderer {
break; break;
} }
} }
} else if (pathConfig['light']) { } else if (URI.isUri(pathConfig)) {
iconPath = pathConfig as IconPath;
connection['iconPath'] = iconPath;
} else {
let singlePath = pathConfig as URI; let singlePath = pathConfig as URI;
iconPath = { light: singlePath, dark: singlePath }; iconPath = { light: singlePath, dark: singlePath };
connection['iconPath'] = iconPath; connection.iconPath = iconPath;
} else {
iconPath = pathConfig as IconPath;
connection.iconPath = iconPath;
} }
return iconPath; return iconPath;
} }
private renderServerIcon(element: HTMLElement, iconPath: IconPath, isConnected: boolean): void { private renderServerIcon(element: HTMLElement, iconPath: IconPath | undefined, isConnected: boolean): void {
if (!element) { return; } if (!element) { return; }
if (iconPath) { if (iconPath) {
iconRenderer.putIcon(element, iconPath); iconRenderer.putIcon(element, iconPath);
@@ -209,7 +210,7 @@ export class ServerTreeRenderer implements IRenderer {
private renderConnection(connection: ConnectionProfile, templateData: IConnectionTemplateData): void { private renderConnection(connection: ConnectionProfile, templateData: IConnectionTemplateData): void {
if (!this._isCompact) { if (!this._isCompact) {
let iconPath: IconPath = this.getIconPath(connection); let iconPath = this.getIconPath(connection);
if (this._connectionManagementService.isConnected(undefined, connection)) { if (this._connectionManagementService.isConnected(undefined, connection)) {
templateData.icon.classList.remove('disconnected'); templateData.icon.classList.remove('disconnected');
templateData.icon.classList.add('connected'); templateData.icon.classList.add('connected');
@@ -252,15 +253,15 @@ export class ServerTreeRenderer implements IRenderer {
/** /**
* Returns the first parent which contains the className * Returns the first parent which contains the className
*/ */
private findParentElement(container: HTMLElement, className: string): HTMLElement { private findParentElement(container: HTMLElement, className: string): HTMLElement | undefined {
let currentElement = container; let currentElement: HTMLElement | null = container;
while (currentElement) { while (currentElement) {
if (currentElement.className.indexOf(className) > -1) { if (currentElement.className.indexOf(className) > -1) {
break; break;
} }
currentElement = currentElement.parentElement; currentElement = currentElement.parentElement;
} }
return currentElement; return withNullAsUndefined(currentElement);
} }
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void { public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {

View File

@@ -142,5 +142,5 @@ export class TreeCreationUtils {
} }
function useAsyncServerTree(configurationService: IConfigurationService): boolean { function useAsyncServerTree(configurationService: IConfigurationService): boolean {
return configurationService.getValue('workbench.enablePreviewFeatures') && configurationService.getValue('serverTree.useAsyncServerTree'); return configurationService.getValue<boolean>('workbench.enablePreviewFeatures') && configurationService.getValue<boolean>('serverTree.useAsyncServerTree');
} }

View File

@@ -98,7 +98,7 @@ suite('AsyncServerTreeDragAndDrop', () => {
assert.equal(treeNodeArray[0].label, labelTreeNode); assert.equal(treeNodeArray[0].label, labelTreeNode);
let labelUndefined = serverTreeDragAndDrop.getDragLabel(undefined); let labelUndefined = serverTreeDragAndDrop.getDragLabel(undefined);
assert.equal(undefined, labelUndefined); assert.equal('', labelUndefined);
}); });

View File

@@ -103,7 +103,7 @@ suite('SQL Drag And Drop Controller tests', () => {
assert.equal(treeNodeArray[0].label, labelTreeNode); assert.equal(treeNodeArray[0].label, labelTreeNode);
let labelUndefined = serverTreeDragAndDrop.getDragLabel(testTree, null); let labelUndefined = serverTreeDragAndDrop.getDragLabel(testTree, null);
assert.equal(undefined, labelUndefined); assert.equal('', labelUndefined);
}); });

View File

@@ -26,18 +26,18 @@ export type ObjectExplorerServiceMockOptions = {
* *
* @param options Options to use for setting up functions on the mock to return various values * @param options Options to use for setting up functions on the mock to return various values
*/ */
export function createObjectExplorerServiceMock(options: ObjectExplorerServiceMockOptions): TypeMoq.Mock<IObjectExplorerService> { export function createObjectExplorerServiceMock(options: ObjectExplorerServiceMockOptions): IObjectExplorerService {
const objectExplorerService = TypeMoq.Mock.ofType(TestObjectExplorerService); const objectExplorerService = TypeMoq.Mock.ofType(TestObjectExplorerService);
if (options.treeNode) { if (options.treeNode) {
objectExplorerService.setup(x => x.getTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(options.treeNode)); objectExplorerService.setup(x => x.getTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(options.treeNode!));
} }
if (options.serverTreeView) { if (options.serverTreeView) {
objectExplorerService.setup(x => x.getServerTreeView()).returns(() => options.serverTreeView); objectExplorerService.setup(x => x.getServerTreeView()).returns(() => options.serverTreeView!);
} }
return objectExplorerService; return objectExplorerService.object;
} }
/** /**
@@ -49,13 +49,13 @@ export class TestObjectExplorerService implements IObjectExplorerService {
constructor() { } constructor() { }
public getSession(sessionId: string): azdata.ObjectExplorerSession { return undefined; } public getSession(sessionId: string): azdata.ObjectExplorerSession { throw new Error('Method not implemented'); }
public providerRegistered(providerId: string): boolean { return true; } public providerRegistered(providerId: string): boolean { return true; }
public get onUpdateObjectExplorerNodes(): Event<ObjectExplorerNodeEventArgs> { return undefined; } public get onUpdateObjectExplorerNodes(): Event<ObjectExplorerNodeEventArgs> { throw new Error('Method not implemented'); }
public get onSelectionOrFocusChange(): Event<void> { return undefined; } public get onSelectionOrFocusChange(): Event<void> { throw new Error('Method not implemented'); }
public async updateObjectExplorerNodes(connection: IConnectionProfile): Promise<void> { } public async updateObjectExplorerNodes(connection: IConnectionProfile): Promise<void> { }
@@ -67,41 +67,41 @@ export class TestObjectExplorerService implements IObjectExplorerService {
public async onSessionDisconnected(handle: number, session: azdata.ObjectExplorerSession): Promise<void> { } public async onSessionDisconnected(handle: number, session: azdata.ObjectExplorerSession): Promise<void> { }
public getObjectExplorerNode(connection: IConnectionProfile): TreeNode { return undefined; } public getObjectExplorerNode(connection: IConnectionProfile): TreeNode { throw new Error('Method not implemented'); }
public async createNewSession(providerId: string, connection: ConnectionProfile): Promise<azdata.ObjectExplorerSessionResponse> { return undefined; } public async createNewSession(providerId: string, connection: ConnectionProfile): Promise<azdata.ObjectExplorerSessionResponse> { throw new Error('Method not implemented'); }
public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { return undefined; } public async expandNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
public async refreshNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { return undefined; } public async refreshNode(providerId: string, session: azdata.ObjectExplorerSession, nodePath: string): Promise<azdata.ObjectExplorerExpandInfo> { throw new Error('Method not implemented'); }
public async closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse> { return undefined; } public async closeSession(providerId: string, session: azdata.ObjectExplorerSession): Promise<azdata.ObjectExplorerCloseSessionResponse> { throw new Error('Method not implemented'); }
public registerProvider(providerId: string, provider: azdata.ObjectExplorerProvider): void { } public registerProvider(providerId: string, provider: azdata.ObjectExplorerProvider): void { }
public registerNodeProvider(nodeProvider: azdata.ObjectExplorerNodeProvider): void { } public registerNodeProvider(nodeProvider: azdata.ObjectExplorerNodeProvider): void { }
public async resolveTreeNodeChildren(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]> { return undefined; } public async resolveTreeNodeChildren(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]> { throw new Error('Method not implemented'); }
public async refreshTreeNode(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]> { return undefined; } public async refreshTreeNode(session: azdata.ObjectExplorerSession, parentTree: TreeNode): Promise<TreeNode[]> { throw new Error('Method not implemented'); }
public registerServerTreeView(view: IServerTreeView): void { } public registerServerTreeView(view: IServerTreeView): void { }
public getSelectedProfileAndDatabase(): { profile: ConnectionProfile, databaseName: string } { return undefined; } public getSelectedProfileAndDatabase(): { profile: ConnectionProfile, databaseName: string } | undefined { return undefined; }
public isFocused(): boolean { return true; } public isFocused(): boolean { return true; }
public getServerTreeView(): IServerTreeView { return undefined; } public getServerTreeView(): IServerTreeView { throw new Error('Method not implemented'); }
public async findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames?: string[]): Promise<azdata.NodeInfo[]> { return undefined; } public async findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames?: string[]): Promise<azdata.NodeInfo[]> { throw new Error('Method not implemented'); }
public getActiveConnectionNodes(): TreeNode[] { return undefined; } public getActiveConnectionNodes(): TreeNode[] { throw new Error('Method not implemented'); }
public async getNodeActions(connectionId: string, nodePath: string): Promise<string[]> { return undefined; } public async getNodeActions(connectionId: string, nodePath: string): Promise<string[]> { throw new Error('Method not implemented'); }
public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> { return undefined; } public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> { throw new Error('Method not implemented'); }
public getSessionConnectionProfile(sessionId: string): azdata.IConnectionProfile { return undefined; } public getSessionConnectionProfile(sessionId: string): azdata.IConnectionProfile { throw new Error('Method not implemented'); }
public async getTreeNode(connectionId: string, nodePath: string): Promise<TreeNode> { return undefined; } public async getTreeNode(connectionId: string, nodePath: string): Promise<TreeNode> { throw new Error('Method not implemented'); }
} }

View File

@@ -28,7 +28,7 @@ export class QueryHistoryInfo {
public connectionProfile: IConnectionProfile, public connectionProfile: IConnectionProfile,
public startTime: Date, public startTime: Date,
status?: QueryStatus) { status?: QueryStatus) {
this.database = connectionProfile ? connectionProfile.databaseName : ''; this.database = connectionProfile?.databaseName ?? '';
this.status = status; this.status = status;
} }
} }

View File

@@ -319,7 +319,7 @@ export class RestoreDialogController implements IRestoreDialogController {
if (this._currentProvider === ConnectionConstants.mssqlProviderName) { if (this._currentProvider === ConnectionConstants.mssqlProviderName) {
let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog; let restoreDialog = this._restoreDialogs[this._currentProvider] as RestoreDialog;
restoreDialog.viewModel.resetRestoreOptions(connection.databaseName); restoreDialog.viewModel.resetRestoreOptions(connection.databaseName!);
this.getMssqlRestoreConfigInfo().then(() => { this.getMssqlRestoreConfigInfo().then(() => {
restoreDialog.open(connection.serverName, this._ownerUri!); restoreDialog.open(connection.serverName, this._ownerUri!);
restoreDialog.validateRestore(); restoreDialog.validateRestore();

View File

@@ -77,7 +77,7 @@ export class TaskService implements ITaskService {
} }
public createNewTask(taskInfo: azdata.TaskInfo) { public createNewTask(taskInfo: azdata.TaskInfo) {
let databaseName: string = taskInfo.databaseName; let databaseName: string | undefined = taskInfo.databaseName;
let serverName: string = taskInfo.serverName; let serverName: string = taskInfo.serverName;
if (taskInfo && taskInfo.connection) { if (taskInfo && taskInfo.connection) {
let connectionProfile = this.connectionManagementService.getConnectionProfile(taskInfo.connection.connectionId); let connectionProfile = this.connectionManagementService.getConnectionProfile(taskInfo.connection.connectionId);

View File

@@ -12,24 +12,24 @@ import { IItemExpandEvent, IItemCollapseEvent } from 'vs/base/parts/tree/browser
*/ */
export class TestTree implements ITree { export class TestTree implements ITree {
readonly onDidChangeFocus: Event<IFocusEvent>; readonly onDidChangeFocus: Event<IFocusEvent> = Event.None;
readonly onDidChangeSelection: Event<ISelectionEvent>; readonly onDidChangeSelection: Event<ISelectionEvent> = Event.None;
readonly onDidChangeHighlight: Event<IHighlightEvent>; readonly onDidChangeHighlight: Event<IHighlightEvent> = Event.None;
readonly onDidExpandItem: Event<IItemExpandEvent>; readonly onDidExpandItem: Event<IItemExpandEvent> = Event.None;
readonly onDidCollapseItem: Event<IItemCollapseEvent>; readonly onDidCollapseItem: Event<IItemCollapseEvent> = Event.None;
readonly onDidDispose: Event<void>; readonly onDidDispose: Event<void> = Event.None;
constructor() { } constructor() { }
public style(styles: ITreeStyles): void { } public style(styles: ITreeStyles): void { }
get onDidFocus(): Event<void> { return undefined; } get onDidFocus(): Event<void> { return Event.None; }
get onDidBlur(): Event<void> { return undefined; } get onDidBlur(): Event<void> { return Event.None; }
get onDidScroll(): Event<void> { return undefined; } get onDidScroll(): Event<void> { return Event.None; }
public getHTMLElement(): HTMLElement { return undefined; } public getHTMLElement(): HTMLElement { throw new Error('Method not implemented'); }
public layout(height?: number, width?: number): void { } public layout(height?: number, width?: number): void { }
@@ -113,7 +113,7 @@ export class TestTree implements ITree {
public deselect(element: any, eventPayload?: any): void { } public deselect(element: any, eventPayload?: any): void { }
getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any> { return undefined; } getNavigator(fromElement?: any, subTreeOnly?: boolean): INavigator<any> { throw new Error('Method not implemented'); }
public dispose(): void { } public dispose(): void { }
} }

View File

@@ -72,7 +72,6 @@
"./sql/workbench/contrib/profiler/**/*.ts", // 100 errors "./sql/workbench/contrib/profiler/**/*.ts", // 100 errors
"./sql/workbench/contrib/query/**/*.ts", // 3077 errors "./sql/workbench/contrib/query/**/*.ts", // 3077 errors
"./sql/workbench/contrib/queryHistory/**/*.ts", // 290 errors "./sql/workbench/contrib/queryHistory/**/*.ts", // 290 errors
"./sql/workbench/contrib/scripting/**/*.ts", // 179 errors
"./sql/workbench/contrib/welcome/**/*.ts", // 66 errors "./sql/workbench/contrib/welcome/**/*.ts", // 66 errors
"./sql/workbench/services/connection/**/*.ts", // 3130 errors "./sql/workbench/services/connection/**/*.ts", // 3130 errors
"./sql/workbench/services/dialog/**/*.ts", // 2995 errors "./sql/workbench/services/dialog/**/*.ts", // 2995 errors