demo bugs (#1301)
* OE shows all connections * OE changes * added node for adding connections * remove dead code * fix test * change connection for new query from OE * fixed OE loading from editor and command together * cleanup after node removal * fix icon paths for diff OS * changed expansion to deferred as well * added new UX for password protected servers * better UX for server errors and save password * fix context menu action order * match profile name in node when making a node from new query * new query and add working fine * added icon for add connection node * fix tests * fix tests * Scripting (#1304) * scripting works * scripting complete * removed dead code * fixed scripting * fix password issue in saved connections
This commit is contained in:
Родитель
f6354613b4
Коммит
b5a3c1ac72
|
@ -368,6 +368,15 @@
|
|||
<trans-unit id="queryOutputHostTitle">
|
||||
<source xml:lang="en">Query Output Document Host</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgAddConnection">
|
||||
<source xml:lang="en">Add Connection</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgSignIn">
|
||||
<source xml:lang="en">Sign In</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="msgConnect">
|
||||
<source xml:lang="en">Connect</source>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
|
|
29
package.json
29
package.json
|
@ -161,7 +161,7 @@
|
|||
"activitybar": [
|
||||
{
|
||||
"id": "objectExplorer",
|
||||
"title": "Object Explorer",
|
||||
"title": "SQL Server",
|
||||
"icon": "./images/server_page_inverse.svg"
|
||||
}
|
||||
]
|
||||
|
@ -216,23 +216,23 @@
|
|||
"view/item/context": [
|
||||
{
|
||||
"command": "extension.objectExplorerNewQuery",
|
||||
"when": "viewItem == Server"
|
||||
},
|
||||
{
|
||||
"command": "extension.objectExplorerNewQuery",
|
||||
"when": "viewItem == Database"
|
||||
},
|
||||
{
|
||||
"command": "extension.objectExplorerNewQuery",
|
||||
"when": "viewItem == Table"
|
||||
"when": "viewItem =~ /^(disconnectedServer|Server|Database|Table)$/",
|
||||
"group": "MS_SQL@1"
|
||||
},
|
||||
{
|
||||
"command": "extension.removeObjectExplorerNode",
|
||||
"when": "viewItem == Server"
|
||||
"when": "viewItem =~ /^(disconnectedServer|Server)$/",
|
||||
"group": "MS_SQL@3"
|
||||
},
|
||||
{
|
||||
"command": "extension.refreshObjectExplorerNode",
|
||||
"when": "view == objectExplorer"
|
||||
"when": "view == objectExplorer",
|
||||
"group": "MS_SQL@2"
|
||||
},
|
||||
{
|
||||
"command": "extension.disconnectObjectExplorerNode",
|
||||
"when": "viewItem == Server",
|
||||
"group": "MS_SQL@4"
|
||||
},
|
||||
{
|
||||
"command": "extension.scriptSelect",
|
||||
|
@ -343,6 +343,11 @@
|
|||
"title": "%extension.refreshObjectExplorerNode%",
|
||||
"category": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "extension.disconnectObjectExplorerNode",
|
||||
"title": "%extension.disconnect%",
|
||||
"category": "MS SQL"
|
||||
},
|
||||
{
|
||||
"command": "extension.scriptSelect",
|
||||
"title": "%extension.scriptSelect%",
|
||||
|
|
|
@ -13,6 +13,8 @@ export const connectionApplicationName = 'vscode-mssql';
|
|||
export const outputChannelName = 'MSSQL';
|
||||
export const connectionConfigFilename = 'settings.json';
|
||||
export const connectionsArrayName = 'connections';
|
||||
export const disconnectedServerLabel = 'disconnectedServer';
|
||||
export const serverLabel = 'Server';
|
||||
export const cmdRunQuery = 'extension.runQuery';
|
||||
export const cmdRunCurrentStatement = 'extension.runCurrentStatement';
|
||||
export const cmdCancelQuery = 'extension.cancelQuery';
|
||||
|
@ -29,6 +31,9 @@ export const cmdAddObjectExplorer = 'extension.addObjectExplorer';
|
|||
export const cmdObjectExplorerNewQuery = 'extension.objectExplorerNewQuery';
|
||||
export const cmdRemoveObjectExplorerNode = 'extension.removeObjectExplorerNode';
|
||||
export const cmdRefreshObjectExplorerNode = 'extension.refreshObjectExplorerNode';
|
||||
export const cmdDisconnectObjectExplorerNode = 'extension.disconnectObjectExplorerNode';
|
||||
export const cmdObjectExplorerNodeSignIn = 'extension.objectExplorerNodeSignIn';
|
||||
export const cmdConnectObjectExplorerNode = 'extension.connectObjectExplorerNode';
|
||||
export const cmdOpenObjectExplorerCommand = 'workbench.view.extension.objectExplorer';
|
||||
export const cmdScriptSelect = 'extension.scriptSelect';
|
||||
export const cmdToggleSqlCmd = 'extension.toggleSqlCmd';
|
||||
|
@ -113,4 +118,3 @@ export const serviceNotCompatibleError = 'Client is not compatible with the serv
|
|||
export const sqlToolsServiceConfigKey = 'service';
|
||||
export const v1SqlToolsServiceConfigKey = 'v1Service';
|
||||
export const scriptSelectText = 'SELECT TOP (1000) * FROM ';
|
||||
export const useDatabaseText = 'USE ';
|
||||
|
|
|
@ -85,7 +85,8 @@ export class ConnectionInfo {
|
|||
export default class ConnectionManager {
|
||||
private _statusView: StatusView;
|
||||
private _connections: { [fileUri: string]: ConnectionInfo };
|
||||
private _objectExplorerSessions: { [sessionId: string]: ConnectionInfo };
|
||||
private _connectionCredentialsToServerInfoMap:
|
||||
Map<Interfaces.IConnectionCredentials, ConnectionContracts.ServerInfo>;
|
||||
|
||||
constructor(context: vscode.ExtensionContext,
|
||||
statusView: StatusView,
|
||||
|
@ -96,7 +97,8 @@ export default class ConnectionManager {
|
|||
private _connectionUI?: ConnectionUI) {
|
||||
this._statusView = statusView;
|
||||
this._connections = {};
|
||||
this._objectExplorerSessions = {};
|
||||
this._connectionCredentialsToServerInfoMap =
|
||||
new Map<Interfaces.IConnectionCredentials, ConnectionContracts.ServerInfo>();
|
||||
|
||||
if (!this.client) {
|
||||
this.client = SqlToolsServerClient.instance;
|
||||
|
@ -267,6 +269,7 @@ export default class ConnectionManager {
|
|||
let connection = self.getConnectionInfo(fileUri);
|
||||
connection.serviceTimer.end();
|
||||
connection.connecting = false;
|
||||
this._connectionCredentialsToServerInfoMap.set(connection.credentials, result.serverInfo);
|
||||
|
||||
let mruConnection: Interfaces.IConnectionCredentials = <any>{};
|
||||
|
||||
|
@ -388,7 +391,7 @@ export default class ConnectionManager {
|
|||
return this.connectionStore.clearRecentlyUsed();
|
||||
}
|
||||
|
||||
// choose database to use on current server
|
||||
// choose database to use on current server from UI
|
||||
public onChooseDatabase(): Promise<boolean> {
|
||||
const self = this;
|
||||
const fileUri = this.vscodeWrapper.activeTextEditorUri;
|
||||
|
@ -438,6 +441,27 @@ export default class ConnectionManager {
|
|||
});
|
||||
}
|
||||
|
||||
public async changeDatabase(newDatabaseCredentials: Interfaces.IConnectionCredentials): Promise<boolean> {
|
||||
const self = this;
|
||||
const fileUri = this.vscodeWrapper.activeTextEditorUri;
|
||||
return new Promise<boolean>(async (resolve, reject) => {
|
||||
if (!self.isConnected(fileUri)) {
|
||||
self.vscodeWrapper.showWarningMessage(LocalizedConstants.msgChooseDatabaseNotConnected);
|
||||
resolve(false);
|
||||
return;
|
||||
}
|
||||
await self.disconnect(fileUri);
|
||||
await self.connect(fileUri, newDatabaseCredentials);
|
||||
Telemetry.sendTelemetryEvent('UseDatabase');
|
||||
self.vscodeWrapper.logToOutputChannel(
|
||||
Utils.formatString(
|
||||
LocalizedConstants.msgChangedDatabase,
|
||||
newDatabaseCredentials.database,
|
||||
newDatabaseCredentials.server, fileUri));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public onChooseLanguageFlavor(isSqlCmd: boolean = false): Promise<boolean> {
|
||||
const fileUri = this._vscodeWrapper.activeTextEditorUri;
|
||||
if (fileUri && this._vscodeWrapper.isEditingSqlFile) {
|
||||
|
@ -523,7 +547,7 @@ export default class ConnectionManager {
|
|||
self.connect(fileUri, connectionCreds)
|
||||
.then(result => {
|
||||
self.handleConnectionResult(result, fileUri, connectionCreds).then(() => {
|
||||
resolve(true);
|
||||
resolve(connectionCreds);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -533,6 +557,16 @@ export default class ConnectionManager {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server info for a connection
|
||||
* @param connectionCreds
|
||||
*/
|
||||
public getServerInfo(connectionCredentials: Interfaces.IConnectionCredentials): ConnectionContracts.ServerInfo {
|
||||
if (this._connectionCredentialsToServerInfoMap.has(connectionCredentials)) {
|
||||
return this._connectionCredentialsToServerInfoMap.get(connectionCredentials);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the connection result. If connection failed because of invalid credentials,
|
||||
* tries to connect again by asking user for different credentials
|
||||
|
@ -566,22 +600,22 @@ export default class ConnectionManager {
|
|||
|
||||
|
||||
// let users pick from a picklist of connections
|
||||
public onNewConnection(objectExplorerSessionId?: string): Promise<boolean> {
|
||||
public onNewConnection(objectExplorerSessionId?: string): Promise<Interfaces.IConnectionCredentials> {
|
||||
const self = this;
|
||||
const fileUri = objectExplorerSessionId ? objectExplorerSessionId : this.vscodeWrapper.activeTextEditorUri;
|
||||
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
return new Promise<Interfaces.IConnectionCredentials>((resolve, reject) => {
|
||||
if (!fileUri) {
|
||||
// A text document needs to be open before we can connect
|
||||
self.vscodeWrapper.showWarningMessage(LocalizedConstants.msgOpenSqlFile);
|
||||
resolve(false);
|
||||
resolve(undefined);
|
||||
return;
|
||||
} else if (!self.vscodeWrapper.isEditingSqlFile) {
|
||||
self.connectionUI.promptToChangeLanguageMode().then( result => {
|
||||
self.connectionUI.promptToChangeLanguageMode().then(result => {
|
||||
if (result) {
|
||||
self.showConnectionsAndConnect(resolve, reject, fileUri);
|
||||
} else {
|
||||
resolve(false);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import * as os from 'os';
|
||||
import * as events from 'events';
|
||||
import vscode = require('vscode');
|
||||
import Constants = require('../constants/constants');
|
||||
|
@ -24,8 +23,12 @@ import { ISelectionData, IConnectionProfile } from './../models/interfaces';
|
|||
import * as path from 'path';
|
||||
import fs = require('fs');
|
||||
import { ObjectExplorerProvider } from '../objectExplorer/objectExplorerProvider';
|
||||
import { ScriptingService } from '../scripting/scriptingService';
|
||||
import { escapeCharacters } from '../utils/escapeCharacters';
|
||||
import { TreeNodeInfo } from '../objectExplorer/treeNodeInfo';
|
||||
import { AccountSignInTreeNode } from '../objectExplorer/accountSignInTreeNode';
|
||||
import { Deferred } from '../protocol';
|
||||
import { ConnectTreeNode } from '../objectExplorer/connectTreeNode';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
|
@ -45,6 +48,7 @@ export default class MainController implements vscode.Disposable {
|
|||
private _lastOpenedTimer: Utils.Timer;
|
||||
private _untitledSqlDocumentService: UntitledSqlDocumentService;
|
||||
private _objectExplorerProvider: ObjectExplorerProvider;
|
||||
private _scriptingService: ScriptingService;
|
||||
|
||||
/**
|
||||
* The main controller constructor
|
||||
|
@ -144,38 +148,82 @@ export default class MainController implements vscode.Disposable {
|
|||
if (!self._objectExplorerProvider.objectExplorerExists) {
|
||||
self._objectExplorerProvider.objectExplorerExists = true;
|
||||
}
|
||||
return self._objectExplorerProvider.createSession();
|
||||
let promise = new Deferred<TreeNodeInfo>();
|
||||
await self._objectExplorerProvider.createSession(promise);
|
||||
return promise.then(() => {
|
||||
this._objectExplorerProvider.refresh(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
this._context.subscriptions.push(vscode.commands.registerCommand(Constants.cmdObjectExplorerNewQuery, async (treeNodeInfo) => {
|
||||
const databaseName = `${escapeCharacters(self.getDatabaseName(treeNodeInfo))}`;
|
||||
const useStatement = Constants.useDatabaseText + databaseName + os.EOL;
|
||||
self.runAndLogErrors(self.onNewQuery(treeNodeInfo.sessionId, useStatement), 'onNewQueryOE');
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNewQuery, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
const connectionCredentials = treeNodeInfo.connectionCredentials;
|
||||
if (connectionCredentials) {
|
||||
const databaseName = `${escapeCharacters(self.getDatabaseName(treeNodeInfo))}`;
|
||||
if (databaseName !== connectionCredentials.database) {
|
||||
connectionCredentials.database = databaseName;
|
||||
}
|
||||
}
|
||||
await self.onNewQuery(treeNodeInfo);
|
||||
await this._connectionMgr.changeDatabase(connectionCredentials);
|
||||
}));
|
||||
|
||||
this.registerCommand(Constants.cmdRemoveObjectExplorerNode);
|
||||
this._event.on(Constants.cmdRemoveObjectExplorerNode, async () => {
|
||||
let connProfile = <IConnectionProfile>this._objectExplorerProvider.getConnectionCredentials(
|
||||
this._objectExplorerProvider.currentNode.sessionId);
|
||||
await this._connectionMgr.connectionStore.removeProfile(connProfile, false);
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(this._objectExplorerProvider.currentNode);
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdRemoveObjectExplorerNode, async (treeNodeInfo: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(treeNodeInfo);
|
||||
let profile = <IConnectionProfile>treeNodeInfo.connectionCredentials;
|
||||
await this._connectionMgr.connectionStore.removeProfile(profile, false);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
});
|
||||
}));
|
||||
|
||||
this.registerCommand(Constants.cmdRefreshObjectExplorerNode);
|
||||
this._event.on(Constants.cmdRefreshObjectExplorerNode, () => {
|
||||
return this._objectExplorerProvider.refreshNode(this._objectExplorerProvider.currentNode);
|
||||
});
|
||||
this._context.subscriptions.push(vscode.commands.registerCommand(Constants.cmdScriptSelect, async (treeNodeInfo) => {
|
||||
const objectNames = treeNodeInfo.label.split('.');
|
||||
const tableName = `[${escapeCharacters(objectNames[0])}].[${escapeCharacters(objectNames[1])}]`;
|
||||
const databaseName = `[${escapeCharacters(self.getDatabaseName(treeNodeInfo))}].`;
|
||||
const selectStatement = Constants.scriptSelectText + databaseName + tableName;
|
||||
self.onNewQuery(treeNodeInfo.sessionId, selectStatement).then((result) => {
|
||||
if (result) {
|
||||
self.onRunQuery();
|
||||
}
|
||||
});
|
||||
|
||||
// initiate the scripting service
|
||||
this._scriptingService = new ScriptingService(this._connectionMgr, this._vscodeWrapper);
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdScriptSelect, async (node: TreeNodeInfo) => {
|
||||
const uri = await this._untitledSqlDocumentService.newQuery();
|
||||
if (!this.connectionManager.isConnected(uri.toString())) {
|
||||
const connectionCreds = node.connectionCredentials;
|
||||
const databaseName = `${escapeCharacters(self.getDatabaseName(node))}`;
|
||||
node.connectionCredentials.database = databaseName;
|
||||
this._statusview.languageFlavorChanged(uri.toString(), Constants.mssqlProviderName);
|
||||
this._statusview.sqlCmdModeChanged(uri.toString(), false);
|
||||
await this.connectionManager.connect(uri.toString(), connectionCreds);
|
||||
const selectStatement = await this._scriptingService.scriptSelect(node, uri.toString());
|
||||
let editor = this._vscodeWrapper.activeTextEditor;
|
||||
editor.edit(editBuilder => {
|
||||
editBuilder.replace(editor.selection, selectStatement);
|
||||
}).then(() => this.onRunQuery());
|
||||
}
|
||||
}));
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdObjectExplorerNodeSignIn, async (node: AccountSignInTreeNode) => {
|
||||
this._objectExplorerProvider.signInNodeServer(node.parentNode);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}));
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdConnectObjectExplorerNode, async (node: ConnectTreeNode) => {
|
||||
let promise = new Deferred<TreeNodeInfo>();
|
||||
await self._objectExplorerProvider.createSession(promise, node.parentNode.connectionCredentials);
|
||||
return promise.then(() => {
|
||||
this._objectExplorerProvider.refresh(undefined);
|
||||
});
|
||||
}));
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
Constants.cmdDisconnectObjectExplorerNode, async (node: TreeNodeInfo) => {
|
||||
await this._objectExplorerProvider.removeObjectExplorerNode(node, true);
|
||||
}));
|
||||
|
||||
// Add handlers for VS Code generated commands
|
||||
this._vscodeWrapper.onDidCloseTextDocument(params => this.onDidCloseTextDocument(params));
|
||||
this._vscodeWrapper.onDidOpenTextDocument(params => this.onDidOpenTextDocument(params));
|
||||
|
@ -359,7 +407,13 @@ export default class MainController implements vscode.Disposable {
|
|||
*/
|
||||
public onNewConnection(): Promise<boolean> {
|
||||
if (this.canRunCommand() && this.validateTextDocumentHasFocus()) {
|
||||
return this._connectionMgr.onNewConnection();
|
||||
this._connectionMgr.onNewConnection().then((result) => {
|
||||
if (result) {
|
||||
this._objectExplorerProvider.objectExplorerExists = false;
|
||||
this._objectExplorerProvider.refresh(undefined);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
@ -610,20 +664,30 @@ export default class MainController implements vscode.Disposable {
|
|||
/**
|
||||
* Opens a new query and creates new connection
|
||||
*/
|
||||
public async onNewQuery(sessionId?: string, content?: string): Promise<boolean> {
|
||||
public async onNewQuery(node?: TreeNodeInfo, content?: string): Promise<boolean> {
|
||||
if (this.canRunCommand()) {
|
||||
if (sessionId) {
|
||||
// from the object explorer context menu
|
||||
if (node) {
|
||||
const uri = await this._untitledSqlDocumentService.newQuery(content);
|
||||
// connect to the node if the command came from the context
|
||||
if (!this.connectionManager.isConnected(sessionId)) {
|
||||
const connectionCreds = this._objectExplorerProvider.getConnectionCredentials(sessionId);
|
||||
if (!this.connectionManager.isConnected(uri.toString())) {
|
||||
const connectionCreds = node.connectionCredentials;
|
||||
this._statusview.languageFlavorChanged(uri.toString(), Constants.mssqlProviderName);
|
||||
return this.connectionManager.connect(uri.toString(), connectionCreds);
|
||||
this._statusview.sqlCmdModeChanged(uri.toString(), false);
|
||||
let result = await this.connectionManager.connect(uri.toString(), connectionCreds);
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
} else {
|
||||
let uri = await this._untitledSqlDocumentService.newQuery();
|
||||
this._statusview.sqlCmdModeChanged(uri.toString(), false);
|
||||
return this._connectionMgr.onNewConnection();
|
||||
// new query command
|
||||
const uri = await this._untitledSqlDocumentService.newQuery();
|
||||
this._connectionMgr.onNewConnection().then(async (result) => {
|
||||
// initiate a new OE with same connection
|
||||
if (result) {
|
||||
this._objectExplorerProvider.refresh(undefined);
|
||||
}
|
||||
this._statusview.sqlCmdModeChanged(uri.toString(), false);
|
||||
return Promise.resolve(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve(false);
|
||||
|
|
|
@ -257,7 +257,7 @@ export class ConnectionCredentials implements IConnectionCredentials {
|
|||
|
||||
// Prompt for password if this is a password based credential and the password for the profile was empty
|
||||
// and not explicitly set as empty. If it was explicitly set as empty, only prompt if pw not saved
|
||||
private static shouldPromptForPassword(credentials: IConnectionCredentials): boolean {
|
||||
public static shouldPromptForPassword(credentials: IConnectionCredentials): boolean {
|
||||
let isSavedEmptyPassword: boolean = (<IConnectionProfile>credentials).emptyPasswordInput
|
||||
&& (<IConnectionProfile>credentials).savePassword;
|
||||
|
||||
|
|
|
@ -153,6 +153,24 @@ export class ConnectionStore {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup credential store
|
||||
* @param connectionCredentials Connection credentials of profile for password lookup
|
||||
*/
|
||||
public async lookupPassword(connectionCredentials: IConnectionCredentials): Promise<string> {
|
||||
const databaseName = connectionCredentials.database === '' ? 'master' :
|
||||
connectionCredentials.database;
|
||||
const credentialId = ConnectionStore.formatCredentialId(
|
||||
connectionCredentials.server, databaseName,
|
||||
connectionCredentials.user, ConnectionStore.CRED_MRU_USER);
|
||||
const savedCredential = await this._credentialStore.readCredential(credentialId);
|
||||
if (savedCredential && savedCredential.password) {
|
||||
return savedCredential.password;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* public for testing purposes. Validates whether a password should be looked up from the credential store or not
|
||||
*
|
||||
|
|
|
@ -253,4 +253,4 @@ export class ListDatabasesResult {
|
|||
public databaseNames: Array<string>;
|
||||
}
|
||||
|
||||
// ------------------------------- </ List Databases Request > --------------------------------------
|
||||
// ------------------------------- </ List Databases Request > --------------------------------------
|
|
@ -0,0 +1,303 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import { RequestType } from 'vscode-jsonrpc';
|
||||
|
||||
export interface ConnectionInfo {
|
||||
options: { [name: string]: any };
|
||||
}
|
||||
|
||||
export enum ScriptOperation {
|
||||
Select = 0,
|
||||
Create = 1,
|
||||
Insert = 2,
|
||||
Update = 3,
|
||||
Delete = 4,
|
||||
Execute = 5,
|
||||
Alter = 6
|
||||
}
|
||||
|
||||
export interface ScriptOptions {
|
||||
/**
|
||||
* Generate ANSI padding statements
|
||||
*/
|
||||
scriptANSIPadding?: boolean;
|
||||
|
||||
/**
|
||||
* Append the generated script to a file
|
||||
*/
|
||||
appendToFile?: boolean;
|
||||
|
||||
/**
|
||||
* Continue to script if an error occurs. Otherwise, stop.
|
||||
*/
|
||||
continueScriptingOnError?: boolean;
|
||||
|
||||
/**
|
||||
* Convert user-defined data types to base types.
|
||||
*/
|
||||
convertUDDTToBaseType?: boolean;
|
||||
|
||||
/**
|
||||
* Generate script for dependent objects for each object scripted.
|
||||
*/
|
||||
generateScriptForDependentObjects?: boolean;
|
||||
|
||||
/**
|
||||
* Include descriptive headers for each object generated.
|
||||
*/
|
||||
includeDescriptiveHeaders?: boolean;
|
||||
|
||||
/**
|
||||
* Check that an object with the given name exists before dropping or altering or that an object with the given name does not exist before creating.
|
||||
*/
|
||||
includeIfNotExists?: boolean;
|
||||
|
||||
/**
|
||||
* Script options to set vardecimal storage format.
|
||||
*/
|
||||
includeVarDecimal?: boolean;
|
||||
|
||||
/**
|
||||
* Include system generated constraint names to enforce declarative referential integrity.
|
||||
*/
|
||||
scriptDRIIncludeSystemNames?: boolean;
|
||||
|
||||
/**
|
||||
* Include statements in the script that are not supported on the specified SQL Server database engine type.
|
||||
*/
|
||||
includeUnsupportedStatements?: boolean;
|
||||
|
||||
/**
|
||||
* Prefix object names with the object schema.
|
||||
*/
|
||||
schemaQualify?: boolean;
|
||||
|
||||
/**
|
||||
* Script options to set bindings option.
|
||||
*/
|
||||
bindings?: boolean;
|
||||
|
||||
/**
|
||||
* Script the objects that use collation.
|
||||
*/
|
||||
collation?: boolean;
|
||||
|
||||
/**
|
||||
* Script the default values.
|
||||
*/
|
||||
default?: boolean;
|
||||
|
||||
/**
|
||||
* Script Object CREATE/DROP statements.
|
||||
*/
|
||||
scriptCreateDrop: string;
|
||||
|
||||
/**
|
||||
* Script the Extended Properties for each object scripted.
|
||||
*/
|
||||
scriptExtendedProperties?: boolean;
|
||||
|
||||
/**
|
||||
* Script only features compatible with the specified version of SQL Server.
|
||||
*/
|
||||
scriptCompatibilityOption: string;
|
||||
|
||||
/**
|
||||
* Script only features compatible with the specified SQL Server database engine type.
|
||||
*/
|
||||
targetDatabaseEngineType: string;
|
||||
|
||||
/**
|
||||
* Script only features compatible with the specified SQL Server database engine edition.
|
||||
*/
|
||||
targetDatabaseEngineEdition: string;
|
||||
|
||||
/**
|
||||
* Script all logins available on the server. Passwords will not be scripted.
|
||||
*/
|
||||
scriptLogins?: boolean;
|
||||
|
||||
/**
|
||||
* Generate object-level permissions.
|
||||
*/
|
||||
scriptObjectLevelPermissions?: boolean;
|
||||
|
||||
/**
|
||||
* Script owner for the objects.
|
||||
*/
|
||||
scriptOwner?: boolean;
|
||||
|
||||
/**
|
||||
* Script statistics, and optionally include histograms, for each selected table or view.
|
||||
*/
|
||||
scriptStatistics: string;
|
||||
|
||||
/**
|
||||
* Generate USE DATABASE statement.
|
||||
*/
|
||||
scripUseDatabase?: boolean;
|
||||
|
||||
/**
|
||||
* Generate script that contains schema only or schema and azdata.
|
||||
*/
|
||||
typeOfDataToScript: string;
|
||||
|
||||
/**
|
||||
* Scripts the change tracking information.
|
||||
*/
|
||||
scriptChangeTracking?: boolean;
|
||||
|
||||
/**
|
||||
* Script the check constraints for each table or view scripted.
|
||||
*/
|
||||
scriptCheckConstraints?: boolean;
|
||||
|
||||
/**
|
||||
* Scripts the data compression information.
|
||||
*/
|
||||
scriptDataCompressionOptions?: boolean;
|
||||
|
||||
/**
|
||||
* Script the foreign keys for each table scripted.
|
||||
*/
|
||||
scriptForeignKeys?: boolean;
|
||||
|
||||
/**
|
||||
* Script the full-text indexes for each table or indexed view scripted.
|
||||
*/
|
||||
scriptFullTextIndexes?: boolean;
|
||||
|
||||
/**
|
||||
* Script the indexes (including XML and clustered indexes) for each table or indexed view scripted.
|
||||
*/
|
||||
scriptIndexes?: boolean;
|
||||
|
||||
/**
|
||||
* Script the primary keys for each table or view scripted
|
||||
*/
|
||||
scriptPrimaryKeys?: boolean;
|
||||
|
||||
/**
|
||||
* Script the triggers for each table or view scripted
|
||||
*/
|
||||
scriptTriggers?: boolean;
|
||||
|
||||
/**
|
||||
* Script the unique keys for each table or view scripted.
|
||||
*/
|
||||
uniqueKeys?: boolean;
|
||||
}
|
||||
|
||||
export interface ScriptingObject {
|
||||
/**
|
||||
* The database object type
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* The schema of the database object
|
||||
*/
|
||||
schema: string;
|
||||
|
||||
/**
|
||||
* The database object name
|
||||
*/
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ScriptingParams {
|
||||
/**
|
||||
* File path used when writing out the script.
|
||||
*/
|
||||
filePath: string;
|
||||
|
||||
/**
|
||||
* Whether scripting to a single file or file per object.
|
||||
*/
|
||||
scriptDestination: string;
|
||||
|
||||
/**
|
||||
* Connection string of the target database the scripting operation will run against.
|
||||
*/
|
||||
connectionString: string;
|
||||
|
||||
/**
|
||||
* A list of scripting objects to script
|
||||
*/
|
||||
scriptingObjects: ScriptingObject[];
|
||||
|
||||
/**
|
||||
* A list of scripting object which specify the include criteria of objects to script.
|
||||
*/
|
||||
includeObjectCriteria: ScriptingObject[];
|
||||
|
||||
/**
|
||||
* A list of scripting object which specify the exclude criteria of objects to not script.
|
||||
*/
|
||||
excludeObjectCriteria: ScriptingObject[];
|
||||
|
||||
/**
|
||||
* A list of schema name of objects to script.
|
||||
*/
|
||||
includeSchemas: string[];
|
||||
|
||||
/**
|
||||
* A list of schema name of objects to not script.
|
||||
*/
|
||||
excludeSchemas: string[];
|
||||
|
||||
/**
|
||||
* A list of type name of objects to script.
|
||||
*/
|
||||
includeTypes: string[];
|
||||
|
||||
/**
|
||||
* A list of type name of objects to not script.
|
||||
*/
|
||||
excludeTypes: string[];
|
||||
|
||||
/**
|
||||
* Scripting options for the ScriptingParams
|
||||
*/
|
||||
scriptOptions: ScriptOptions;
|
||||
|
||||
/**
|
||||
* Connection details for the ScriptingParams
|
||||
*/
|
||||
connectionDetails: ConnectionInfo;
|
||||
|
||||
/**
|
||||
* Owner URI of the connection
|
||||
*/
|
||||
ownerURI: string;
|
||||
|
||||
/**
|
||||
* Whether the scripting operation is for
|
||||
* select script statements
|
||||
*/
|
||||
selectScript: boolean;
|
||||
|
||||
/**
|
||||
* Operation associated with the script request
|
||||
*/
|
||||
operation: ScriptOperation;
|
||||
}
|
||||
|
||||
export interface ScriptingResult {
|
||||
operationId: string;
|
||||
script: string;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------- < Scripting Request > ----------------------------------------------
|
||||
|
||||
export namespace ScriptingRequest {
|
||||
/**
|
||||
* Returns children of a given node as a NodeInfo array.
|
||||
*/
|
||||
export const type = new RequestType<ScriptingParams, ScriptingResult, void, void>('scripting/script');
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as LocalizedConstants from '../constants/localizedConstants';
|
||||
import Constants = require('../constants/constants');
|
||||
import { TreeNodeInfo } from './treeNodeInfo';
|
||||
|
||||
export class AccountSignInTreeNode extends vscode.TreeItem {
|
||||
|
||||
|
||||
constructor(
|
||||
private _parentNode: TreeNodeInfo,
|
||||
) {
|
||||
super(LocalizedConstants.msgSignIn, vscode.TreeItemCollapsibleState.None);
|
||||
|
||||
this.command = {
|
||||
title: LocalizedConstants.msgSignIn,
|
||||
command: Constants.cmdObjectExplorerNodeSignIn,
|
||||
arguments: [this]
|
||||
};
|
||||
}
|
||||
|
||||
public get parentNode(): TreeNodeInfo {
|
||||
return this._parentNode;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import Constants = require('../constants/constants');
|
||||
import * as LocalizedConstants from '../constants/localizedConstants';
|
||||
import { ObjectExplorerUtils } from './objectExplorerUtils';
|
||||
|
||||
export class AddConnectionTreeNode extends vscode.TreeItem {
|
||||
|
||||
constructor() {
|
||||
super(LocalizedConstants.msgAddConnection, vscode.TreeItemCollapsibleState.None);
|
||||
this.command = {
|
||||
title: LocalizedConstants.msgAddConnection,
|
||||
command: Constants.cmdAddObjectExplorer
|
||||
};
|
||||
this.iconPath = {
|
||||
light: path.join(ObjectExplorerUtils.rootPath, 'add.svg'),
|
||||
dark: path.join(ObjectExplorerUtils.rootPath, 'add_inverse.svg')
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as LocalizedConstants from '../constants/localizedConstants';
|
||||
import Constants = require('../constants/constants');
|
||||
import { TreeNodeInfo } from './treeNodeInfo';
|
||||
|
||||
export class ConnectTreeNode extends vscode.TreeItem {
|
||||
|
||||
constructor(
|
||||
private _parentNode: TreeNodeInfo,
|
||||
) {
|
||||
super(LocalizedConstants.msgConnect, vscode.TreeItemCollapsibleState.None);
|
||||
|
||||
this.command = {
|
||||
title: LocalizedConstants.msgConnect,
|
||||
command: Constants.cmdConnectObjectExplorerNode,
|
||||
arguments: [this]
|
||||
};
|
||||
}
|
||||
|
||||
public get parentNode(): TreeNodeInfo {
|
||||
return this._parentNode;
|
||||
}
|
||||
}
|
|
@ -8,6 +8,8 @@ import ConnectionManager from '../controllers/connectionManager';
|
|||
import { ObjectExplorerService } from './objectExplorerService';
|
||||
import { ConnectionCredentials } from '../models/connectionCredentials';
|
||||
import { TreeNodeInfo } from './treeNodeInfo';
|
||||
import { IConnectionCredentials } from '../models/interfaces';
|
||||
import { Deferred } from '../protocol';
|
||||
|
||||
export class ObjectExplorerProvider implements vscode.TreeDataProvider<any> {
|
||||
|
||||
|
@ -29,29 +31,36 @@ export class ObjectExplorerProvider implements vscode.TreeDataProvider<any> {
|
|||
return node;
|
||||
}
|
||||
|
||||
getChildren(element?: TreeNodeInfo): Promise<vscode.TreeItem[]> {
|
||||
const children = this._objectExplorerService.getChildren(element);
|
||||
async getChildren(element?: TreeNodeInfo): Promise<vscode.TreeItem[]> {
|
||||
const children = await this._objectExplorerService.getChildren(element);
|
||||
if (children) {
|
||||
return Promise.resolve(children);
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
async createSession(): Promise<string> {
|
||||
return await this._objectExplorerService.createSession();
|
||||
async createSession(promise: Deferred<TreeNodeInfo>, connectionCredentials?: IConnectionCredentials): Promise<void> {
|
||||
return this._objectExplorerService.createSession(promise, connectionCredentials);
|
||||
}
|
||||
|
||||
public getConnectionCredentials(sessionId: string): ConnectionCredentials {
|
||||
return this._objectExplorerService.getConnectionCredentials(sessionId);
|
||||
if (sessionId) {
|
||||
return this._objectExplorerService.getConnectionCredentials(sessionId);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public removeObjectExplorerNode(node: TreeNodeInfo): Promise<void> {
|
||||
return this._objectExplorerService.removeObjectExplorerNode(node);
|
||||
public removeObjectExplorerNode(node: TreeNodeInfo, isDisconnect: boolean = false): Promise<void> {
|
||||
return this._objectExplorerService.removeObjectExplorerNode(node, isDisconnect);
|
||||
}
|
||||
|
||||
public refreshNode(node: TreeNodeInfo): Promise<boolean> {
|
||||
public refreshNode(node: TreeNodeInfo): Promise<void> {
|
||||
return this._objectExplorerService.refreshNode(node);
|
||||
}
|
||||
|
||||
public signInNodeServer(node: TreeNodeInfo): void {
|
||||
return this._objectExplorerService.signInNodeServer(node);
|
||||
}
|
||||
|
||||
/** Getters */
|
||||
public get currentNode(): TreeNodeInfo {
|
||||
return this._objectExplorerService.currentNode;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as vscode from 'vscode';
|
||||
import SqlToolsServiceClient from '../languageservice/serviceclient';
|
||||
import ConnectionManager from '../controllers/connectionManager';
|
||||
import { CreateSessionCompleteNotification, SessionCreatedParameters, CreateSessionRequest } from '../models/contracts/objectExplorer/createSessionRequest';
|
||||
|
@ -13,25 +14,40 @@ import { TreeItemCollapsibleState } from 'vscode';
|
|||
import { RefreshRequest, RefreshParams } from '../models/contracts/objectExplorer/refreshSessionRequest';
|
||||
import { CloseSessionRequest, CloseSessionParams } from '../models/contracts/objectExplorer/closeSessionRequest';
|
||||
import { TreeNodeInfo } from './treeNodeInfo';
|
||||
import { ConnectionProfile } from '../models/connectionProfile';
|
||||
import { IConnectionCredentials } from '../models/interfaces';
|
||||
import LocalizedConstants = require('../constants/localizedConstants');
|
||||
import { AddConnectionTreeNode } from './addConnectionTreeNode';
|
||||
import { AccountSignInTreeNode } from './accountSignInTreeNode';
|
||||
import { ConnectTreeNode } from './connectTreeNode';
|
||||
import { Deferred } from '../protocol';
|
||||
import Constants = require('../constants/constants');
|
||||
import { ObjectExplorerUtils } from './objectExplorerUtils';
|
||||
import { ConnectionStore } from '../models/connectionStore';
|
||||
|
||||
export class ObjectExplorerService {
|
||||
|
||||
private _client: SqlToolsServiceClient;
|
||||
private _currentNode: TreeNodeInfo;
|
||||
private _treeNodeToChildrenMap: Map<TreeNodeInfo, TreeNodeInfo[]>;
|
||||
private _treeNodeToChildrenMap: Map<TreeNodeInfo, vscode.TreeItem[]>;
|
||||
private _nodePathToNodeLabelMap: Map<string, string>;
|
||||
private _rootTreeNodeArray: Array<TreeNodeInfo>;
|
||||
private _sessionIdToConnectionCredentialsMap: Map<string, ConnectionCredentials>;
|
||||
|
||||
// Deferred promise maps
|
||||
private _sessionIdToPromiseMap: Map<string, Deferred<vscode.TreeItem>>;
|
||||
private _expandParamsToPromiseMap: Map<ExpandParams, Deferred<TreeNodeInfo[]>>;
|
||||
|
||||
constructor(private _connectionManager: ConnectionManager,
|
||||
private _objectExplorerProvider: ObjectExplorerProvider) {
|
||||
this._connectionManager = _connectionManager;
|
||||
this._client = this._connectionManager.client;
|
||||
this._treeNodeToChildrenMap = new Map<TreeNodeInfo, TreeNodeInfo[]>();
|
||||
this._treeNodeToChildrenMap = new Map<TreeNodeInfo, vscode.TreeItem[]>();
|
||||
this._rootTreeNodeArray = new Array<TreeNodeInfo>();
|
||||
this._sessionIdToConnectionCredentialsMap = new Map<string, ConnectionCredentials>();
|
||||
this._nodePathToNodeLabelMap = new Map<string, string>();
|
||||
this._sessionIdToPromiseMap = new Map<string, Deferred<vscode.TreeItem>>();
|
||||
this._expandParamsToPromiseMap = new Map<ExpandParams, Deferred<TreeNodeInfo[]>>();
|
||||
|
||||
this._client.onNotification(CreateSessionCompleteNotification.type,
|
||||
this.handleSessionCreatedNotification());
|
||||
this._client.onNotification(ExpandCompleteNotification.type,
|
||||
|
@ -43,10 +59,46 @@ export class ObjectExplorerService {
|
|||
const handler = (result: SessionCreatedParameters) => {
|
||||
if (result.success) {
|
||||
let nodeLabel = this._nodePathToNodeLabelMap.get(result.rootNode.nodePath);
|
||||
self._currentNode = TreeNodeInfo.fromNodeInfo(result.rootNode, result.sessionId, self.currentNode, nodeLabel);
|
||||
self._rootTreeNodeArray.push(self.currentNode);
|
||||
// if no node label, check if it has a name in saved profiles
|
||||
// in case this call came from new query
|
||||
let savedConnections = this._connectionManager.connectionStore.loadAllConnections();
|
||||
for (let connection of savedConnections) {
|
||||
if (connection.connectionCreds.server === result.rootNode.nodePath) {
|
||||
nodeLabel = connection.label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// set connection and other things
|
||||
if (self._currentNode) {
|
||||
self._currentNode = TreeNodeInfo.fromNodeInfo(result.rootNode, result.sessionId,
|
||||
self._currentNode, self._currentNode.connectionCredentials,
|
||||
nodeLabel ? nodeLabel : result.rootNode.nodePath);
|
||||
} else {
|
||||
const credentials = this._sessionIdToConnectionCredentialsMap.get(result.sessionId);
|
||||
self._currentNode = TreeNodeInfo.fromNodeInfo(result.rootNode, result.sessionId, self._currentNode,
|
||||
credentials, nodeLabel ? nodeLabel : result.rootNode.nodePath);
|
||||
}
|
||||
self.updateNode(self._currentNode);
|
||||
self._objectExplorerProvider.objectExplorerExists = true;
|
||||
return self._objectExplorerProvider.refresh(undefined);
|
||||
const promise = self._sessionIdToPromiseMap.get(result.sessionId);
|
||||
// remove the sign in node once the session is created
|
||||
if (self._treeNodeToChildrenMap.has(self._currentNode)) {
|
||||
self._treeNodeToChildrenMap.delete(self._currentNode);
|
||||
}
|
||||
return promise.resolve(self._currentNode);
|
||||
} else {
|
||||
// failure
|
||||
self.updateNode(self._currentNode);
|
||||
self._currentNode = undefined;
|
||||
let error = LocalizedConstants.connectErrorLabel;
|
||||
if (result.errorMessage) {
|
||||
error += ` : ${result.errorMessage}`;
|
||||
}
|
||||
self._connectionManager.vscodeWrapper.showErrorMessage(error);
|
||||
const promise = self._sessionIdToPromiseMap.get(result.sessionId);
|
||||
if (promise) {
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
};
|
||||
return handler;
|
||||
|
@ -56,70 +108,155 @@ export class ObjectExplorerService {
|
|||
const self = this;
|
||||
const handler = (result: ExpandResponse) => {
|
||||
if (result && result.nodes) {
|
||||
const children = result.nodes.map(node => TreeNodeInfo.fromNodeInfo(node, self.currentNode.sessionId, self.currentNode));
|
||||
self._currentNode.collapsibleState = TreeItemCollapsibleState.Expanded;
|
||||
self._treeNodeToChildrenMap.set(self.currentNode, children);
|
||||
return self._objectExplorerProvider.refresh(self.currentNode);
|
||||
const children = result.nodes.map(node => TreeNodeInfo.fromNodeInfo(node, self._currentNode.sessionId,
|
||||
self._currentNode, self._currentNode.connectionCredentials));
|
||||
self._treeNodeToChildrenMap.set(self._currentNode, children);
|
||||
const expandParams: ExpandParams = {
|
||||
sessionId: result.sessionId,
|
||||
nodePath: result.nodePath
|
||||
}
|
||||
for (let key of self._expandParamsToPromiseMap.keys()) {
|
||||
if (key.sessionId === expandParams.sessionId &&
|
||||
key.nodePath === expandParams.nodePath) {
|
||||
let promise = self._expandParamsToPromiseMap.get(key);
|
||||
return promise.resolve(children);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return handler;
|
||||
}
|
||||
|
||||
private async expandNode(node: TreeNodeInfo, sessionId: string): Promise<boolean> {
|
||||
private async expandNode(node: TreeNodeInfo, sessionId: string, promise: Deferred<TreeNodeInfo[]>): Promise<boolean> {
|
||||
const expandParams: ExpandParams = {
|
||||
sessionId: sessionId,
|
||||
nodePath: node.nodePath
|
||||
};
|
||||
const response = await this._connectionManager.client.sendRequest(ExpandRequest.type, expandParams);
|
||||
if (promise) {
|
||||
this._expandParamsToPromiseMap.set(expandParams, promise);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async getChildren(element?: TreeNodeInfo): Promise<TreeNodeInfo[]> {
|
||||
private updateNode(node: TreeNodeInfo): void {
|
||||
for (let rootTreeNode of this._rootTreeNodeArray) {
|
||||
if (rootTreeNode.connectionCredentials === node.connectionCredentials &&
|
||||
rootTreeNode.label === node.label) {
|
||||
const index = this._rootTreeNodeArray.indexOf(rootTreeNode);
|
||||
this._rootTreeNodeArray[index] = node;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._rootTreeNodeArray.push(node);
|
||||
}
|
||||
|
||||
async getChildren(element?: TreeNodeInfo): Promise<vscode.TreeItem[]> {
|
||||
if (element) {
|
||||
if (element !== this.currentNode) {
|
||||
if (element !== this._currentNode) {
|
||||
this._currentNode = element;
|
||||
}
|
||||
if (this._treeNodeToChildrenMap.get(this._currentNode)) {
|
||||
// get cached children
|
||||
if (this._treeNodeToChildrenMap.has(this._currentNode)) {
|
||||
return this._treeNodeToChildrenMap.get(this._currentNode);
|
||||
} else {
|
||||
// expansion
|
||||
await this.expandNode(element, element.sessionId);
|
||||
return [];
|
||||
// check if session exists
|
||||
if (element.sessionId) {
|
||||
// clean created session promise
|
||||
this._sessionIdToPromiseMap.delete(element.sessionId);
|
||||
|
||||
// node expansion
|
||||
let promise = new Deferred<TreeNodeInfo[]>();
|
||||
this.expandNode(element, element.sessionId, promise);
|
||||
return promise.then((children) => {
|
||||
if (children) {
|
||||
// clean expand session promise
|
||||
for (const key of this._expandParamsToPromiseMap.keys()) {
|
||||
if (key.sessionId === element.sessionId &&
|
||||
key.nodePath === element.nodePath) {
|
||||
this._expandParamsToPromiseMap.delete(key);
|
||||
}
|
||||
}
|
||||
return children;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// start node session
|
||||
let promise = new Deferred<TreeNodeInfo>();
|
||||
this.createSession(promise, element.connectionCredentials);
|
||||
return promise.then((node) => {
|
||||
// If password wasn't given
|
||||
if (!node) {
|
||||
const signInNode = new AccountSignInTreeNode(element);
|
||||
this._treeNodeToChildrenMap.set(element, [signInNode]);
|
||||
return [signInNode];
|
||||
}
|
||||
// otherwise expand the node by refreshing the root
|
||||
// to add connected context key
|
||||
this._objectExplorerProvider.refresh(undefined);
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// retrieve saved connections first when opening object explorer
|
||||
// for the first time
|
||||
if (!this._objectExplorerProvider.objectExplorerExists) {
|
||||
let savedConnections = this._connectionManager.connectionStore.loadAllConnections();
|
||||
savedConnections.forEach(async (conn) => {
|
||||
let connectionCredentials = conn.connectionCreds;
|
||||
let savedConnections = this._connectionManager.connectionStore.loadAllConnections();
|
||||
if ((!this._objectExplorerProvider.objectExplorerExists ||
|
||||
savedConnections.length !== this._rootTreeNodeArray.length) &&
|
||||
savedConnections.length > 0) {
|
||||
this._rootTreeNodeArray = [];
|
||||
savedConnections.forEach((conn) => {
|
||||
this._nodePathToNodeLabelMap.set(conn.connectionCreds.server, conn.label);
|
||||
if (connectionCredentials) {
|
||||
const connectionDetails = ConnectionCredentials.createConnectionDetails(connectionCredentials);
|
||||
const response = await this._connectionManager.client.sendRequest(CreateSessionRequest.type, connectionDetails);
|
||||
this._sessionIdToConnectionCredentialsMap.set(response.sessionId, connectionCredentials);
|
||||
}
|
||||
let node = new TreeNodeInfo(conn.label, Constants.disconnectedServerLabel,
|
||||
TreeItemCollapsibleState.Collapsed,
|
||||
undefined, undefined, Constants.serverLabel,
|
||||
undefined, conn.connectionCreds, undefined);
|
||||
this._rootTreeNodeArray.push(node);
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this._rootTreeNodeArray.length === 0) {
|
||||
this.createSession();
|
||||
return [];
|
||||
} else {
|
||||
this._objectExplorerProvider.objectExplorerExists = true;
|
||||
return this._rootTreeNodeArray;
|
||||
} else {
|
||||
if (this._rootTreeNodeArray.length > 0) {
|
||||
return this._rootTreeNodeArray;
|
||||
} else {
|
||||
return [new AddConnectionTreeNode()];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async createSession(): Promise<string> {
|
||||
const connectionUI = this._connectionManager.connectionUI;
|
||||
const connectionCreds = await connectionUI.showConnections();
|
||||
if (connectionCreds) {
|
||||
this._nodePathToNodeLabelMap.set(connectionCreds.server, (<ConnectionProfile>connectionCreds).profileName);
|
||||
const connectionDetails = ConnectionCredentials.createConnectionDetails(connectionCreds);
|
||||
/**
|
||||
* Create an OE session for the given connection credentials
|
||||
* otherwise prompt the user to select a connection to make an
|
||||
* OE out of
|
||||
* @param connectionCredentials Connection Credentials for a node
|
||||
*/
|
||||
public async createSession(promise: Deferred<vscode.TreeItem>, connectionCredentials?: IConnectionCredentials): Promise<void> {
|
||||
if (!connectionCredentials) {
|
||||
const connectionUI = this._connectionManager.connectionUI;
|
||||
connectionCredentials = await connectionUI.showConnections();
|
||||
}
|
||||
if (connectionCredentials) {
|
||||
// show password prompt if SQL Login and password isn't saved
|
||||
const shouldPromptForPassword = ConnectionCredentials.shouldPromptForPassword(connectionCredentials);
|
||||
if (shouldPromptForPassword) {
|
||||
// look up saved password
|
||||
let password = await this._connectionManager.connectionStore.lookupPassword(connectionCredentials);
|
||||
if (!password) {
|
||||
let password = await this._connectionManager.connectionUI.promptForPassword();
|
||||
if (!password) {
|
||||
return promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
connectionCredentials.password = password;
|
||||
}
|
||||
const connectionDetails = ConnectionCredentials.createConnectionDetails(connectionCredentials);
|
||||
const response = await this._connectionManager.client.sendRequest(CreateSessionRequest.type, connectionDetails);
|
||||
this._sessionIdToConnectionCredentialsMap.set(response.sessionId, connectionCreds);
|
||||
return response.sessionId;
|
||||
if (response) {
|
||||
this._sessionIdToConnectionCredentialsMap.set(response.sessionId, connectionCredentials);
|
||||
this._sessionIdToPromiseMap.set(response.sessionId, promise);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,44 +267,71 @@ export class ObjectExplorerService {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
public async removeObjectExplorerNode(node: TreeNodeInfo): Promise<void> {
|
||||
public async removeObjectExplorerNode(node: TreeNodeInfo, isDisconnect: boolean = false): Promise<void> {
|
||||
await this.closeSession(node);
|
||||
const index = this._rootTreeNodeArray.indexOf(node, 0);
|
||||
if (index > -1) {
|
||||
this._rootTreeNodeArray.splice(index, 1);
|
||||
if (!isDisconnect) {
|
||||
const index = this._rootTreeNodeArray.indexOf(node, 0);
|
||||
if (index > -1) {
|
||||
this._rootTreeNodeArray.splice(index, 1);
|
||||
}
|
||||
}
|
||||
const nodeUri = ObjectExplorerUtils.getNodeUri(node);
|
||||
this._connectionManager.disconnect(nodeUri);
|
||||
this._treeNodeToChildrenMap.delete(node);
|
||||
this._nodePathToNodeLabelMap.delete(node.nodePath);
|
||||
this._sessionIdToConnectionCredentialsMap.delete(node.sessionId);
|
||||
if (this._sessionIdToPromiseMap.has(node.sessionId)) {
|
||||
this._sessionIdToPromiseMap.delete(node.sessionId);
|
||||
}
|
||||
this._currentNode = undefined;
|
||||
const nodeUri = node.nodePath + '_' + node.label;
|
||||
this._connectionManager.disconnect(nodeUri);
|
||||
await this._objectExplorerProvider.refresh(undefined);
|
||||
if (isDisconnect) {
|
||||
this._treeNodeToChildrenMap.set(node, [new ConnectTreeNode(node)]);
|
||||
this.updateNode(node);
|
||||
return this._objectExplorerProvider.refresh(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public async refreshNode(node: TreeNodeInfo): Promise<boolean> {
|
||||
public async refreshNode(node: TreeNodeInfo): Promise<void> {
|
||||
const refreshParams: RefreshParams = {
|
||||
sessionId: node.sessionId,
|
||||
nodePath: node.nodePath
|
||||
};
|
||||
const response = await this._connectionManager.client.sendRequest(RefreshRequest.type, refreshParams);
|
||||
return response;
|
||||
await this._connectionManager.client.sendRequest(RefreshRequest.type, refreshParams);
|
||||
return this._objectExplorerProvider.refresh(node);
|
||||
}
|
||||
|
||||
private async closeSession(node: TreeNodeInfo): Promise<void> {
|
||||
const closeSessionParams: CloseSessionParams = {
|
||||
sessionId: node.sessionId
|
||||
};
|
||||
const response = await this._connectionManager.client.sendRequest(CloseSessionRequest.type,
|
||||
closeSessionParams);
|
||||
if (response && response.success) {
|
||||
this._sessionIdToConnectionCredentialsMap.delete(response.sessionId);
|
||||
node.sessionId = undefined;
|
||||
this._currentNode = node;
|
||||
this._treeNodeToChildrenMap.set(this._currentNode, undefined);
|
||||
this._currentNode.collapsibleState = TreeItemCollapsibleState.Collapsed;
|
||||
public signInNodeServer(node: TreeNodeInfo): void {
|
||||
if (this._treeNodeToChildrenMap.has(node)) {
|
||||
this._treeNodeToChildrenMap.delete(node);
|
||||
}
|
||||
}
|
||||
|
||||
public async closeSession(node: TreeNodeInfo): Promise<void> {
|
||||
if (node.sessionId) {
|
||||
const closeSessionParams: CloseSessionParams = {
|
||||
sessionId: node.sessionId
|
||||
};
|
||||
const response = await this._connectionManager.client.sendRequest(CloseSessionRequest.type,
|
||||
closeSessionParams);
|
||||
if (response && response.success) {
|
||||
this._sessionIdToConnectionCredentialsMap.delete(response.sessionId);
|
||||
node.nodeType = Constants.disconnectedServerLabel;
|
||||
node.sessionId = undefined;
|
||||
this.updateNode(node);
|
||||
this._currentNode = node;
|
||||
this._treeNodeToChildrenMap.set(this._currentNode, undefined);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/** Getters */
|
||||
public get currentNode(): TreeNodeInfo {
|
||||
return this._currentNode;
|
||||
}
|
||||
|
||||
public get rootTreeNodeArray(): TreeNodeInfo[] {
|
||||
return this._rootTreeNodeArray;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,33 @@
|
|||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
import * as path from 'path';
|
||||
import { TreeNodeInfo } from './treeNodeInfo';
|
||||
import { IConnectionProfile } from '../models/interfaces';
|
||||
import Constants = require('../constants/constants');
|
||||
|
||||
export class ObjectExplorerUtils {
|
||||
|
||||
public static readonly rootPath: string = __dirname + '\\objectTypes\\';
|
||||
public static readonly rootPath: string = path.join(__dirname, 'objectTypes');
|
||||
|
||||
public static iconPath(label: string): string {
|
||||
if (label) {
|
||||
return ObjectExplorerUtils.rootPath + `${label}.svg`;
|
||||
return path.join(ObjectExplorerUtils.rootPath, `${label}.svg`);
|
||||
}
|
||||
}
|
||||
public static getNodeUri(node: TreeNodeInfo): string {
|
||||
while (node) {
|
||||
if (node.nodeType === Constants.serverLabel) {
|
||||
break;
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
const nodeUri = node.nodePath + '_' + node.label;
|
||||
return nodeUri;
|
||||
}
|
||||
|
||||
public static getNodeUriFromProfile(profile: IConnectionProfile): string {
|
||||
const uri = profile.server + '_' + profile.profileName;
|
||||
return uri;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>add</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,6v4H10v4H6V10H2V6H6V2h4V6Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M13,7V9H9v4H7V9H3V7H7V3H9V7Z"/></g></svg>
|
После Ширина: | Высота: | Размер: 486 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>add</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline" style="display: none;"><path class="icon-vs-out" d="M14,6v4H10v4H6V10H2V6H6V2h4V6Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M13,7V9H9v4H7V9H3V7H7V3H9V7Z"/></g></svg>
|
После Ширина: | Высота: | Размер: 486 B |
|
@ -6,6 +6,7 @@
|
|||
import * as vscode from 'vscode';
|
||||
import { NodeInfo } from '../models/contracts/objectExplorer/nodeInfo';
|
||||
import { ObjectExplorerUtils } from './objectExplorerUtils';
|
||||
import { IConnectionCredentials } from '../models/interfaces';
|
||||
|
||||
export class TreeNodeInfo extends vscode.TreeItem {
|
||||
|
||||
|
@ -17,6 +18,7 @@ export class TreeNodeInfo extends vscode.TreeItem {
|
|||
private _errorMessage: string;
|
||||
private _sessionId: string;
|
||||
private _parentNode: TreeNodeInfo;
|
||||
private _connectionCredentials: IConnectionCredentials;
|
||||
|
||||
constructor(
|
||||
label: string,
|
||||
|
@ -26,6 +28,7 @@ export class TreeNodeInfo extends vscode.TreeItem {
|
|||
nodeStatus: string,
|
||||
nodeType: string,
|
||||
sessionId: string,
|
||||
connectionCredentials: IConnectionCredentials,
|
||||
parentNode: TreeNodeInfo
|
||||
) {
|
||||
super(label, collapsibleState);
|
||||
|
@ -35,13 +38,19 @@ export class TreeNodeInfo extends vscode.TreeItem {
|
|||
this._nodeType = nodeType;
|
||||
this._sessionId = sessionId;
|
||||
this._parentNode = parentNode;
|
||||
this._connectionCredentials = connectionCredentials;
|
||||
this.iconPath = ObjectExplorerUtils.iconPath(this.nodeType);
|
||||
}
|
||||
|
||||
public static fromNodeInfo(nodeInfo: NodeInfo, sessionId: string, parentNode: TreeNodeInfo, label?: string): TreeNodeInfo {
|
||||
public static fromNodeInfo(
|
||||
nodeInfo: NodeInfo,
|
||||
sessionId: string,
|
||||
parentNode: TreeNodeInfo,
|
||||
connectionCredentials: IConnectionCredentials,
|
||||
label?: string): TreeNodeInfo {
|
||||
const treeNodeInfo = new TreeNodeInfo(label ? label : nodeInfo.label, nodeInfo.nodeType,
|
||||
vscode.TreeItemCollapsibleState.Collapsed, nodeInfo.nodePath, nodeInfo.nodeStatus,
|
||||
nodeInfo.nodeType, sessionId, parentNode);
|
||||
nodeInfo.nodeType, sessionId, connectionCredentials, parentNode);
|
||||
return treeNodeInfo;
|
||||
}
|
||||
|
||||
|
@ -78,6 +87,10 @@ export class TreeNodeInfo extends vscode.TreeItem {
|
|||
return this._parentNode;
|
||||
}
|
||||
|
||||
public get connectionCredentials(): IConnectionCredentials {
|
||||
return this._connectionCredentials;
|
||||
}
|
||||
|
||||
/** Setters */
|
||||
public set nodePath(value: string) {
|
||||
this._nodePath = value;
|
||||
|
@ -110,4 +123,8 @@ export class TreeNodeInfo extends vscode.TreeItem {
|
|||
public set parentNode(value: TreeNodeInfo) {
|
||||
this._parentNode = value;
|
||||
}
|
||||
|
||||
public set connectionCredentials(value: IConnectionCredentials) {
|
||||
this._connectionCredentials = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export interface IMessageProtocol {
|
|||
onMessage: Event<string>;
|
||||
}
|
||||
|
||||
class Deferred<T> {
|
||||
export class Deferred<T> {
|
||||
promise: Promise<T>;
|
||||
resolve: (value?: T | PromiseLike<T>) => void;
|
||||
reject: (reason?: any) => void;
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
/* --------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
|
||||
import SqlToolsServiceClient from '../languageservice/serviceclient';
|
||||
import ConnectionManager from '../controllers/connectionManager';
|
||||
import { ScriptingRequest, ScriptingParams, ScriptOperation, ScriptingObject, ScriptOptions } from '../models/contracts/scripting/scriptingRequest';
|
||||
import { TreeNodeInfo } from '../objectExplorer/treeNodeInfo';
|
||||
import VscodeWrapper from '../controllers/vscodeWrapper';
|
||||
|
||||
export class ScriptingService {
|
||||
|
||||
private _client: SqlToolsServiceClient;
|
||||
|
||||
constructor(
|
||||
private _connectionManager: ConnectionManager,
|
||||
private _vscodeWrapper: VscodeWrapper
|
||||
) {
|
||||
this._client = this._connectionManager.client;
|
||||
}
|
||||
|
||||
// map for the version of SQL Server (default is 140)
|
||||
readonly scriptCompatibilityOptionMap = {
|
||||
90: 'Script90Compat',
|
||||
100: 'Script100Compat',
|
||||
105: 'Script105Compat',
|
||||
110: 'Script110Compat',
|
||||
120: 'Script120Compat',
|
||||
130: 'Script130Compat',
|
||||
140: 'Script140Compat'
|
||||
};
|
||||
|
||||
// map for the target database engine edition (default is Enterprise)
|
||||
readonly targetDatabaseEngineEditionMap = {
|
||||
0: 'SqlServerEnterpriseEdition',
|
||||
1: 'SqlServerPersonalEdition',
|
||||
2: 'SqlServerStandardEdition',
|
||||
3: 'SqlServerEnterpriseEdition',
|
||||
4: 'SqlServerExpressEdition',
|
||||
5: 'SqlAzureDatabaseEdition',
|
||||
6: 'SqlDatawarehouseEdition',
|
||||
7: 'SqlServerStretchEdition'
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to get the object name and schema name
|
||||
* @param node
|
||||
*/
|
||||
private getObjectNames(node: TreeNodeInfo): string[] {
|
||||
let fullName = node.label;
|
||||
let objects = fullName.split('.');
|
||||
return objects;
|
||||
}
|
||||
|
||||
public async scriptSelect(node: TreeNodeInfo, uri: string): Promise<string> {
|
||||
const objectNames = this.getObjectNames(node);
|
||||
let scriptingObject: ScriptingObject = {
|
||||
type: node.nodeType,
|
||||
schema: objectNames[objectNames.length-2],
|
||||
name: objectNames[objectNames.length-1]
|
||||
};
|
||||
let serverInfo = this._connectionManager.getServerInfo(node.connectionCredentials);
|
||||
let scriptOptions: ScriptOptions = {
|
||||
scriptCreateDrop: 'ScriptSelect',
|
||||
typeOfDataToScript: 'SchemaOnly',
|
||||
scriptStatistics: 'ScriptStatsNone',
|
||||
targetDatabaseEngineEdition:
|
||||
serverInfo.engineEditionId ? this.targetDatabaseEngineEditionMap[serverInfo.engineEditionId] : 'SqlServerEnterpriseEdition',
|
||||
targetDatabaseEngineType: serverInfo.isCloud ? 'SqlAzure': 'SingleInstance',
|
||||
scriptCompatibilityOption: serverInfo.serverMajorVersion ?
|
||||
this.scriptCompatibilityOptionMap[serverInfo.serverMajorVersion] : 'Script140Compat'
|
||||
};
|
||||
let scriptingParams : ScriptingParams = {
|
||||
filePath: undefined,
|
||||
scriptDestination: 'ToEditor',
|
||||
connectionString: undefined,
|
||||
scriptingObjects: [scriptingObject],
|
||||
includeObjectCriteria: undefined,
|
||||
excludeObjectCriteria: undefined,
|
||||
includeSchemas: undefined,
|
||||
excludeSchemas: undefined,
|
||||
includeTypes: undefined,
|
||||
excludeTypes: undefined,
|
||||
scriptOptions: scriptOptions,
|
||||
connectionDetails: undefined,
|
||||
ownerURI: uri,
|
||||
selectScript: undefined,
|
||||
operation: ScriptOperation.Select
|
||||
}
|
||||
const result = await this._client.sendRequest(ScriptingRequest.type, scriptingParams);
|
||||
return result.script;
|
||||
}
|
||||
|
||||
}
|
|
@ -17,6 +17,7 @@ import Interfaces = require('../models/interfaces');
|
|||
import { Timer } from '../models/utils';
|
||||
import * as Utils from '../models/utils';
|
||||
import VscodeWrapper from '../controllers/vscodeWrapper';
|
||||
import { ObjectExplorerUtils} from '../objectExplorer/objectExplorerUtils';
|
||||
|
||||
/**
|
||||
* The different tasks for managing connection profiles.
|
||||
|
@ -182,6 +183,26 @@ export class ConnectionUI {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user for password
|
||||
*/
|
||||
public promptForPassword(): Promise<string> {
|
||||
const self = this;
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
let question: IQuestion = {
|
||||
type: QuestionTypes.password,
|
||||
name: LocalizedConstants.passwordPrompt,
|
||||
message: LocalizedConstants.passwordPrompt,
|
||||
placeHolder: LocalizedConstants.passwordPlaceholder
|
||||
};
|
||||
self._prompter.promptSingle(question).then((result: string) => {
|
||||
resolve(result);
|
||||
}).catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to change language mode to SQL.
|
||||
* @returns resolves to true if the user changed the language mode to SQL.
|
||||
|
@ -434,7 +455,7 @@ export class ConnectionUI {
|
|||
const self = this;
|
||||
let uri = self.vscodeWrapper.activeTextEditorUri;
|
||||
if (!uri) {
|
||||
uri = profile.server + '_' + profile.profileName;
|
||||
uri = ObjectExplorerUtils.getNodeUriFromProfile(profile);
|
||||
}
|
||||
return self.connectionManager.connect(uri, profile).then(result => {
|
||||
if (result) {
|
||||
|
|
|
@ -205,18 +205,18 @@ suite('MainController Tests', () => {
|
|||
test('onNewQuery should call the new query and new connection' , () => {
|
||||
|
||||
untitledSqlDocumentService.setup(x => x.newQuery()).returns(() => Promise.resolve(TypeMoq.It.isAny()));
|
||||
connectionManager.setup(x => x.onNewConnection()).returns(() => Promise.resolve(true));
|
||||
connectionManager.setup(x => x.onNewConnection()).returns(() => Promise.resolve(TypeMoq.It.isAny()));
|
||||
|
||||
return mainController.onNewQuery(undefined).then(result => {
|
||||
untitledSqlDocumentService.verify(x => x.newQuery(), TypeMoq.Times.once());
|
||||
connectionManager.verify(x => x.onNewConnection(), TypeMoq.Times.once());
|
||||
connectionManager.verify(x => x.onNewConnection(), TypeMoq.Times.atLeastOnce());
|
||||
});
|
||||
});
|
||||
|
||||
test('onNewQuery should not call the new connection if new query fails' , done => {
|
||||
|
||||
untitledSqlDocumentService.setup(x => x.newQuery()).returns(() => { return Promise.reject<vscode.Uri>('error'); } );
|
||||
connectionManager.setup(x => x.onNewConnection()).returns(() => { return Promise.resolve(true); } );
|
||||
connectionManager.setup(x => x.onNewConnection()).returns(() => { return Promise.resolve(TypeMoq.It.isAny()); } );
|
||||
|
||||
mainController.onNewQuery(undefined).catch(error => {
|
||||
untitledSqlDocumentService.verify(x => x.newQuery(), TypeMoq.Times.once());
|
||||
|
|
|
@ -35,12 +35,12 @@ suite('Object Explorer Tests', () => {
|
|||
test('Test Create Session', () => {
|
||||
expect(objectExplorerService.object.currentNode, 'Current Node should be undefined').is.equal(undefined);
|
||||
expect(objectExplorerProvider.objectExplorerExists, 'Object Explorer should not exist until started').is.equal(undefined);
|
||||
objectExplorerService.setup(s => s.createSession()).returns(() => {
|
||||
objectExplorerService.setup(s => s.createSession(TypeMoq.It.isAny())).returns(() => {
|
||||
objectExplorerService.setup(s => s.currentNode).returns(() => TypeMoq.It.isAny());
|
||||
objectExplorerProvider.objectExplorerExists = true;
|
||||
return Promise.resolve(TypeMoq.It.isAnyString());
|
||||
return Promise.resolve(TypeMoq.It.isAny());
|
||||
});
|
||||
objectExplorerProvider.createSession().then(sessionId => {
|
||||
objectExplorerProvider.createSession(TypeMoq.It.isAny()).then(sessionId => {
|
||||
expect(sessionId, 'Session Id should not be undefined').is.not.equal(undefined);
|
||||
expect(objectExplorerService.object.currentNode, 'Current Node should not be undefined').is.not.equal(undefined);
|
||||
expect(objectExplorerProvider.objectExplorerExists, 'Object Explorer session should exist').is.equal(true);
|
||||
|
@ -62,15 +62,14 @@ suite('Object Explorer Tests', () => {
|
|||
expect(credentials, 'Connection Credentials should not be null').is.not.equal(undefined);
|
||||
});
|
||||
|
||||
test('Test remove Object Explorer node', () => {
|
||||
test('Test remove Object Explorer node', async () => {
|
||||
let isNodeDeleted = false;
|
||||
objectExplorerService.setup(s => s.removeObjectExplorerNode(TypeMoq.It.isAny())).returns(() => {
|
||||
objectExplorerService.setup(s => s.removeObjectExplorerNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
|
||||
isNodeDeleted = true;
|
||||
return Promise.resolve(undefined);
|
||||
});
|
||||
objectExplorerProvider.removeObjectExplorerNode(TypeMoq.It.isAny()).then(() => {
|
||||
expect(isNodeDeleted, 'Node should be deleted').is.equal(true);
|
||||
});
|
||||
objectExplorerProvider.removeObjectExplorerNode(TypeMoq.It.isAny(), TypeMoq.It.isAny());
|
||||
expect(isNodeDeleted, 'Node should be deleted').is.equal(true);
|
||||
});
|
||||
|
||||
test('Test Get Children from Object Explorer Provider', () => {
|
||||
|
|
|
@ -490,7 +490,7 @@ suite('Per File Connection Tests', () => {
|
|||
let connectionManagerMock: TypeMoq.IMock<ConnectionManager> = TypeMoq.Mock.ofType(ConnectionManager);
|
||||
connectionManagerMock.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false);
|
||||
connectionManagerMock.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => true);
|
||||
connectionManagerMock.setup(x => x.onNewConnection()).returns(() => Promise.resolve(false));
|
||||
connectionManagerMock.setup(x => x.onNewConnection()).returns(() => Promise.resolve(undefined));
|
||||
|
||||
let controller: MainController = new MainController(contextMock.object,
|
||||
connectionManagerMock.object,
|
||||
|
|
Загрузка…
Ссылка в новой задаче