Compare commits

...

31 Commits

Author SHA1 Message Date
Charles Gagnon
8eff468100 Temporary fix for Data Explorer context menus (#23957) (#23967)
* Temporary fix for Data Explorer context menus

* Fix scripting actions

* better fix

* remove unused

(cherry picked from commit a182b852b1)
2023-07-24 10:20:55 -07:00
Cheena Malhotra
0a2cce3f12 Fix context menu in treeViews (#23948) (#23950) 2023-07-20 21:22:43 -07:00
Aasim Khan
2ef4e494c3 Removing unnecessary tree expansions causing errors to popup again (#23932) (#23937) 2023-07-19 17:44:28 -07:00
Aasim Khan
acb44b8247 Fixing issue that caused moving connections from one group to other to throw (#23933) (#23936) 2023-07-19 17:44:03 -07:00
Karl Burtram
9d2391c310 Update .cachesalt (#23929) 2023-07-18 21:37:54 -07:00
Alex Ma
636771328c Port request for connection title removal (#23916)
* Port request of title generation removal

* added fix to CMS

* added updated mainthread

* Revert "added updated mainthread"

This reverts commit a5806555a17bb7495c5a3573a98bd36a0f361785.

* Revert "added fix to CMS"

This reverts commit 2b8c24f09e4d7eadfcd047bbc554285ebd9811bc.

* Revert "Port request of title generation removal"

This reverts commit e6a2302778c9fd580f1f3d6de72c5037c14d815b.

* Remove/comment out connection title generation from all areas. (#23873)

---------

Co-authored-by: Cheena Malhotra <13396919+cheenamalhotra@users.noreply.github.com>
2023-07-18 20:57:08 -07:00
Cheena Malhotra
041bf75121 [Release/1.45] Allow empty username + fix profiles on read (#23924) (#23928)
* Allow empty username + fix profiles on read (#23924)

* Bump STS
2023-07-18 20:55:36 -07:00
Karl Burtram
ff10e3a042 Update build container to use G++-7 (#23912) (#23914) 2023-07-18 07:45:48 -07:00
Cheena Malhotra
6092b0a00b Clean encryption keys with cache clear command (#23875) (#23903) 2023-07-17 12:23:26 -10:00
Cheena Malhotra
f996ad0832 Fix serverInfo undefined due to wrong connection URI set on connection. (#23884) (#23891) 2023-07-17 07:08:57 -10:00
Charles Gagnon
df4847b517 Better fix for file browser race condition (#23882)
(cherry picked from commit 46f5f7a216b0b4069b7b8a11495ad25c9fafa25f)
2023-07-14 11:09:23 -10:00
Barbara Valdez
2be85a373f Add basic validation to database names (#23842) (#23881)
Co-authored-by: Cory Rivera <corivera@microsoft.com>
2023-07-14 10:25:45 -10:00
Sakshi Sharma
75a03a42f5 Add limit on the file size that can be opened with Open XEL feature (#23841) (#23876)
* Add limit on the file size that can be opened with Open XEL feature

* Add limit on the file size that can be opened and post a notification for large files

* Update wording

* Use FileService interface instead of fs to fix layering rules
2023-07-14 06:37:32 -10:00
Lucy Zhang
d20d664180 fix dropdown alignment (#23870) (#23874) 2023-07-13 21:12:21 -07:00
Kim Santiago
bf388cf31a Fix error when opening create project from db dialog without connection (#23864) (#23871)
* Fix error when opening create project from db dialog without connection

* Update extensions/sql-database-projects/src/dialogs/createProjectFromDatabaseDialog.ts



---------

Co-authored-by: Benjin Dubishar <benjin.dubishar@gmail.com>
2023-07-13 21:11:13 -07:00
Charles Gagnon
0bf439a73e Fix file browser opening race condition (#23869)
(cherry picked from commit db4e12577b5bbd19c2f65e5facf995c218aafa12)
2023-07-13 15:41:11 -07:00
Maddy
1acc00679e update ITreeElement with ICompressedTreeElement (#23839) (#23852) 2023-07-13 13:51:34 -07:00
Aasim Khan
8e1a288d99 Setting right options of command based new connections (#23849) (#23853) 2023-07-13 07:07:10 -10:00
Aasim Khan
5299cd4bbe Fixing chartjs (#23831) (#23851) 2023-07-13 06:22:25 -10:00
Cory Rivera
a644558bd0 Update SQL Tools version to 4.8.1.3 (#23844) 2023-07-13 08:53:54 -07:00
Charles Gagnon
fb807a1d28 Fix dialog buttons (#23843) (#23850)
* Fix dialog buttons

* comments

* Remove debug

(cherry picked from commit d294c81588)
2023-07-13 07:40:14 -07:00
Charles Gagnon
b6450d553f Update telemetry opt out URL (#23835) (#23838)
(cherry picked from commit 8bdc7d0346)
2023-07-12 16:31:11 -07:00
Aasim Khan
c78b7ca02f Fixing unsupported providers behavior on Async Server Tree (#23758) (#23791)
* Throwing error so that connection loading fails.

* Fixing stuff

* Fixing tests
2023-07-12 11:36:41 -07:00
Cheena Malhotra
90fccfda34 [Release/1.45] Fix connection URI default value comparisons (#23775)
* Fix connection URI default value comparisons (#23705)

* Bump STS
2023-07-11 16:13:26 -10:00
Sakshi Sharma
62ae25d05f Update vscode version for Profiler extension (#23767) (#23778)
* Update vscode version for Profiler extension

* Include all versions (*)
2023-07-11 14:14:44 -10:00
Lewis Sanchez
f7552fb746 Fix flaky ADS unit tests by using DOM rendering (#23770) (#23781)
* Makes use of xvfb to run unit tests

* Restore test script as before, but with xvfb

* Use same command that worked in CLI

* Add --build CLI arg

* Add coverage arg

* Revert change to core unit test script

* Reset core unit tests script in linux pipeline

* Revert "Skip flaky vscode test suites (#23535)"

This reverts commit 882bdb3aab.

* Revert "Disable failing vscode test suite (#23539)"

This reverts commit 562a0ce595.

* Revert "Disable vscode remote configuration suite (#23545)"

This reverts commit 40fa1cebd8.

* Revert "Disable editor resolver service test suite (#23550)"

This reverts commit cd68dca844.

* Revert "Disable upstream extension enablement suite (#23557)"

This reverts commit faf3c6976e.

* Revert "Skip a few more suites impacted by xterm issue (#23589)"

This reverts commit 1662cedf17.

# Conflicts:
#	src/vs/workbench/contrib/experiments/test/electron-sandbox/experimentService.test.ts

* Use DOM Renderer for tests

* Remove extra whitespace

* Update comment
2023-07-11 13:02:16 -10:00
Cory Rivera
034d818946 Limit Detach Database and Database/Server Properties to insiders and dev builds. (#23750) (#23773) 2023-07-11 09:50:03 -10:00
Barbara Valdez
94899e6e36 Create new context menu item for Database (#23771) (#23774) 2023-07-11 09:49:37 -10:00
Christopher Suh
a002a8cdec fix dropdown behavior (#23708) (#23772) 2023-07-11 08:21:15 -10:00
Alex Ma
1a52c8b0d8 [Loc] added translated getting started to langpack (#23744) (#23746) 2023-07-10 16:11:34 -07:00
Sakshi Sharma
949dddfe8c Update profiler version and related azdata dependency (#23739) 2023-07-10 12:58:26 -07:00
95 changed files with 411 additions and 1405 deletions

View File

@@ -1 +1 @@
2023-03-31T12:39:03.753Z
2023-07-18T12:39:03.753Z

View File

@@ -1,7 +1,11 @@
#Download base image ubuntu 22.04
FROM mcr.microsoft.com/mirror/docker/library/ubuntu:22.04
# Download base image ubuntu 20.04
FROM mcr.microsoft.com/mirror/docker/library/ubuntu:20.04
#Set timezone to avoid blocking prompts on docker build
# Adding apt repos for g++-7
RUN echo "deb http://dk.archive.ubuntu.com/ubuntu/ bionic main" >> /etc/apt/sources.list
RUN echo "deb http://dk.archive.ubuntu.com/ubuntu/ bionic universe" >> /etc/apt/sources.list
# Set timezone to avoid blocking prompts on docker build
ENV TZ=America/Los_Angeles
RUN ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime
RUN echo "$TZ" > /etc/timezone
@@ -14,13 +18,19 @@ RUN apt-get update && apt-get upgrade -y
RUN apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 \
libkrb5-dev git apt-transport-https ca-certificates curl gnupg-agent software-properties-common \
libnss3 libasound2 make gcc libx11-dev fakeroot rpm libgconf-2-4 libunwind8 g++ libgbm-dev wget
libnss3 libasound2 make gcc libx11-dev fakeroot rpm libgconf-2-4 libunwind8 g++-7 libgbm-dev wget
# make GCC 7 the default compiler
RUN rm /usr/bin/gcc
RUN ln -s /usr/bin/gcc-7 /usr/bin/gcc
RUN ln -s /usr/bin/g++-7 /usr/bin/g++
# Adding Libssl for dotnet 5.0 and ESRP signing to work
RUN wget -c http://security.ubuntu.com/ubuntu/pool/main/o/openssl1.0/libssl1.0.0_1.0.2n-1ubuntu5_amd64.deb
RUN dpkg -i libssl1.0.0_1.0.2n-1ubuntu5_amd64.deb
#docker
# docker
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

View File

@@ -1,7 +1,7 @@
resources:
containers:
- container: linux-x64
image: sqltoolscontainers.azurecr.io/linux-build-agent:8
image: sqltoolscontainers.azurecr.io/linux-build-agent:9
endpoint: SqlToolsContainers
stages:

View File

@@ -883,6 +883,9 @@ export abstract class AzureAuth implements vscode.Disposable {
// unlink both cache files
await this.msalCacheProvider.unlinkMsalCache();
await this.msalCacheProvider.unlinkLocalCache();
// Delete Encryption Keys
await this.msalCacheProvider.clearCacheEncryptionKeys();
}
public async deleteAllCacheAdal(): Promise<void> {

View File

@@ -20,6 +20,8 @@ import { Configuration, PublicClientApplication } from '@azure/msal-node';
import * as Constants from '../constants';
import { Logger } from '../utils/Logger';
import { ILoggerCallback, LogLevel as MsalLogLevel } from "@azure/msal-common";
import { displayReloadAds } from '../utils';
import { reloadPromptCacheClear } from '../localizedConstants';
let localize = nls.loadMessageBundle();
@@ -108,8 +110,7 @@ export class AzureAccountProviderService implements vscode.Disposable {
return Promise.all(promises)
.then(
() => {
let message = localize('clearTokenCacheSuccess', "Token cache successfully cleared");
void vscode.window.showInformationMessage(`${loc.extensionName}: ${message}`);
void displayReloadAds(reloadPromptCacheClear);
},
err => {
let message = localize('clearTokenCacheFailure', "Failed to clear token cache");

View File

@@ -100,10 +100,7 @@ export class FileEncryptionHelper {
if (resetOnError) {
// Reset IV/Keys if crypto cannot encrypt/decrypt data.
// This could be a possible case of corruption of expected iv/key combination
await this.deleteEncryptionKey(this._ivCredId);
await this.deleteEncryptionKey(this._keyCredId);
this._ivBuffer = undefined;
this._keyBuffer = undefined;
await this.clearEncryptionKeys();
await this.init();
}
// Throw error so cache file can be reset to empty.
@@ -111,6 +108,13 @@ export class FileEncryptionHelper {
}
}
public async clearEncryptionKeys(): Promise<void> {
await this.deleteEncryptionKey(this._ivCredId);
await this.deleteEncryptionKey(this._keyCredId);
this._ivBuffer = undefined;
this._keyBuffer = undefined;
}
protected async readEncryptionKey(credentialId: string): Promise<string | undefined> {
return (await this._credentialService.readCredential(credentialId))?.password;
}

View File

@@ -62,6 +62,10 @@ export class MsalCachePluginProvider {
return this._fileEncryptionHelper.getEncryptionKeys();
}
public async clearCacheEncryptionKeys(): Promise<void> {
await this._fileEncryptionHelper.clearEncryptionKeys();
}
public getCachePlugin(): ICachePlugin {
const beforeCacheAccess = async (cacheContext: TokenCacheContext): Promise<void> => {
try {

View File

@@ -293,7 +293,7 @@ async function onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent): Pro
if (vscode.workspace.getConfiguration(Constants.AzureSection).get('authenticationLibrary') === 'ADAL') {
void vscode.window.showInformationMessage(loc.deprecatedOption);
}
await displayReloadAds();
await utils.displayReloadAds(loc.reloadPrompt);
}
}
@@ -301,17 +301,3 @@ function updatePiiLoggingLevel(): void {
const piiLogging: boolean = vscode.workspace.getConfiguration(Constants.AzureSection).get('piiLogging', false);
Logger.piiLogging = piiLogging;
}
// Display notification with button to reload
// return true if button clicked
// return false if button not clicked
async function displayReloadAds(): Promise<boolean> {
const result = await vscode.window.showInformationMessage(loc.reloadPrompt, loc.reloadChoice);
if (result === loc.reloadChoice) {
await vscode.commands.executeCommand('workbench.action.reloadWindow');
return true;
} else {
return false;
}
}

View File

@@ -64,6 +64,7 @@ export const subscription = localize('azurecore.subscription', "Subscription");
export const typeIcon = localize('azurecore.typeIcon', "Type Icon");
export const reloadPrompt = localize('azurecore.reloadPrompt', "Authentication Library has changed, please reload Azure Data Studio.");
export const reloadPromptCacheClear = localize('azurecore.reloadPromptCacheClear', "Token cache has been cleared successfully, please reload Azure Data Studio.");
export const reloadChoice = localize('azurecore.reloadChoice', "Reload Azure Data Studio");
export const deprecatedOption = localize('azurecore.deprecated', "Warning: ADAL has been deprecated, and is scheduled to be removed in the next release. Please use MSAL instead.");

View File

@@ -200,3 +200,17 @@ export function getProxyEnabledHttpClient(): HttpClient {
return new HttpClient(proxy, agentOptions);
}
/* Display notification with button to reload
* return true if button clicked
* return false if button not clicked
*/
export async function displayReloadAds(message: string): Promise<boolean> {
const result = await vscode.window.showInformationMessage(message, loc.reloadChoice);
if (result === loc.reloadChoice) {
await vscode.commands.executeCommand('workbench.action.reloadWindow');
return true;
} else {
return false;
}
}

View File

@@ -1,6 +1,6 @@
{
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
"version": "4.8.0.39",
"version": "4.8.1.5",
"downloadFileNames": {
"Windows_86": "win-x86-net7.0.zip",
"Windows_64": "win-x64-net7.0.zip",

View File

@@ -83,6 +83,12 @@
"title": "%title.newObject%",
"icon": "$(add)"
},
{
"command": "mssql.newDatabase",
"category": "MSSQL",
"title": "%title.newDatabase%",
"icon": "$(add)"
},
{
"command": "mssql.objectProperties",
"category": "MSSQL",
@@ -490,6 +496,10 @@
"command": "mssql.newObject",
"when": "false"
},
{
"command": "mssql.newDatabase",
"when": "false"
},
{
"command": "mssql.objectProperties",
"when": "false"
@@ -526,12 +536,22 @@
},
{
"command": "mssql.newObject",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType =~ /^(ServerLevelLogins|Users|ServerLevelServerRoles|ApplicationRoles|DatabaseRoles|Databases)$/ && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType =~ /^(ServerLevelLogins|Users|ServerLevelServerRoles|ApplicationRoles|DatabaseRoles)$/ && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newObject",
"when": "connectionProvider == MSSQL && nodeType =~ /^(ServerLevelLogin|User|ServerLevelServerRole|ApplicationRole|DatabaseRole|Database)$/ && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && nodeType =~ /^(ServerLevelLogin|User|ServerLevelServerRole|ApplicationRole|DatabaseRole)$/ && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newDatabase",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType == Databases && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newDatabase",
"when": "connectionProvider == MSSQL && nodeType == Database && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
@@ -546,7 +566,7 @@
},
{
"command": "mssql.detachDatabase",
"when": "connectionProvider == MSSQL && nodeType == Database && !isCloud && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && nodeType == Database && !isCloud && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures && (productQualityType =~ /^(insider|dev)$/ || isDevelopment)",
"group": "1_objectManagement@2"
},
{
@@ -567,7 +587,7 @@
},
{
"command": "mssql.objectProperties",
"when": "connectionProvider == MSSQL && serverInfo && !isCloud && nodeType && nodeType =~ /^(Database|Server)$/ && mssql:engineedition != 11 && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && serverInfo && !isCloud && nodeType =~ /^(Database|Server)$/ && mssql:engineedition != 11 && config.workbench.enablePreviewFeatures && (productQualityType =~ /^(insider|dev)$/ || isDevelopment)",
"group": "z_objectexplorer@3"
},
{
@@ -613,12 +633,22 @@
},
{
"command": "mssql.newObject",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType =~ /^(ServerLevelLogins|Users|ServerLevelServerRoles|ApplicationRoles|DatabaseRoles|Databases)$/ && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType =~ /^(ServerLevelLogins|Users|ServerLevelServerRoles|ApplicationRoles|DatabaseRoles)$/ && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newObject",
"when": "connectionProvider == MSSQL && nodeType =~ /^(ServerLevelLogin|User|ServerLevelServerRole|ApplicationRole|DatabaseRole|Database)$/ && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"when": "connectionProvider == MSSQL && nodeType =~ /^(ServerLevelLogin|User|ServerLevelServerRole|ApplicationRole|DatabaseRole)$/ && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newDatabase",
"when": "connectionProvider == MSSQL && nodeType == Folder && objectType == Databases && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{
"command": "mssql.newDatabase",
"when": "connectionProvider == MSSQL && nodeType == Database && !(nodePath =~ /^.*\\/System Databases\\/.*$/) && config.workbench.enablePreviewFeatures",
"group": "1_objectManagement@0"
},
{

View File

@@ -186,6 +186,7 @@
"mssql.objectExplorer.disableGroupBySchemaTitle": "SQL Server: Disable Group By Schema",
"mssql.objectExplorer.expandTimeout": "The timeout in seconds for expanding a node in Object Explorer. The default value is 45 seconds.",
"title.newObject": "New (Preview)",
"title.newDatabase": "New Database (Preview)",
"title.objectProperties": "Properties (Preview)",
"title.deleteObject": "Delete (Preview)",
"title.renameObject": "Rename (Preview)",

View File

@@ -31,6 +31,9 @@ export function registerObjectManagementCommands(appContext: AppContext) {
appContext.extensionContext.subscriptions.push(vscode.commands.registerCommand('mssql.newObject', async (context: azdata.ObjectExplorerContext) => {
await handleNewObjectDialogCommand(context, service);
}));
appContext.extensionContext.subscriptions.push(vscode.commands.registerCommand('mssql.newDatabase', async (context: azdata.ObjectExplorerContext) => {
await handleNewObjectDialogCommand(context, service);
}));
appContext.extensionContext.subscriptions.push(vscode.commands.registerCommand('mssql.objectProperties', async (context: azdata.ObjectExplorerContext) => {
await handleObjectPropertiesDialogCommand(context, service);
}));

View File

@@ -114,10 +114,17 @@ export class DatabaseDialog extends ObjectManagementDialogBase<Database, Databas
//#region Create Database
private initializeGeneralSection(): azdata.GroupContainer {
let containers: azdata.Component[] = [];
this.nameInput = this.createInputBox(localizedConstants.NameText, async () => {
// The max length for database names is 128 characters: https://learn.microsoft.com/sql/t-sql/functions/db-name-transact-sql
const maxLengthDatabaseName: number = 128;
const props: azdata.InputBoxProperties = {
ariaLabel: localizedConstants.NameText,
required: true,
maxLength: maxLengthDatabaseName
};
this.nameInput = this.createInputBoxWithProperties(async () => {
this.objectInfo.name = this.nameInput.value;
await this.runValidation(false);
});
}, props);
containers.push(this.createLabelInputContainer(localizedConstants.NameText, this.nameInput));
if (this.viewInfo.loginNames?.length > 0) {

View File

@@ -26,18 +26,11 @@ export function registerTableDesignerCommands(appContext: AppContext) {
if (!connectionString) {
throw new Error(FailedToGetConnectionStringError);
}
let titleString = `${context.connectionProfile!.serverName} - ${context.connectionProfile!.databaseName} - ${NewTableText}`;
// append distinguishing options to end to let users know exact connection.
let distinguishingOptions = await azdata.connection.getEditorConnectionProfileTitle(context.connectionProfile, true);
if (distinguishingOptions !== '') {
distinguishingOptions = distinguishingOptions.replace('(', '[').replace(')', ']');
titleString += `${distinguishingOptions}`;
}
const tableIcon = context.nodeInfo!.nodeSubType as azdata.designers.TableIcon;
const telemetryInfo = await getTelemetryInfo(context, tableIcon);
await azdata.designers.openTableDesigner(sqlProviderName, {
title: NewTableText,
tooltip: titleString,
tooltip: `${context.connectionProfile!.serverName} - ${context.connectionProfile!.databaseName} - ${NewTableText}`,
server: context.connectionProfile!.serverName,
database: context.connectionProfile!.databaseName,
isNewTable: true,
@@ -63,18 +56,11 @@ export function registerTableDesignerCommands(appContext: AppContext) {
if (!connectionString) {
throw new Error(FailedToGetConnectionStringError);
}
let titleString = `${server} - ${database} - ${schema}.${name}`;
// append distinguishing options to end to let users know exact connection.
let distinguishingOptions = await azdata.connection.getEditorConnectionProfileTitle(context.connectionProfile, true);
if (distinguishingOptions !== '') {
distinguishingOptions = distinguishingOptions.replace('(', '[').replace(')', ']');
titleString += `${distinguishingOptions}`;
}
const tableIcon = context.nodeInfo!.nodeSubType as azdata.designers.TableIcon;
const telemetryInfo = await getTelemetryInfo(context, tableIcon);
await azdata.designers.openTableDesigner(sqlProviderName, {
title: `${schema}.${name}`,
tooltip: titleString,
tooltip: `${server} - ${database} - ${schema}.${name}`,
server: server,
database: database,
isNewTable: false,

View File

@@ -147,6 +147,24 @@ export abstract class DialogBase<DialogResult> {
return this.createInputBox(ariaLabel, textChangeHandler, value, enabled, 'password', width);
}
protected createInputBoxWithProperties(textChangeHandler: (newValue: string) => Promise<void>, properties: azdata.InputBoxProperties, customValidation?: () => Promise<boolean>): azdata.InputBoxComponent {
properties.width = properties.width ?? DefaultInputWidth;
properties.inputType = properties.inputType ?? 'text';
properties.value = properties.value ?? '';
properties.enabled = properties.enabled ?? true;
const inputbox = this.modelView.modelBuilder.inputBox().withProps(properties);
if (customValidation) {
inputbox.withValidation(customValidation);
}
const inputBoxComponent = inputbox.component();
this.disposables.push(inputBoxComponent.onTextChanged(async () => {
await textChangeHandler(inputBoxComponent.value!);
this.onFormFieldChange();
await this.runValidation(false);
}));
return inputBoxComponent;
}
protected createInputBox(ariaLabel: string, textChangeHandler: (newValue: string) => Promise<void>, value: string = '', enabled: boolean = true, type: azdata.InputBoxInputType = 'text', width: number = DefaultInputWidth, min?: number, max?: number): azdata.InputBoxComponent {
const inputbox = this.modelView.modelBuilder.inputBox().withProps({ inputType: type, enabled: enabled, ariaLabel: ariaLabel, value: value, width: width, min: min, max: max }).component();
this.disposables.push(inputbox.onTextChanged(async () => {

View File

@@ -2,13 +2,14 @@
"name": "profiler",
"displayName": "%displayName%",
"description": "%description%",
"version": "0.13.0",
"version": "0.14.0",
"publisher": "Microsoft",
"preview": true,
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
"icon": "images/extension.png",
"engines": {
"vscode": "0.10.0"
"vscode": "*",
"azdata": ">=1.45.0"
},
"activationEvents": [
"*"

View File

@@ -51,7 +51,9 @@ export class CreateProjectFromDatabaseDialog {
this.dialog.cancelButton.label = constants.cancelButtonText;
let connected = false;
if (this.profile) {
// make sure the connection profile passed in has sufficient information to attempt to connect
if (this.profile && this.profile?.serverName) {
const connections = await getAzdataApi()!.connection.getConnections(true);
connected = !!connections.find(c => c.connectionId === this.profile!.id);

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Ja (empfohlen)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "Erste &&Schritte",
"showReleaseNotes": "\"Erste Schritte\" anzeigen"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Verbindung erstellen",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Sí (opción recomendada)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "I&&ntroducción",
"showReleaseNotes": "Ver introducción"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Crear una conexión",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Oui (recommandé)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "Pri&&se en main",
"showReleaseNotes": "Afficher la mise en route"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Créer une connexion",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Sì (scelta consigliata)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "&&Introduzione",
"showReleaseNotes": "Mostra introduzione"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Crea una connessione",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "はい (推奨)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "はじめに(&&S)",
"showReleaseNotes": "「はじめに」を表示する"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "接続の作成",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "예(추천)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "시작(&&S)",
"showReleaseNotes": "시작 표시"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "연결 만들기",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Sim (recomendado)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "I&&ntrodução",
"showReleaseNotes": "Mostrar Introdução"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Criar uma conexão",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "Да (рекомендуется)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "&&Начало работы",
"showReleaseNotes": "Показать раздел \"Начало работы\""
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "Создать подключение",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "是(推荐)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "入门(&&S)",
"showReleaseNotes": "显示入门指南"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "创建连接",

View File

@@ -14606,8 +14606,8 @@
"enablePreviewFeatures.yes": "是 (建議)"
},
"sql/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted": {
"miGettingStarted": "Getting &&Started",
"showReleaseNotes": "Show Getting Started"
"miGettingStarted": "使用者入門(&&S)",
"showReleaseNotes": "顯示使用者入門"
},
"sql/workbench/contrib/welcome/page/browser/az_data_welcome_page": {
"welcomePage.createConnection": "建立連線",

View File

@@ -32,7 +32,7 @@
"reportIssueUrl": "https://github.com/Microsoft/azuredatastudio/issues/new",
"requestFeatureUrl": "https://go.microsoft.com/fwlink/?linkid=2118021",
"privacyStatementUrl": "https://privacy.microsoft.com/privacystatement",
"telemetryOptOutUrl": "https://github.com/Microsoft/azuredatastudio/wiki/How-to-Disable-Telemetry-Reporting",
"telemetryOptOutUrl": "https://go.microsoft.com/fwlink/?linkid=2241254",
"urlProtocol": "azuredatastudio-oss",
"enableTelemetry": false,
"webviewContentExternalBaseUrlTemplate": "https://{{uuid}}.vscode-cdn.net/insider/3c8520fab514b9f56070214496b26ff68d1b1cb5/out/vs/workbench/contrib/webview/browser/pre/",

View File

@@ -7096,11 +7096,11 @@ Fehler: {1}</target>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">Erste &amp;&amp;Schritte</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">"Erste Schritte" anzeigen</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</target>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">I&amp;&amp;ntroducción</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">Ver introducción</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Erreur : {1}</target>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">Pri&amp;&amp;se en main</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">Afficher la mise en route</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Errore: {1}</target>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">&amp;&amp;Introduzione</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">Mostra introduzione</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</source>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">はじめに(&amp;&amp;S)</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">「はじめに」を表示する</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</source>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">시작(&amp;&amp;S)</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">시작 표시</target>
</trans-unit>
</body>
</file>

View File

@@ -7097,11 +7097,11 @@ Erro: {1}</target>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">I&amp;&amp;ntrodução</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">Mostrar Introdução</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</source>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">&amp;&amp;Начало работы</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">Показать раздел "Начало работы"</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</source>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">入门(&amp;&amp;S)</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">显示入门指南</target>
</trans-unit>
</body>
</file>

View File

@@ -7096,11 +7096,11 @@ Error: {1}</source>
<trans-unit id="miGettingStarted">
<source xml:lang="en">Getting &amp;&amp;Started</source>
<note>&amp;&amp; denotes a mnemonic</note>
<target state="new">Getting &amp;&amp;Started</target>
<target state="translated">使用者入門(&amp;&amp;S)</target>
</trans-unit>
<trans-unit id="showReleaseNotes">
<source xml:lang="en">Show Getting Started</source>
<target state="new">Show Getting Started</target>
<target state="translated">顯示使用者入門</target>
</trans-unit>
</body>
</file>

View File

@@ -508,15 +508,6 @@ declare module 'azdata' {
* @returns The new password that is returned from the operation or undefined if unsuccessful.
*/
export function openChangePasswordDialog(profile: IConnectionProfile): Thenable<string | undefined>;
/**
* Gets the formatted title of the connection profile for display
* @param profile The connection profile we want to get the full display info for.
* @param getOptionsOnly Provide if you only want to get the differing advanced options (for some titles).
* @param includeGroupName Provide if you want to include the groupName as well (in areas that do not display the groupName).
* @returns The title formatted with server info in front, with non default options at the end.
*/
export function getEditorConnectionProfileTitle(profile: IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): Thenable<string>;
}
/*

View File

@@ -79,7 +79,7 @@ export class Dropdown extends BaseDropdown {
}
}
export class DropdownList extends BaseDropdown {
export class DropdownList extends Dropdown {
protected borderWidth = 1;
private button?: Button;
@@ -145,7 +145,7 @@ export class DropdownList extends BaseDropdown {
/**
* Render the dropdown contents
*/
protected renderContents(container: HTMLElement): IDisposable | null {
protected override renderContents(container: HTMLElement): IDisposable | null {
let div = DOM.append(container, this._contentContainer);
div.style.width = (DOM.getTotalWidth(this.element) - this.borderWidth * 2) + 'px'; // Subtract border width
return { dispose: () => { } };

View File

@@ -14,6 +14,7 @@ import * as nls from 'vs/nls';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { isDisposable } from 'vs/base/common/lifecycle';
import { isUndefinedOrNull } from 'vs/base/common/types';
export const GROUPS_CONFIG_KEY = 'datasource.connectionGroups';
export const CONNECTIONS_CONFIG_KEY = 'datasource.connections';
@@ -241,6 +242,11 @@ export class ConnectionConfig {
profile.id = generateUuid();
changed = true;
}
// SSMS 19 requires "user", fix any profiles created without user property.
if (profile.providerName === 'MSSQL' && isUndefinedOrNull(profile.options.user)) {
profile.options.user = '';
changed = true;
}
idsCache[profile.id] = true;
}
return changed;
@@ -359,10 +365,10 @@ export class ConnectionConfig {
let profiles = this.getIConnectionProfileStores(true);
let existingProfile = profiles.find(p =>
p.providerName === profile.providerName &&
p.options.authenticationType === profile.options.authenticationType &&
p.options.database === profile.options.database &&
p.options.server === profile.options.server &&
p.options.user === profile.options.user &&
this.checkIfAuthenticationOptionsMatch(p, profile) &&
p.options.databaseName === profile.options.databaseName &&
p.options.serverName === profile.options.serverName &&
p.options.userName === profile.options.userName &&
p.options.connectionName === profile.options.connectionName &&
p.groupId === newGroupID &&
this.checkIfNonDefaultOptionsMatch(p, profile));
@@ -375,6 +381,10 @@ export class ConnectionConfig {
return result;
}
private checkIfAuthenticationOptionsMatch(profileStore: IConnectionProfileStore, profile: ConnectionProfile): boolean {
return ((profileStore.options.authenticationType === undefined || profileStore.options.authenticationType === '') && (profile.options.authenticationType === undefined || profile.options.authenticationType === '')) || profileStore.options.authenticationType === profile.options.authenticationType;
}
/**
* Moves the connection under the target group with the new ID.
*/

View File

@@ -381,15 +381,6 @@ export interface IConnectionManagementService {
* @returns the new valid password that is entered, or undefined if cancelled or errored.
*/
openChangePasswordDialog(profile: IConnectionProfile): Promise<string | undefined>;
/**
* Gets the formatted title of the connection profile for display.
* @param profile The connection profile we want to get the full display info for.
* @param getOptionsOnly Provide if you only want to get the differing advanced options (for some titles).
* @param includeGroupName Provide if you want to include the groupName as well (in areas that do not display the groupName).
* @returns The title formatted with server info in front, with non default options at the end.
*/
getEditorConnectionProfileTitle(profile: IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): string;
}
export enum RunQueryOnConnectionMode {

View File

@@ -9,7 +9,7 @@ import { ProviderConnectionInfo } from 'sql/platform/connection/common/providerC
import * as interfaces from 'sql/platform/connection/common/interfaces';
import { generateUuid } from 'vs/base/common/uuid';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { isString } from 'vs/base/common/types';
import { isString, isUndefinedOrNull } from 'vs/base/common/types';
import { deepClone } from 'vs/base/common/objects';
import * as Constants from 'sql/platform/connection/common/constants';
import { URI } from 'vs/base/common/uri';
@@ -81,7 +81,7 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
// Set values for advanced options received in model.
Object.keys(model.options).forEach(a => {
let option = options.find(opt => opt.name === a);
if (option !== undefined) {
if (option !== undefined && option.defaultValue?.toString().toLocaleLowerCase() !== model.options[a]?.toString().toLocaleLowerCase()) {
this.options[option.name] = model.options[a];
}
});
@@ -358,7 +358,13 @@ export class ConnectionProfile extends ProviderConnectionInfo implements interfa
id: connectionInfo.id
};
profile.options = connectionInfo.options;
Object.keys(connectionInfo.options).forEach(element => {
// Allow empty strings to ensure "user": "" is populated if empty << Required by SSMS 19.2
// Do not change this design until SSMS 19 goes out of support.
if (!isUndefinedOrNull(connectionInfo.options[element])) {
profile.options[element] = connectionInfo.options[element];
}
});
return profile;
}

View File

@@ -326,12 +326,6 @@ export class ConnectionStore {
return this.convertToConnectionGroup(groups, profilesInConfiguration);
}
public getAllConnectionsFromConfig(): ConnectionProfile[] {
let profilesInConfiguration: ConnectionProfile[] | undefined;
profilesInConfiguration = this.connectionConfig.getConnections(true);
return profilesInConfiguration;
}
private convertToConnectionGroup(groups: IConnectionProfileGroup[], connections?: ConnectionProfile[], parent?: ConnectionProfileGroup): ConnectionProfileGroup[] {
const result: ConnectionProfileGroup[] = [];
const children = groups.filter(g => g.parentId === (parent ? parent.id : undefined));

View File

@@ -134,7 +134,7 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
this.options[name] = value;
}
public getServerInfo() {
private getServerInfo() {
let title = '';
if (this.serverCapabilities) {
title = this.serverName;
@@ -169,7 +169,7 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
return label;
}
public hasLoaded(): boolean {
private hasLoaded(): boolean {
return Object.keys(this.capabilitiesService.providers).length > 0;
}
@@ -231,7 +231,8 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
let finalValue = undefined;
let options = this.serverCapabilities.connectionOptions.filter(value => value.name === idNames[index]!);
if (options.length > 0 && value) {
finalValue = value !== options[0].defaultValue ? value : undefined;
let defaultValue = options[0].defaultValue ?? '';
finalValue = value && value.toString().toLocaleLowerCase() !== defaultValue.toString().toLocaleLowerCase() ? value : undefined;
if (options[0].specialValueType === 'appName' && this.providerName === Constants.mssqlProviderName) {
finalValue = (value as string).startsWith('azdata') ? undefined : finalValue
}
@@ -390,7 +391,7 @@ export class ProviderConnectionInfo implements azdata.ConnectionInfo {
element.specialValueType !== ConnectionOptionSpecialType.password) {
if (getNonDefault) {
let value = this.getOptionValue(element.name);
if (value && value !== element.defaultValue) {
if (value && value.toString().toLocaleLowerCase() !== element.defaultValue?.toLocaleLowerCase()) {
connectionOptions.push(element);
}
}

View File

@@ -82,7 +82,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: ''
authenticationType: 'SqlLogin'
},
providerName: 'MSSQL',
groupId: 'test',
@@ -95,7 +95,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: ''
authenticationType: 'SqlLogin'
},
providerName: 'MSSQL',
groupId: 'test',
@@ -108,7 +108,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: ''
authenticationType: 'SqlLogin'
},
providerName: 'MSSQL',
groupId: 'g3',
@@ -296,7 +296,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: undefined,
groupId: undefined,
@@ -364,7 +364,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: undefined,
@@ -509,7 +509,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -541,7 +541,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -580,7 +580,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'newid',
@@ -662,7 +662,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -681,7 +681,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -723,7 +723,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -745,7 +745,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -785,7 +785,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -807,7 +807,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -816,7 +816,10 @@ suite('ConnectionConfig', () => {
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { 'testProperty1': 'nonDefault' },
options: {
'testProperty1': 'nonDefault',
'testProperty2': '10'
},
saveProfile: true,
id: 'server3',
connectionName: undefined!
@@ -850,7 +853,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -872,7 +875,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -912,7 +915,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -1016,7 +1019,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
@@ -1035,7 +1038,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -1054,7 +1057,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -1090,7 +1093,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',
@@ -1128,7 +1131,7 @@ suite('ConnectionConfig', () => {
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
authenticationType: 'SqlLogin',
savePassword: true,
groupFullName: 'test',
groupId: 'test',

View File

@@ -355,10 +355,6 @@ export class TestConnectionManagementService implements IConnectionManagementSer
return undefined;
}
getEditorConnectionProfileTitle(profile: IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): string {
return undefined!;
}
openCustomErrorDialog(options: azdata.window.IErrorDialogOptions): Promise<string | undefined> {
return undefined;
}

View File

@@ -185,10 +185,6 @@ export class MainThreadConnectionManagement extends Disposable implements MainTh
return this._connectionManagementService.openChangePasswordDialog(convertedProfile);
}
public $getEditorConnectionProfileTitle(profile: IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): Thenable<string | undefined> {
return Promise.resolve(this._connectionManagementService.getEditorConnectionProfileTitle(profile, getOptionsOnly, includeGroupName));
}
public async $listDatabases(connectionId: string): Promise<string[]> {
let connectionUri = await this.$getUriForConnection(connectionId);
let result = await this._connectionManagementService.listDatabases(connectionUri);

View File

@@ -78,10 +78,6 @@ export class ExtHostConnectionManagement extends ExtHostConnectionManagementShap
return this._proxy.$openChangePasswordDialog(profile);
}
public $getEditorConnectionProfileTitle(profile: azdata.IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): Thenable<string> {
return this._proxy.$getEditorConnectionProfileTitle(profile, getOptionsOnly, includeGroupName);
}
public $listDatabases(connectionId: string): Thenable<string[]> {
return this._proxy.$listDatabases(connectionId);
}

View File

@@ -141,9 +141,6 @@ export function createAdsApiFactory(accessor: ServicesAccessor): IAdsExtensionAp
openChangePasswordDialog(profile: azdata.IConnectionProfile): Thenable<string | undefined> {
return extHostConnectionManagement.$openChangePasswordDialog(profile);
},
getEditorConnectionProfileTitle(profile: azdata.IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): Thenable<string> {
return extHostConnectionManagement.$getEditorConnectionProfileTitle(profile, getOptionsOnly, includeGroupName);
},
listDatabases(connectionId: string): Thenable<string[]> {
return extHostConnectionManagement.$listDatabases(connectionId);
},

View File

@@ -729,7 +729,6 @@ export interface MainThreadConnectionManagementShape extends IDisposable {
$getServerInfo(connectedId: string): Thenable<azdata.ServerInfo>;
$openConnectionDialog(providers: string[], initialConnectionProfile?: azdata.IConnectionProfile, connectionCompletionOptions?: azdata.IConnectionCompletionOptions): Thenable<azdata.connection.Connection>;
$openChangePasswordDialog(profile: azdata.IConnectionProfile): Thenable<string | undefined>;
$getEditorConnectionProfileTitle(profile: azdata.IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): Thenable<string>;
$listDatabases(connectionId: string): Thenable<string[]>;
$getConnectionString(connectionId: string, includePassword: boolean): Thenable<string>;
$getUriForConnection(connectionId: string): Thenable<string>;

View File

@@ -85,14 +85,11 @@ export class DashboardInput extends EditorInput {
}
let name = this.connectionProfile.connectionName ? this.connectionProfile.connectionName : this.connectionProfile.serverName
if (!this.connectionProfile.connectionName && this.connectionProfile.databaseName
if (this.connectionProfile.databaseName
&& !this.isMasterMssql()) {
// Only add DB name if this is a non-default, non-master connection
name = name + ':' + this.connectionProfile.databaseName;
}
// Append any differing options if needed.
name += this._connectionService.getEditorConnectionProfileTitle(this.connectionProfile, true, true)
return name;
}

View File

@@ -243,17 +243,11 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab
title = this._description + ' ';
}
if (profile) {
let distinguishedTitle = this.connectionManagementService.getEditorConnectionProfileTitle(profile);
if (distinguishedTitle !== '') {
title += distinguishedTitle;
}
else {
title += `${profile.serverName}`;
if (profile.databaseName) {
title += `.${profile.databaseName}`;
}
title += ` (${profile.userName || profile.authenticationType})`;
title += `${profile.serverName}`;
if (profile.databaseName) {
title += `.${profile.databaseName}`;
}
title += ` (${profile.userName || profile.authenticationType})`;
} else {
title += localize('disconnected', "disconnected");
}

View File

@@ -220,11 +220,11 @@ export class Graph implements IInsight {
};
if (options.xAxisMax !== undefined) {
retval.scales = mixin(retval.scales, { xAxes: [{ ticks: { max: options.xAxisMax } }] }, true, customMixin);
retval.scales = mixin(retval.scales, { x: { max: options.xAxisMax } }, true, customMixin);
}
if (options.xAxisMin !== undefined) {
retval.scales = mixin(retval.scales, { xAxes: [{ ticks: { min: options.xAxisMin } }] }, true, customMixin);
retval.scales = mixin(retval.scales, { x: { min: options.xAxisMin } }, true, customMixin);
}
retval.scales.y = {
@@ -242,11 +242,11 @@ export class Graph implements IInsight {
};
if (options.yAxisMax !== undefined) {
retval.scales = mixin(retval.scales, { yAxes: [{ ticks: { max: options.yAxisMax } }] }, true, customMixin);
retval.scales = mixin(retval.scales, { y: { max: options.yAxisMax } }, true, customMixin);
}
if (options.yAxisMin !== undefined) {
retval.scales = mixin(retval.scales, { yAxes: [{ ticks: { min: options.yAxisMin } }] }, true, customMixin);
retval.scales = mixin(retval.scales, { y: { min: options.yAxisMin } }, true, customMixin);
}
if (this.originalType === ChartType.TimeSeries) {
@@ -254,11 +254,9 @@ export class Graph implements IInsight {
if (options.xAxisMax !== undefined) {
retval = mixin(retval, {
scales: {
xAxes: [{
time: {
max: options.xAxisMax
}
}],
x: {
max: options.xAxisMax
}
}
}, true, customMixin);
}
@@ -266,11 +264,9 @@ export class Graph implements IInsight {
if (options.xAxisMin !== undefined) {
retval = mixin(retval, {
scales: {
xAxes: [{
time: {
min: options.xAxisMin
}
}],
x: {
min: options.xAxisMin
}
}
}, true, customMixin);
}

View File

@@ -11,7 +11,7 @@ import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/acti
import { localize } from 'vs/nls';
import { ConnectionStatusbarItem } from 'sql/workbench/contrib/connection/browser/connectionStatus';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionType, IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
@@ -21,6 +21,7 @@ import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
import { ActiveConnectionsFilterAction, AddServerAction, AddServerGroupAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
import { CONTEXT_SERVER_TREE_VIEW, CONTEXT_SERVER_TREE_HAS_CONNECTIONS } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView';
import { SqlIconId } from 'sql/base/common/codicons';
import * as Utils from 'sql/platform/connection/common/utils';
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
@@ -132,23 +133,31 @@ CommandsRegistry.registerCommand('azdata.connect',
groupFullName: undefined,
saveProfile: true,
id: undefined,
groupId: undefined,
groupId: Utils.defaultGroupId,
options: args.options
};
const connectionProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesServices, profile);
const root = connectionManagementService.getConnectionGroups().filter(g => g.id === Utils.defaultGroupId)[0];
connectionProfile.parent = root;
connectionProfile.groupFullName = root.fullName;
connectionManagementService.connect(connectionProfile, undefined, {
saveTheConnection: true,
showDashboard: true,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
showFirewallRuleOnError: true,
params: {
connectionType: ConnectionType.default,
}
});
} else {
connectionManagementService.showConnectionDialog(undefined, {
saveTheConnection: true,
showDashboard: true,
showConnectionDialogOnError: true,
showFirewallRuleOnError: true
showFirewallRuleOnError: true,
params: {
connectionType: ConnectionType.default,
}
});
}
});

View File

@@ -68,30 +68,20 @@ export class ConnectionStatusbarItem extends Disposable implements IWorkbenchCon
// Set connection info to connection status bar
private _setConnectionText(connectionProfile: IConnectionProfile): void {
let distinguishedTitle = this.connectionManagementService.getEditorConnectionProfileTitle(connectionProfile);
let text: string = '';
let tooltip: string = '';
if (distinguishedTitle === '') {
text = connectionProfile.serverName;
if (text) {
if (connectionProfile.databaseName && connectionProfile.databaseName !== '') {
text = text + ' : ' + connectionProfile.databaseName;
} else {
text = text + ' : ' + '<default>';
}
}
tooltip = 'Server: ' + connectionProfile.serverName + '\r\n' +
'Database: ' + (connectionProfile.databaseName ? connectionProfile.databaseName : '<default>') + '\r\n';
if (connectionProfile.userName && connectionProfile.userName !== '') {
tooltip = tooltip + 'Login: ' + connectionProfile.userName + '\r\n';
let text: string = connectionProfile.serverName;
if (text) {
if (connectionProfile.databaseName && connectionProfile.databaseName !== '') {
text = text + ' : ' + connectionProfile.databaseName;
} else {
text = text + ' : ' + '<default>';
}
}
else {
text = distinguishedTitle;
tooltip = (connectionProfile as any).serverInfo;
let tooltip = 'Server: ' + connectionProfile.serverName + '\r\n' +
'Database: ' + (connectionProfile.databaseName ? connectionProfile.databaseName : '<default>') + '\r\n';
if (connectionProfile.userName && connectionProfile.userName !== '') {
tooltip = tooltip + 'Login: ' + connectionProfile.userName + '\r\n';
}
this.statusItem.update({

View File

@@ -57,9 +57,7 @@ export class BreadcrumbService implements IBreadcrumbService {
}
private getServerBreadcrumb(profile: ConnectionProfile): MenuItem {
let formattedProfileName = profile.connectionName ? profile.connectionName : profile.serverName;
formattedProfileName += this.commonService.connectionManagementService.getEditorConnectionProfileTitle(profile, true, true);
return { label: formattedProfileName, routerLink: ['server-dashboard'] };
return profile.connectionName ? { label: profile.connectionName, routerLink: ['server-dashboard'] } : { label: profile.serverName, routerLink: ['server-dashboard'] };
}
private getDbBreadcrumb(profile: ConnectionProfile): MenuItem {

View File

@@ -358,7 +358,7 @@ export class ToggleAddCellActionViewItem extends DropdownMenuActionViewItem {
{
actionRunner,
classNames: ToggleAddCellDropdownAction.ICON,
anchorAlignmentProvider: () => AnchorAlignment.RIGHT
anchorAlignmentProvider: () => AnchorAlignment.LEFT
});
this.setActionContext(cellContext);
}
@@ -392,7 +392,7 @@ export class CellToggleMoreActionViewItem extends DropdownMenuActionViewItem {
{
actionRunner,
classNames: CellToggleMoreAction.ICON,
anchorAlignmentProvider: () => AnchorAlignment.RIGHT
anchorAlignmentProvider: () => AnchorAlignment.LEFT
});
this.setActionContext(this._cellContext);
this._actions = [

View File

@@ -59,6 +59,7 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { debounce } from 'vs/base/common/decorators';
import { ToggleAddCellDropdownAction } from 'sql/workbench/contrib/notebook/browser/cellToolbarActions';
import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
const PRIORITY = 105;
@@ -551,7 +552,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
undefined,
'codicon masked-pseudo masked-pseudo-after add-new dropdown-arrow',
localize('addCell', "Cell"),
undefined
() => AnchorAlignment.LEFT
);
dropdownMenuActionViewItem.render(buttonDropdownContainer);
dropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri);
@@ -572,7 +573,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
undefined,
'codicon notebook-button masked-pseudo masked-pseudo-after icon-dashboard-view dropdown-arrow',
localize('editor', "Editor"),
undefined
() => AnchorAlignment.LEFT
);
viewsDropdownMenuActionViewItem.render(viewsDropdownContainer);
viewsDropdownMenuActionViewItem.setActionContext(this._notebookParams.notebookUri);

View File

@@ -756,10 +756,7 @@ export class AttachToDropdown extends SelectBox {
} else {
let connections: string[] = [];
if (model.context && model.context.title && (connProviderIds.includes(this.model.context.providerName))) {
let textResult = model.context.title;
let fullTitleText = this._connectionManagementService.getEditorConnectionProfileTitle(model.context);
textResult = fullTitleText.length !== 0 ? fullTitleText : textResult;
connections.push(textResult);
connections.push(model.context.title);
} else if (this._configurationService.getValue(saveConnectionNameConfigName) && model.savedConnectionName) {
connections.push(model.savedConnectionName);
} else {

View File

@@ -37,7 +37,8 @@ import { MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import * as aria from 'vs/base/browser/ui/aria/aria';
import * as errors from 'vs/base/common/errors';
import { NotebookSearchWidget } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookSearchWidget';
import { ITreeElement, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { ITreeContextMenuEvent, ObjectTreeElementCollapseState } from 'vs/base/browser/ui/tree/tree';
import { Iterable } from 'vs/base/common/iterator';
import { searchClearIcon, searchCollapseAllIcon, searchExpandAllIcon, searchStopIcon } from 'vs/workbench/contrib/search/browser/searchIcons';
import { Action, IAction } from 'vs/base/common/actions';
@@ -430,45 +431,49 @@ export class NotebookSearchView extends SearchView {
return false;
}
private createSearchResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable<ITreeElement<RenderableMatch>> {
private createSearchResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable<ICompressedTreeElement<RenderableMatch>> {
const folderMatches = this.searchResult.folderMatches()
.filter(fm => !fm.isEmpty())
.sort(searchMatchComparer);
if (folderMatches.length === 1) {
return this.createSearchFolderIterator(folderMatches[0], collapseResults);
return this.createSearchFolderIterator(folderMatches[0], collapseResults, true);
}
return Iterable.map(folderMatches, folderMatch => {
const children = this.createSearchFolderIterator(folderMatch, collapseResults);
return <ITreeElement<RenderableMatch>>{ element: folderMatch, children };
const children = this.createSearchFolderIterator(folderMatch, collapseResults, true);
return <ICompressedTreeElement<RenderableMatch>>{ element: folderMatch, children };
});
}
private createSearchFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable<ITreeElement<RenderableMatch>> {
private createSearchFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults'], childFolderIncompressible: boolean): Iterable<ICompressedTreeElement<RenderableMatch>> {
const sortOrder = this.searchConfig.sortOrder;
const matches = folderMatch.matches().sort((a, b) => searchMatchComparer(a, b, sortOrder));
return Iterable.map(matches, fileMatch => {
//const children = this.createFileIterator(fileMatch);
let nodeExists = true;
try { this.tree.getNode(fileMatch); } catch (e) { nodeExists = false; }
const matchArray = this.isTreeLayoutViewVisible ? folderMatch.matches() : folderMatch.allDownstreamFileMatches();
const matches = matchArray.sort((a, b) => searchMatchComparer(a, b, sortOrder));
const collapsed = nodeExists ? undefined :
(collapseResults === 'alwaysCollapse' || (fileMatch.matches().length > 10 && collapseResults !== 'alwaysExpand'));
return Iterable.map(matches, match => {
let children;
if (match instanceof FileMatch) {
children = this.createSearchFileIterator(match);
} else {
children = this.createSearchFolderIterator(match, collapseResults, false);
}
return <ITreeElement<RenderableMatch>>{ element: fileMatch, undefined, collapsed, collapsible: false };
const collapsed = (collapseResults === 'alwaysCollapse' || (match.count() > 10 && collapseResults !== 'alwaysExpand')) ? ObjectTreeElementCollapseState.PreserveOrCollapsed : ObjectTreeElementCollapseState.PreserveOrExpanded;
return <ICompressedTreeElement<RenderableMatch>>{ element: match, children, collapsed, incompressible: (match instanceof FileMatch) ? true : childFolderIncompressible };
});
}
private createSearchFileIterator(fileMatch: FileMatch): Iterable<ITreeElement<RenderableMatch>> {
private createSearchFileIterator(fileMatch: FileMatch): Iterable<ICompressedTreeElement<RenderableMatch>> {
const matches = fileMatch.matches().sort(searchMatchComparer);
return Iterable.map(matches, r => (<ITreeElement<RenderableMatch>>{ element: r }));
return Iterable.map(matches, r => (<ICompressedTreeElement<RenderableMatch>>{ element: r }));
}
private createSearchIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable<ITreeElement<RenderableMatch>> {
private createSearchIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable<ICompressedTreeElement<RenderableMatch>> {
return match instanceof SearchResult ? this.createSearchResultIterator(collapseResults) :
match instanceof FolderMatch ? this.createSearchFolderIterator(match, collapseResults) :
match instanceof FolderMatch ? this.createSearchFolderIterator(match, collapseResults, false) :
this.createSearchFileIterator(match);
}

View File

@@ -126,7 +126,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
// get the full ConnectionProfiles with the server info updated properly
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
await this._tree.setInput(treeInput);
await this.refreshConnectionTreeTitles();
this._treeSelectionHandler.onTreeActionStateChange(false);
} else {
if (this._connectionManagementService.hasRegisteredServers()) {
@@ -273,7 +272,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (connectionParentGroup) {
connectionParentGroup.addOrReplaceConnection(newConnection);
await this._tree.updateChildren(connectionParentGroup);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(newConnection);
await this._tree.expand(newConnection);
}
@@ -288,7 +286,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
await this._tree.rerender(connectionInTree);
await this._tree.revealSelectFocusElement(connectionInTree);
await this._tree.updateChildren(connectionInTree);
await this.refreshConnectionTreeTitles();
await this._tree.expand(connectionInTree);
}
}
@@ -301,7 +298,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (parentGroup) {
parentGroup.removeConnections([e]);
await this._tree.updateChildren(parentGroup);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(parentGroup);
}
}
@@ -319,14 +315,12 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const newProfileParent = <ConnectionProfileGroup>this._tree.getElementById(e.profile.groupId);
newProfileParent.addOrReplaceConnection(e.profile);
await this._tree.updateChildren(newProfileParent);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(e.profile);
await this._tree.expand(e.profile);
} else {
// If the profile was not moved to a different group then just update the profile in the group.
oldProfileParent.replaceConnection(e.profile, e.oldProfileId);
await this._tree.updateChildren(oldProfileParent)
await this.refreshConnectionTreeTitles();
await this._tree.updateChildren(oldProfileParent);
await this._tree.revealSelectFocusElement(e.profile);
await this._tree.expand(e.profile);
}
@@ -338,8 +332,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const movedConnection = <ConnectionProfile>e.source;
const oldParent = <ConnectionProfileGroup>this._tree.getElementById(e.oldGroupId);
const newParent = <ConnectionProfileGroup>this._tree.getElementById(e.newGroupId);
// Storing the expanded state of children of the moved connection so that they can be expanded after the move.
const profileExpandedState = this._tree.getExpandedState(movedConnection);
if (oldParent) {
oldParent.removeConnections([movedConnection]);
await this._tree.updateChildren(oldParent);
@@ -351,12 +343,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
await this._tree.updateChildren(newParent);
await this._tree.expand(newParent);
}
await this.refreshConnectionTreeTitles();
const newConnection = this._tree.getElementById(movedConnection.id);
if (newConnection) {
await this._tree.revealSelectFocusElement(newConnection);
// Expanding the previously expanded children of the moved connection after the move.
await this._tree.expandElements(profileExpandedState);
}
}
}));
@@ -366,7 +355,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const parent = <ConnectionProfileGroup>this._tree.getElementById(e.parentId);
parent.children = parent.children.filter(c => c.id !== e.id);
await this._tree.updateChildren(parent);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(parent);
}
}));
@@ -389,7 +377,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
e.parent = parent;
e.parentId = parent.id;
await this._tree.updateChildren(parent);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(e);
}
}));
@@ -400,7 +387,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
if (newParent) {
newParent.children[newParent.children.findIndex(c => c.id === e.id)] = e;
await this._tree.updateChildren(newParent);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(e);
}
}
@@ -411,18 +397,13 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
const movedGroup = <ConnectionProfileGroup>e.source;
const oldParent = <ConnectionProfileGroup>this._tree.getElementById(e.oldGroupId);
const newParent = <ConnectionProfileGroup>this._tree.getElementById(e.newGroupId);
// Storing the expanded state of children of the moved group so that they can be expanded after the move.
const profileExpandedState = this._tree.getExpandedState(movedGroup);
oldParent.children = oldParent.children.filter(c => c.id !== movedGroup.id);
await this._tree.updateChildren(oldParent);
newParent.children.push(movedGroup);
(<ConnectionProfileGroup>movedGroup).parent = newParent;
(<ConnectionProfileGroup>movedGroup).parentId = newParent.id;
await this._tree.updateChildren(newParent);
await this.refreshConnectionTreeTitles();
await this._tree.revealSelectFocusElement(movedGroup);
// Expanding the previously expanded children of the moved group after the move.
this._tree.expandElements(profileExpandedState);
}
}));
@@ -706,7 +687,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return;
}
await this._tree.setInput(treeInput!);
await this.refreshConnectionTreeTitles();
if (isHidden(this.messages!)) {
this._tree.getFocus();
if (this._tree instanceof AsyncServerTree) {
@@ -972,13 +952,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
return actionContext;
}
private async refreshConnectionTreeTitles(): Promise<void> {
let treeInput = this._tree.getInput();
let treeArray = TreeUpdateUtils.alterTreeChildrenTitles([treeInput], this._connectionManagementService, false);
treeInput = treeArray[0];
await this._tree!.setInput(treeInput);
}
public collapseAllConnections(): void {
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService)!;
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);

View File

@@ -19,6 +19,7 @@ import { IConnectionDialogService } from 'sql/workbench/services/connection/comm
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IFileService } from 'vs/platform/files/common/files';
CommandsRegistry.registerCommand({
id: 'profiler.newProfiler',
@@ -107,9 +108,10 @@ CommandsRegistry.registerCommand({
const editorService: IEditorService = accessor.get(IEditorService);
const fileDialogService: IFileDialogService = accessor.get(IFileDialogService);
const profilerService: IProfilerService = accessor.get(IProfilerService);
const instantiationService: IInstantiationService = accessor.get(IInstantiationService)
const instantiationService: IInstantiationService = accessor.get(IInstantiationService);
const fileService: IFileService = accessor.get(IFileService);
const result = await profilerService.openFile(fileDialogService, editorService, instantiationService);
const result = await profilerService.openFile(fileDialogService, editorService, instantiationService, fileService);
return result;
}

View File

@@ -770,7 +770,27 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
}
},
getActionsContext: () => (<TreeViewItemHandleArg>{ $treeViewId: this.id, $treeItemHandle: node.handle, $treeItem: node }),
getActionsContext: () => {
// This context is used in two ways - one is for the original Data Explorer tree view that extensions can register. Items
// for the tree view itself don't have a childProvider so are routed directly to the extension. In this case we want to have
// the actions behave like VS Code expects them to behave - so we don't pass in a $treeItem at all so it gets handled by the
// VS Code logic here :
// https://github.com/microsoft/azuredatastudio/blob/f5bed289affca2fce04fee6dd2db3ce02a8f5c83/src/vs/workbench/api/common/extHostTreeViews.ts#L65
// The other is for the "childProvider" nodes - which come from a registered provider directly. These currently expect that
// the node (with its payload) is passed, which gets handled by processors such as https://github.com/microsoft/azuredatastudio/blob/f5bed289affca2fce04fee6dd2db3ce02a8f5c83/src/sql/workbench/api/common/extHostObjectExplorer.ts#L27
// This should be a temporary fix until further investigation can be done and a fix to the core data explorer logic can be made
// (which will likely involve that logic using the treeItemHandle to look up the treeItem itself like VS Code seems to be doing)
// Have to remove parent, this was added in a VS Code merge to ITreeItem instances, but isn't meant to be serializable over JSON RPC,
// so passing it directly will cause the command to fail as there's a loop (child -> parent -> child -> parent ...)
// Note: Do NOT make a new object here - the core tree logic expects the tree items to stay as the same object so returning a
// different object here will break functionality such as scripting.
delete node.parent;
const treeItem = node.childProvider ? node : undefined;
return (<TreeViewItemHandleArg>{ $treeViewId: this.id, $treeItemHandle: node.handle, $treeItem: treeItem })
},
actionRunner
});

View File

@@ -61,10 +61,6 @@ export class SingleConnectionManagementService {
public get connectionInfo(): ConnectionManagementInfo {
return this._connectionService.getConnectionInfo(this._uri);
}
public getEditorConnectionProfileTitle(profile: IConnectionProfile, getOptionsOnly?: boolean, includeGroupName?: boolean): string {
return this._connectionService.getEditorConnectionProfileTitle(profile, getOptionsOnly, includeGroupName);
}
}
export class SingleAdminService {

View File

@@ -553,12 +553,7 @@ class SavedConnectionNode {
}
getChildren() {
let input = TreeUpdateUtils.getTreeInput(this.connectionManagementService);
let newInput = [input];
if (input instanceof ConnectionProfileGroup) {
newInput = TreeUpdateUtils.alterTreeChildrenTitles([input], this.connectionManagementService);
}
return this.dataSource.getChildren(newInput[0]);
return this.dataSource.getChildren(TreeUpdateUtils.getTreeInput(this.connectionManagementService));
}
}

View File

@@ -682,8 +682,8 @@ export class ConnectionManagementService extends Disposable implements IConnecti
connection.options = connectionErrorHandleResult.options;
}
if (connectionErrorHandleResult.reconnect) {
// Attempt reconnect if requested by provider
return this.connectWithOptions(connection, uri, options, callbacks);
// Attempt reconnect if requested by provider and reset URI to be regenerated.
return this.connectWithOptions(connection, undefined, options, callbacks);
} else {
if (callbacks.onConnectCanceled) {
callbacks.onConnectCanceled();
@@ -724,183 +724,6 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return result;
}
public getEditorConnectionProfileTitle(profile: interfaces.IConnectionProfile, getOptionsOnly?: boolean, includeGroupName: boolean = true): string {
let result = '';
if (profile) {
let tempProfile = new ConnectionProfile(this._capabilitiesService, profile);
let trimTitle = tempProfile.getOriginalTitle();
let idToFind = tempProfile.id;
let isChild = false;
let totalConnections: ConnectionProfile[] = [];
let allConnections = this.getConnections();
totalConnections = totalConnections.concat(allConnections);
let initialSearch = totalConnections.filter(inputProfile => {
return inputProfile.id === tempProfile.id;
});
let secondarySearch = totalConnections.filter(inputProfile => {
return inputProfile.matches(tempProfile)
});
if (initialSearch.length === 0) {
if (secondarySearch.length === 1) {
// Sometimes the connection id will change for an object explorer connection, especially when connecting from dashboard,
// and it will identify as different from the stored one even without changes. Get the info for the stored version as it's the same profile.
idToFind = secondarySearch[0].id;
}
}
// Handle case where a profile may have been an edited existing connection (the one retrieved may not be up to date)
if (initialSearch.length === 1 && !initialSearch[0].matches(tempProfile)) {
// Remove the old profile with outdated information.
totalConnections = totalConnections.filter(inputProfile => {
return inputProfile.id !== tempProfile.id;
});
// Replace with up to date version of the profile.
totalConnections = totalConnections.concat(tempProfile);
}
let newConnectionTitles = [];
this.generateEditorConnectionTitles(totalConnections);
if (includeGroupName) {
this.appendGroupName(totalConnections);
}
newConnectionTitles = totalConnections;
let searchResult = newConnectionTitles.filter(inputProfile => inputProfile.id === idToFind);
let finalTitle = searchResult[0]?.title;
if (finalTitle) {
let optionsAppend = finalTitle.substring(trimTitle.length);
if (getOptionsOnly) {
finalTitle = optionsAppend;
}
else if (!getOptionsOnly && isChild) {
finalTitle = tempProfile.getOriginalTitle() + optionsAppend;
}
else {
finalTitle = searchResult[0].getOriginalTitle() + optionsAppend;
}
result = finalTitle;
}
}
return result;
}
/**
* Change the connection title to display only the unique properties among profiles provided. (used for editors)
*/
private generateEditorConnectionTitles(inputList: ConnectionProfile[]): void {
let profileListMap = new Map<string, number[]>();
// Need to reset title to when it was before (as it may have contained a previously generated title)
for (let i = 0; i < inputList.length; i++) {
inputList[i].title = inputList[i].getOriginalTitle();
}
// Map the indices of profiles that share the same server info
for (let i = 0; i < inputList.length; i++) {
// do not add if the profile is still loading as that will result in erroneous entries.
if (inputList[i].serverCapabilities && inputList[i].hasLoaded()) {
let titleKey = inputList[i].getOriginalTitle();
if (profileListMap.has(titleKey)) {
let profilesForKey = profileListMap.get(titleKey);
profilesForKey.push(i);
profileListMap.set(titleKey, profilesForKey);
}
else {
profileListMap.set(titleKey, [i]);
}
}
}
profileListMap.forEach(function (indexes, titleString) {
if (profileListMap.get(titleString)?.length > 1) {
let combinedOptions = [];
let needSpecial = false;
if (titleString === inputList[indexes[0]].connectionName) {
// check for potential connections with the same name but technically different connections.
let listOfDuplicates = indexes.filter(item => inputList[item].getOptionsKey() !== inputList[indexes[0]].getOptionsKey());
if (listOfDuplicates.length > 0) {
// if we do find duplicates, we will need to include the special properties.
needSpecial = true;
}
}
indexes.forEach((indexValue) => {
// Add all possible options across all profiles with the same title to an option list.
let valueOptions = inputList[indexValue].getConnectionOptionsList(needSpecial, false);
combinedOptions = combinedOptions.concat(valueOptions.filter(item => combinedOptions.indexOf(item) < 0));
});
// Generate list of non default option keys for each profile that shares the same server info.
let optionKeyMap = new Map<ConnectionProfile, string[]>();
let optionValueOccuranceMap = new Map<string, number>();
for (let p = 0; p < indexes.length; p++) {
optionKeyMap.set(inputList[indexes[p]], []);
for (let i = 0; i < combinedOptions.length; i++) {
// See if the option is not default for the inputList profile or is.
if (inputList[indexes[p]].getConnectionOptionsList(needSpecial, true).indexOf(combinedOptions[i]) > -1) {
let optionValue = inputList[indexes[p]].getOptionValue(combinedOptions[i].name);
let currentArray = optionKeyMap.get(inputList[indexes[p]]);
let valueString = combinedOptions[i].name + ConnectionProfile.displayNameValueSeparator + optionValue;
if (!optionValueOccuranceMap.get(valueString)) {
optionValueOccuranceMap.set(valueString, 0);
}
optionValueOccuranceMap.set(valueString, optionValueOccuranceMap.get(valueString) + 1);
currentArray.push(valueString);
optionKeyMap.set(inputList[indexes[p]], currentArray);
}
}
}
// Filter out options that are found in ALL the entries with the same server info.
optionValueOccuranceMap.forEach(function (count, optionValue) {
optionKeyMap.forEach(function (connOptionValues, profile) {
if (count === optionKeyMap.size) {
optionKeyMap.set(profile, connOptionValues.filter(value => value !== optionValue));
}
});
});
// Generate the final unique connection string for each profile in the list.
optionKeyMap.forEach(function (connOptionValues, profile) {
let uniqueOptionString = connOptionValues.join(ConnectionProfile.displayIdSeparator);
if (uniqueOptionString.length > 0) {
profile.title = profile.getOriginalTitle() + ' (' + uniqueOptionString + ')';
}
});
}
});
}
private appendGroupName(inputList: ConnectionProfile[]): void {
let profileListMap = new Map<string, number[]>();
// Map the indices of profiles that share the same server group
for (let i = 0; i < inputList.length; i++) {
let groupName = inputList[i].groupFullName;
if (profileListMap.has(groupName)) {
let profilesForKey = profileListMap.get(groupName);
profilesForKey.push(i);
profileListMap.set(groupName, profilesForKey);
}
else {
profileListMap.set(groupName, [i]);
}
}
if (profileListMap.size > 1) {
profileListMap.forEach(function (indexes, groupName) {
for (let t = 0; t < indexes.length; t++) {
if (groupName !== '') {
inputList[indexes[t]].title += nls.localize('connection.connTitleGroupSection', ' (Group: {0})', groupName);
}
}
});
}
}
private doActionsAfterConnectionComplete(uri: string, options: IConnectionCompletionOptions): void {
let connectionManagementInfo = this._connectionStatusManager.findConnection(uri);
if (!connectionManagementInfo) {
@@ -1368,16 +1191,22 @@ export class ConnectionManagementService extends Disposable implements IConnecti
});
await this._extensionService.activateByEvent(`onConnect:${connection.providerName}`);
if (this._providers.get(connection.providerName) === undefined) {
await this.handleUnsupportedProvider(connection.providerName);
throw new Error(nls.localize('connection.providerNotFound', "Connection provider '{0}' not found", connection.providerName));
}
return this._providers.get(connection.providerName).onReady.then((provider) => {
provider.connect(uri, connectionInfo);
this._onConnectRequestSent.fire();
// Connections are made per URI so while there may possibly be multiple editors with
// that URI they all share the same state
const editor = this._editorService.findEditors(URI.parse(uri))[0]?.editor;
// TODO make this generic enough to handle non-SQL languages too
const language = editor instanceof QueryEditorInput && editor.state.isSqlCmdMode ? 'sqlcmd' : 'sql';
this.doChangeLanguageFlavor(uri, language, connection.providerName);
const editors = this._editorService.findEditors(URI.parse(uri));
if (editors && editors[0]?.editor) {
const editor = editors[0].editor;
// TODO make this generic enough to handle non-SQL languages too
const language = editor instanceof QueryEditorInput && editor.state.isSqlCmdMode ? 'sqlcmd' : 'sql';
this.doChangeLanguageFlavor(uri, language, connection.providerName);
}
return true;
});
}
@@ -1602,13 +1431,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
return;
}
this._connectionStatusManager.changeConnectionUri(newUri, oldUri);
if (!this._uriToProvider[oldUri]) {
this._logService.error(`No provider found for old URI : '${oldUri}'`);
throw new Error(nls.localize('connectionManagementService.noProviderForUri', 'Could not find provider for uri: {0}', oldUri));
if (this._uriToProvider[oldUri]) {
// Provider will persist after disconnect, it is okay to overwrite the map if it exists from a previously deleted connection.
this._uriToProvider[newUri] = this._uriToProvider[oldUri];
delete this._uriToProvider[oldUri];
}
// Provider will persist after disconnect, it is okay to overwrite the map if it exists from a previously deleted connection.
this._uriToProvider[newUri] = this._uriToProvider[oldUri];
delete this._uriToProvider[oldUri];
}
/**
@@ -1644,7 +1471,11 @@ export class ConnectionManagementService extends Disposable implements IConnecti
});
// send connection request
self.sendConnectRequest(connection, uri).catch((e) => this._logService.error(e));
self.sendConnectRequest(connection, uri).catch((e) => {
this._logService.error(e);
this._connectionStatusManager.removeConnection(uri);
reject(e);
});
});
}

View File

@@ -16,7 +16,7 @@ import * as Constants from 'sql/platform/connection/common/constants';
import * as Utils from 'sql/platform/connection/common/utils';
import { IHandleFirewallRuleResult } from 'sql/workbench/services/resourceProvider/common/resourceProviderService';
import { IConnectionProfile, ServiceOptionType } from 'sql/platform/connection/common/interfaces';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { TestConnectionProvider } from 'sql/platform/connection/test/common/testConnectionProvider';
import { TestResourceProvider } from 'sql/workbench/services/resourceProvider/test/common/testResourceProviderService';
@@ -2046,184 +2046,6 @@ suite('SQL ConnectionManagementService tests', () => {
});
});
// TODO - need to rework test to match new format.
test.skip('getEditorConnectionProfileTitle should return a correctly formatted title for a connection profile', () => {
let profile: IConnectionProfile = {
connectionName: 'new name',
serverName: 'new server',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: Constants.AuthenticationType.Integrated,
savePassword: true,
groupFullName: 'g2/g2-2',
groupId: 'group id',
serverCapabilities: undefined,
getOptionsKey: () => { return ''; },
getOptionKeyIdNames: undefined!,
matches: undefined,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined
};
let capabilitiesService = new TestCapabilitiesService();
const testOption1 = {
name: 'testOption1',
displayName: 'testOption1',
description: 'test description',
groupName: 'test group name',
valueType: ServiceOptionType.string,
specialValueType: undefined,
defaultValue: '',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
const testOption2 = {
name: 'testOption2',
displayName: 'testOption2',
description: 'test description',
groupName: 'test group name',
valueType: ServiceOptionType.number,
specialValueType: undefined,
defaultValue: '10',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
const testOption3 = {
name: 'testOption3',
displayName: 'testOption3',
description: 'test description',
groupName: 'test group name',
valueType: ServiceOptionType.string,
specialValueType: undefined,
defaultValue: 'default',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
profile.options['testOption1'] = 'test value';
profile.options['testOption2'] = '50';
profile.options['testOption3'] = 'default';
let mainProvider = capabilitiesService.capabilities['MSSQL'];
let mainProperties = mainProvider.connection;
let mainOptions = mainProperties.connectionOptions;
mainOptions.push(testOption1);
mainOptions.push(testOption2);
mainOptions.push(testOption3);
mainProperties.connectionOptions = mainOptions;
mainProvider.connection = mainProperties;
capabilitiesService.capabilities['MSSQL'] = mainProvider;
const connectionStoreMock = TypeMoq.Mock.ofType(ConnectionStore, TypeMoq.MockBehavior.Loose, new TestStorageService());
const connectionStatusManagerMock = TypeMoq.Mock.ofType(ConnectionStatusManager, TypeMoq.MockBehavior.Loose);
connectionStatusManagerMock.setup(x => x.getActiveConnectionProfiles(undefined)).returns(() => {
return [];
});
const testInstantiationService = new TestInstantiationService();
testInstantiationService.stub(IStorageService, new TestStorageService());
sinon.stub(testInstantiationService, 'createInstance').withArgs(ConnectionStore).returns(connectionStoreMock.object).withArgs(ConnectionStatusManager).returns(connectionStatusManagerMock.object);
const connectionManagementService = new ConnectionManagementService(undefined, testInstantiationService, undefined, undefined, undefined, capabilitiesService, undefined, undefined, undefined, new TestErrorDiagnosticsService(), undefined, undefined, undefined, undefined, getBasicExtensionService(), undefined, undefined, undefined);
// We should expect that options by themselves are empty if no other profiles exist.
let result = connectionManagementService.getEditorConnectionProfileTitle(profile, true);
assert.strictEqual(result, '', `Options appeared when they should not have.`);
// We should expect that the string contains only the server info (basic) if there is no other connection with the same server info.
result = connectionManagementService.getEditorConnectionProfileTitle(profile);
let generatedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
let expectedNonDefaultOption = ' (testOption1=test value; testOption2=50)';
let profileServerInfo = generatedProfile.serverInfo.substring(0, generatedProfile.serverInfo.indexOf(expectedNonDefaultOption));
assert.strictEqual(result, `${profileServerInfo}`, `getEditorConnectionProfileTitle included a connection name when it shouldn't`);
connectionStoreMock.setup(x => x.getAllConnectionsFromConfig()).returns(() => {
return [generatedProfile];
});
// We should expect that the string only contains the server info (basic) if there is only default options, and another connection with similar title but non default options.
profile.options['testOption1'] = undefined;
profile.options['testOption2'] = undefined;
profile.options['testOption3'] = undefined;
result = connectionManagementService.getEditorConnectionProfileTitle(profile);
assert.strictEqual(result, `${profileServerInfo}`, `getEditorConnectionProfileTitle included differing connection options when it shouldn't`);
//Reset profiles for next test and add secondary profile .
profile.options['testOption1'] = 'test value';
profile.options['testOption2'] = '50';
profile.options['testOption3'] = 'default';
generatedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
profile.options['testOption1'] = undefined;
profile.options['testOption2'] = undefined;
profile.options['testOption3'] = undefined;
let emptyGeneratedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
connectionStoreMock.setup(x => x.getAllConnectionsFromConfig()).returns(() => {
return [generatedProfile, emptyGeneratedProfile];
});
// We should expect that the string contains the server info appended with differing options, if there's another connection with similar title that has only default options.
result = connectionManagementService.getEditorConnectionProfileTitle(generatedProfile);
assert.equal(result, `${generatedProfile.serverInfo}`, `getEditorConnectionProfileTitle did not include differing connection options when it should`);
connectionStoreMock.setup(x => x.getAllConnectionsFromConfig()).returns(() => {
return [generatedProfile, emptyGeneratedProfile];
});
// We should expect that the string contains only the differing options when we ask for options only
result = connectionManagementService.getEditorConnectionProfileTitle(generatedProfile, true);
assert.equal(result, expectedNonDefaultOption, `getEditorConnectionProfileTitle did not return differing options only`);
//Reset profiles for next test and add secondary profile .
profile.options['testOption1'] = 'test value';
profile.options['testOption2'] = '50';
profile.options['testOption3'] = 'default';
profile.connectionName = 'New Connection Name';
profile.options['connectionName'] = profile.connectionName;
generatedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
profile.options['testOption1'] = undefined;
profile.options['testOption2'] = undefined;
profile.options['testOption3'] = undefined;
profile.connectionName = 'new name';
profile.options['connectionName'] = profile.connectionName;
emptyGeneratedProfile = ConnectionProfile.fromIConnectionProfile(capabilitiesService, profile);
expectedNonDefaultOption = ' (connectionName=New Connection Name; testOption1=test value; testOption2=50)';
connectionStoreMock.setup(x => x.getAllConnectionsFromConfig()).returns(() => {
return [generatedProfile, emptyGeneratedProfile];
});
// We should expect that the string now contains connectionName, when it is different.
result = connectionManagementService.getEditorConnectionProfileTitle(generatedProfile, false);
assert.equal(result, `${profileServerInfo}${expectedNonDefaultOption}`, `getEditorConnectionProfileTitle did not include connectionName in options when it should`);
connectionStoreMock.setup(x => x.getAllConnectionsFromConfig()).returns(() => {
return [generatedProfile, emptyGeneratedProfile];
});
// We should expect that the string contains only the differing options (including Connection Name) against server info when we ask for options only.
result = connectionManagementService.getEditorConnectionProfileTitle(generatedProfile, true);
assert.equal(result, expectedNonDefaultOption, `getEditorConnectionProfileTitle did not include only options with connectionName`);
});
export function createConnectionProfile(id: string, password?: string): ConnectionProfile {
const capabilitiesService = new TestCapabilitiesService();
return new ConnectionProfile(capabilitiesService, {

View File

@@ -138,7 +138,8 @@ export class DialogModal extends Modal {
if (this._doneButton.enabled) {
let buttonSpinnerHandler = setTimeout(() => {
this._doneButton.enabled = false;
this._doneButton.element.innerHTML = '&nbsp';
// Temporarily set the label to empty since we're showing a spinner instead
this._doneButton.label = ''
this._doneButton.element.classList.add('validating');
}, 100);
if (await this._dialog.validateClose()) {

View File

@@ -319,7 +319,8 @@ export class WizardModal extends Modal {
let button = newPage === undefined ? this._doneButton : this._nextButton;
let buttonSpinnerHandler = setTimeout(() => {
button.enabled = false;
button.element.innerHTML = '&nbsp';
// Temporarily set the label to empty since we're showing a spinner instead
button.label = '';
button.element.classList.add('validating');
}, 100);
let navigationValid = await this._wizard.validateNavigation(newPage);

View File

@@ -114,8 +114,8 @@ export class FileBrowserDialog extends Modal {
fileValidationServiceType: string,
): void {
this._viewModel.initialize(ownerUri, expandPath, fileFilters, fileValidationServiceType);
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters);
this._fileFilterSelectBox.select(0);
this._viewModel.openFileBrowser(0, false).catch(err => onUnexpectedError(err));
this._fileFilterSelectBox.setOptions(this._viewModel.formattedFileFilters, 0);
this._filePathInputBox.value = expandPath;
this._isFolderSelected = true;
this.enableOkButton();
@@ -125,7 +125,6 @@ export class FileBrowserDialog extends Modal {
this._fileBrowserTreeView = this._instantiationService.createInstance(FileBrowserTreeView);
this._fileBrowserTreeView.setOnClickedCallback((arg) => this.onClicked(arg));
this._fileBrowserTreeView.setOnDoubleClickedCallback((arg) => this.onDoubleClicked(arg));
this._viewModel.openFileBrowser(0, false).catch(err => onUnexpectedError(err));
}
/* enter key */

View File

@@ -120,19 +120,10 @@ export class AsyncServerTree extends WorkbenchAsyncDataTree<ConnectionProfileGro
return node;
}
public override async updateChildren(element?: ServerTreeElement, recursive?: boolean, rerender?: boolean, options?: IAsyncDataTreeUpdateChildrenOptions<ServerTreeElement>): Promise<void> {
const expandedChildren = this.getExpandedState(element);
public override async updateChildren(element?: ServerTreeElement, recursive: boolean = false, rerender: boolean = false, options: IAsyncDataTreeUpdateChildrenOptions<ServerTreeElement> = {
diffDepth: 0
}): Promise<void> {
await super.updateChildren(element, recursive, rerender, options);
await this.expandElements(expandedChildren);
}
public async expandElements(elements: ServerTreeElement[]): Promise<void> {
for (let element of elements) {
const node = this.getDataNode(element, false);
if (node) {
await this.expand(node.element);
}
}
}
/**

View File

@@ -149,7 +149,7 @@ class ConnectionProfileTemplate extends Disposable {
this._label.element.setLabel(labelText, '', {
matches: createMatches(filterData)
});
this._root.title = labelText;
this._root.title = treeNode?.filters?.length > 0 ? getLabelWithFilteredSuffix(element.serverInfo) : element.serverInfo;
const actionProvider = this._objectExplorerService.getServerTreeView().treeActionProvider;
if (!this._isCompact) {
const tree = this._objectExplorerService.getServerTreeView().tree;

View File

@@ -235,7 +235,7 @@ export class ServerTreeRenderer implements IRenderer {
const treeNode = this._objectExplorerService.getObjectExplorerNode(connection);
let label = treeNode?.filters?.length > 0 ? getLabelWithFilteredSuffix(connection.title) : connection.title;
templateData.label.textContent = label;
templateData.root.title = treeNode?.filters?.length > 0 ? getLabelWithFilteredSuffix(connection.title) : connection.serverInfo;
templateData.root.title = treeNode?.filters?.length > 0 ? getLabelWithFilteredSuffix(connection.serverInfo) : connection.serverInfo;
templateData.connectionProfile = connection;
}
}

View File

@@ -128,7 +128,10 @@ export class TreeSelectionHandler {
}
const selectedNode = selection[0];
if (selectedNode instanceof ConnectionProfile && !capabilitiesService.getCapabilities(selectedNode.providerName)) {
connectionManagementService.handleUnsupportedProvider(selectedNode.providerName).catch(onUnexpectedError);
// Async tree handles unsupported providers through the connection management service
if (!(tree instanceof AsyncServerTree)) {
connectionManagementService.handleUnsupportedProvider(selectedNode.providerName).catch(onUnexpectedError);
}
return;
}

View File

@@ -47,21 +47,6 @@ export class TreeUpdateUtils {
public static isInDragAndDrop: boolean = false;
/**
* Functions to restore/remove the groupId for title generation as they are removed when added to treeInput
*/
private static restoreGroupId(treeInput: ConnectionProfileGroup, originalProfiles: ConnectionProfile[]) {
for (let i = 0; i < treeInput.connections.length; i++) {
treeInput.connections[i].groupId = originalProfiles[i].groupId
}
}
private static removeGroupId(treeInput: ConnectionProfileGroup) {
for (let i = 0; i < treeInput.connections.length; i++) {
treeInput.connections[i].groupId = undefined;
}
}
/**
* Set input for the tree.
*/
@@ -83,21 +68,11 @@ export class TreeUpdateUtils {
if (viewKey === 'recent') {
groups = connectionManagementService.getRecentConnections(providers);
treeInput.addConnections(groups);
this.restoreGroupId(treeInput, connectionManagementService.getRecentConnections(providers));
let treeArray = TreeUpdateUtils.alterTreeChildrenTitles([treeInput], connectionManagementService);
this.removeGroupId(treeInput);
treeInput = treeArray[0];
} else if (viewKey === 'active') {
groups = connectionManagementService.getActiveConnections(providers);
treeInput.addConnections(groups);
this.restoreGroupId(treeInput, connectionManagementService.getActiveConnections(providers));
let treeArray = TreeUpdateUtils.alterTreeChildrenTitles([treeInput], connectionManagementService);
this.removeGroupId(treeInput);
treeInput = treeArray[0];
} else if (viewKey === 'saved') {
treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService, providers);
let treeArray = TreeUpdateUtils.alterTreeChildrenTitles([treeInput], connectionManagementService);
treeInput = treeArray[0];
}
const previousTreeInput = tree.getInput();
if (treeInput) {
@@ -118,29 +93,13 @@ export class TreeUpdateUtils {
}
}
/**
* Calls alterConnectionTitles on all levels of the Object Explorer Tree
* so that profiles in connection groups can have distinguishing titles too.
*/
public static alterTreeChildrenTitles(inputGroups: ConnectionProfileGroup[], connectionManagementService: IConnectionManagementService, includeGroupName?: boolean): ConnectionProfileGroup[] {
inputGroups.forEach(group => {
group.children = TreeUpdateUtils.alterTreeChildrenTitles(group.children, connectionManagementService, includeGroupName);
let connections = group.connections;
TreeUpdateUtils.alterConnectionTitles(connections, connectionManagementService, includeGroupName);
group.connections = connections;
});
return inputGroups;
}
/**
* Set input for the registered servers tree.
*/
public static async registeredServerUpdate(tree: ITree | AsyncServerTree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): Promise<void> {
if (tree instanceof AsyncServerTree) {
let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
const treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
if (treeInput) {
let treeArray = this.alterTreeChildrenTitles([treeInput], connectionManagementService, false);
treeInput = treeArray[0];
await tree.setInput(treeInput);
}
tree.rerender();
@@ -169,8 +128,6 @@ export class TreeUpdateUtils {
let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
if (treeInput) {
let treeArray = TreeUpdateUtils.alterTreeChildrenTitles([treeInput], connectionManagementService, false);
treeInput = treeArray[0];
const originalInput = tree.getInput();
if (treeInput !== originalInput) {
return tree.setInput(treeInput).then(async () => {
@@ -413,14 +370,4 @@ export class TreeUpdateUtils {
}
return connectionProfile;
}
private static alterConnectionTitles(inputList: ConnectionProfile[], connectionManagementService: IConnectionManagementService, includeGroupName?: boolean): void {
for (let i = 0; i < inputList.length; i++) {
let currentConnection = inputList[i];
let listOfDuplicates = inputList.filter(connection => connection.getOriginalTitle() === currentConnection.getOriginalTitle());
if (listOfDuplicates.length > 1) {
inputList[i].title = connectionManagementService.getEditorConnectionProfileTitle(inputList[i], false, includeGroupName);
}
}
}
}

View File

@@ -1,645 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
import * as assert from 'assert';
import * as azdata from 'azdata';
// TODO - Need to fix these tests to match the refactoring of the Connection Title Generation.
suite.skip('treeUpdateUtils alterConnection', () => {
let capabilitiesService: TestCapabilitiesService;
const testOption1 = {
name: 'testOption1',
displayName: 'testOption1',
description: 'test description',
groupName: 'test group name',
valueType: 'string',
specialValueType: undefined,
defaultValue: '',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
const testOption2 = {
name: 'testOption2',
displayName: 'testOption2',
description: 'test description',
groupName: 'test group name',
valueType: 'number',
specialValueType: undefined,
defaultValue: '10',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
const testOption3 = {
name: 'testOption3',
displayName: 'testOption3',
description: 'test description',
groupName: 'test group name',
valueType: 'string',
specialValueType: undefined,
defaultValue: 'default',
categoryValues: undefined,
isIdentity: false,
isRequired: false
};
setup(() => {
capabilitiesService = new TestCapabilitiesService();
let mainProvider = capabilitiesService.capabilities[mssqlProviderName];
let mainProperties = mainProvider.connection;
let mainOptions = mainProperties.connectionOptions;
mainOptions.push((testOption1 as azdata.ConnectionOption));
mainOptions.push((testOption2 as azdata.ConnectionOption));
mainOptions.push((testOption3 as azdata.ConnectionOption));
mainProperties.connectionOptions = mainOptions;
mainProvider.connection = mainProperties;
capabilitiesService.capabilities['MSSQL'] = mainProvider;
});
test('Default properties should not be added to the altered title', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption3: 'default', testOption2: '10' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1 = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2 = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
connectionProfileGroup.addConnections([connectionProfile1, connectionProfile2]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
assert.equal(connectionProfile1.title, updatedTitleMap[0]);
assert.equal(connectionProfile1.title + ' (testOption3=nonDefault)', updatedTitleMap[1]);
});
test('Similar connections should have different titles based on all differing properties', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption2: '15', testOption1: 'test string 1', testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption2: '50', testOption1: 'test string 1', testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile3: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption2: '15', testOption1: 'test string 2', testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile4: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption2: '50', testOption1: 'test string 2', testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let defaultProfile: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption3: 'nonDefault' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let defaultConnectionProfile = new ConnectionProfile(capabilitiesService, defaultProfile);
let connectionProfile1 = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2 = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfile3 = new ConnectionProfile(capabilitiesService, profile3);
let connectionProfile4 = new ConnectionProfile(capabilitiesService, profile4);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let originalTitle = defaultConnectionProfile.title;
connectionProfileGroup.addConnections([defaultConnectionProfile, connectionProfile1, connectionProfile2, connectionProfile3, connectionProfile4]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
assert.equal(originalTitle, updatedTitleMap[0]);
assert.equal(originalTitle + ' (testOption1=test string 1; testOption2=15)', updatedTitleMap[1]);
assert.equal(originalTitle + ' (testOption1=test string 1; testOption2=50)', updatedTitleMap[2]);
assert.equal(originalTitle + ' (testOption1=test string 2; testOption2=15)', updatedTitleMap[3]);
assert.equal(originalTitle + ' (testOption1=test string 2; testOption2=50)', updatedTitleMap[4]);
});
test('identical connections should have same title if on different levels', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3-1',
groupId: 'g3-1',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile3: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3-2',
groupId: 'g3-2',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1 = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2 = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfile3 = new ConnectionProfile(capabilitiesService, profile3);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let childConnectionProfileGroup = new ConnectionProfileGroup('g3-1', undefined, 'g3-1', undefined, undefined);
let grandChildConnectionProfileGroup = new ConnectionProfileGroup('g3-2', undefined, 'g3-2', undefined, undefined);
childConnectionProfileGroup.addConnections([connectionProfile2]);
connectionProfileGroup.addConnections([connectionProfile1]);
grandChildConnectionProfileGroup.addConnections([connectionProfile3]);
childConnectionProfileGroup.addGroups([grandChildConnectionProfileGroup]);
connectionProfileGroup.addGroups([childConnectionProfileGroup]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
let updatedChildTitleMap = updatedProfileGroup[0].children[0].connections.map(profile => profile.title);
let updatedGrandChildTitleMap = updatedProfileGroup[0].children[0].children[0].connections.map(profile => profile.title);
// Titles should be the same if they're in different levels.
assert.equal(updatedTitleMap[0], updatedChildTitleMap[0]);
assert.equal(updatedTitleMap[0], updatedGrandChildTitleMap[0]);
assert.equal(updatedChildTitleMap[0], updatedGrandChildTitleMap[0]);
});
test('connections should not affect connections on a different level', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value1' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile1a: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value2' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3-1',
groupId: 'g3-1',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1 = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile1a = new ConnectionProfile(capabilitiesService, profile1a);
let connectionProfile2 = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let childConnectionProfileGroup = new ConnectionProfileGroup('g3-1', undefined, 'g3-1', undefined, undefined);
childConnectionProfileGroup.addConnections([connectionProfile2]);
connectionProfileGroup.addConnections([connectionProfile1, connectionProfile1a]);
connectionProfileGroup.addGroups([childConnectionProfileGroup]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
let updatedChildTitleMap = updatedProfileGroup[0].children[0].connections.map(profile => profile.title);
// Titles should be altered for the first group only.
assert.equal(updatedChildTitleMap[0] + ' (testOption1=value1)', updatedTitleMap[0]);
assert.equal(updatedChildTitleMap[0] + ' (testOption1=value2)', updatedTitleMap[1]);
});
test('non default options should only be appended to the connection with non default options', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value1', testOption2: '15' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1 = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2 = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
connectionProfileGroup.addConnections([connectionProfile1, connectionProfile2]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
//Title for second profile should be the same as the first but with non default options appended.
assert.equal(updatedTitleMap[0] + ' (testOption1=value1; testOption2=15)', updatedTitleMap[1]);
});
test('identical profiles added into one group and separate groups should have the same options appended', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value1', testOption2: '15' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3',
groupId: 'g3',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value2', testOption2: '30' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1Base = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2Base = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
connectionProfileGroup.addConnections([connectionProfile1Base, connectionProfile2Base]);
profile1.groupFullName = 'g3-1';
profile1.groupId = 'g3-1';
profile2.groupFullName = 'g3-1';
profile2.groupId = 'g3-1';
let connectionProfile1Child = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2Child = new ConnectionProfile(capabilitiesService, profile2);
let childConnectionProfileGroup = new ConnectionProfileGroup('g3-1', undefined, 'g3-1', undefined, undefined);
childConnectionProfileGroup.addConnections([connectionProfile1Child, connectionProfile2Child]);
profile1.groupFullName = 'g3-2';
profile1.groupId = 'g3-2';
profile2.groupFullName = 'g3-2';
profile2.groupId = 'g3-2';
let connectionProfile1Grandchild = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2Grandchild = new ConnectionProfile(capabilitiesService, profile2);
let grandchildConnectionProfileGroup = new ConnectionProfileGroup('g3-2', undefined, 'g3-2', undefined, undefined);
grandchildConnectionProfileGroup.addConnections([connectionProfile1Grandchild, connectionProfile2Grandchild]);
childConnectionProfileGroup.addGroups([grandchildConnectionProfileGroup]);
connectionProfileGroup.addGroups([childConnectionProfileGroup]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedTitleMap = updatedProfileGroup[0].connections.map(profile => profile.title);
let updatedChildTitleMap = updatedProfileGroup[0].children[0].connections.map(profile => profile.title);
let updatedGrandchildTitleMap = updatedProfileGroup[0].children[0].children[0].connections.map(profile => profile.title);
//Titles for the same profile in different groups should be identical
assert.equal(updatedTitleMap[0], updatedChildTitleMap[0]);
assert.equal(updatedTitleMap[0], updatedGrandchildTitleMap[0]);
assert.equal(updatedTitleMap[1], updatedChildTitleMap[1]);
assert.equal(updatedTitleMap[1], updatedGrandchildTitleMap[1]);
});
test('profiles in adjacent groups on the same layer should not affect titles on nearby groups', async () => {
let profile1: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3a',
groupId: 'g3a',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: {},
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let profile2: IConnectionProfile = {
serverName: 'server3',
databaseName: 'database',
userName: 'user',
password: 'password',
authenticationType: '',
savePassword: true,
groupFullName: 'g3a',
groupId: 'g3a',
serverCapabilities: undefined,
getOptionsKey: undefined!,
getOptionKeyIdNames: undefined!,
matches: undefined!,
providerName: 'MSSQL',
options: { testOption1: 'value2', testOption2: '30' },
saveProfile: true,
id: undefined!,
connectionName: undefined!
};
let connectionProfile1a = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2a = new ConnectionProfile(capabilitiesService, profile2);
let connectionProfileGroup = new ConnectionProfileGroup('g3', undefined, 'g3', undefined, undefined);
let childConnectionProfileGroup1 = new ConnectionProfileGroup('g3a', undefined, 'g3a', undefined, undefined);
childConnectionProfileGroup1.addConnections([connectionProfile1a, connectionProfile2a]);
profile1.groupFullName = 'g3b';
profile1.groupId = 'g3b';
profile2.groupFullName = 'g3b';
profile2.groupId = 'g3b';
let connectionProfile1b = new ConnectionProfile(capabilitiesService, profile1);
let connectionProfile2b = new ConnectionProfile(capabilitiesService, profile2);
let childConnectionProfileGroup2 = new ConnectionProfileGroup('g3b', undefined, 'g3b', undefined, undefined);
childConnectionProfileGroup2.addConnections([connectionProfile1b, connectionProfile2b]);
connectionProfileGroup.addGroups([childConnectionProfileGroup1]);
connectionProfileGroup.addGroups([childConnectionProfileGroup2]);
let updatedProfileGroup = TreeUpdateUtils.alterTreeChildrenTitles([connectionProfileGroup], undefined);
let updatedChildATitleMap = updatedProfileGroup[0].children[0].connections.map(profile => profile.title);
let updatedChildBTitleMap = updatedProfileGroup[0].children[1].connections.map(profile => profile.title);
//Check that titles are generated properly for the first group.
assert.equal(updatedChildATitleMap[0] + ' (testOption1=value2; testOption2=30)', updatedChildATitleMap[1]);
//Titles for the same profile in adjacent groups should be identical
assert.equal(updatedChildATitleMap[0], updatedChildBTitleMap[0]);
assert.equal(updatedChildATitleMap[1], updatedChildBTitleMap[1]);
});
});

View File

@@ -11,6 +11,7 @@ import * as azdata from 'azdata';
import { INewProfilerState } from 'sql/workbench/common/editor/profiler/profilerState';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IFileService } from 'vs/platform/files/common/files';
const PROFILER_SERVICE_ID = 'profilerService';
export const IProfilerService = createDecorator<IProfilerService>(PROFILER_SERVICE_ID);
@@ -148,7 +149,7 @@ export interface IProfilerService {
* @param editorService service to open profiler editor
* @param instantiationService service to create profiler instance
*/
openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService): Promise<boolean>;
openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService, fileService: IFileService): Promise<boolean>;
}
export enum ProfilingSessionType {

View File

@@ -22,6 +22,7 @@ import { ProfilerFilterDialog } from 'sql/workbench/services/profiler/browser/pr
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { ByteSize, IFileService } from 'vs/platform/files/common/files';
class TwoWayMap<T, K> {
private forwardMap: Map<T, K>;
@@ -303,7 +304,7 @@ export class ProfilerService implements IProfilerService {
await this._configurationService.updateValue(PROFILER_FILTER_SETTINGS, config, ConfigurationTarget.USER);
}
public async openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService): Promise<boolean> {
public async openFile(fileDialogService: IFileDialogService, editorService: IEditorService, instantiationService: IInstantiationService, fileService: IFileService): Promise<boolean> {
const fileURIs = await fileDialogService.showOpenDialog({
filters: [
{
@@ -317,6 +318,21 @@ export class ProfilerService implements IProfilerService {
if (fileURIs?.length === 1) {
const fileURI = fileURIs[0];
try {
const fileSize = (await fileService.stat(fileURI)).size;
const fileLimitSize = 1 * ByteSize.GB;
const fileOpenWarningSize = 100 * ByteSize.MB;
if (fileSize > fileLimitSize) {
this._notificationService.error(nls.localize('FileTooLarge', "The file is too large to open in profiler. The profiler can open files that are less than 1GB."));
return false;
} else if (fileSize > fileOpenWarningSize) {
this._notificationService.info(nls.localize('LargeFileWait', "Loading the file might take a moment due to the file size."));
}
} catch (err) {
this._notificationService.error(err.message);
}
let profilerInput: ProfilerInput = instantiationService.createInstance(ProfilerInput, undefined, fileURI);
await editorService.openEditor(profilerInput, { pinned: true }, ACTIVE_GROUP);
profilerInput.setConnectionState(false); // Reset connection to be not connected for File session, so that "Start" is not enabled.

View File

@@ -49,8 +49,6 @@
"sql/base/browser/ui/panel/panel.ts",
"sql/base/browser/ui/selectBox/selectBox.ts",
"sql/base/browser/ui/panel/panel.component.ts",
"sql/workbench/services/dialog/browser/dialogModal.ts",
"sql/workbench/services/dialog/browser/wizardModal.ts",
"sql/base/browser/ui/taskbar/taskbar.ts",
"sql/workbench/contrib/notebook/browser/outputs/notebookMarkdown.ts",
"sql/workbench/contrib/notebook/browser/cellViews/textCell.component.ts",

View File

@@ -18,6 +18,23 @@ export class TestConfigurationService implements IConfigurationService {
readonly onDidChangeConfiguration = this.onDidChangeConfigurationEmitter.event;
constructor(configuration?: any) {
// {{SQL CARBON EDIT}} - START
// Ensures that all configuration services use the DOM renderer. There's an issue
// with GPU rendering that is causing unit tests to be flaky and obscuring true failing tests.
// This is a temporary fix and should be removed once xterm GPU rendering is working again.
if (configuration) {
if (configuration.integrated) {
configuration.integrated.gpuAcceleration = 'off';
}
else {
configuration.integrated = { gpuAcceleration: 'off' };
}
}
else {
configuration = { integrated: { gpuAcceleration: 'off' } };
}
// {{SQL CARBON EDIT}} - END
this.configuration = configuration || Object.create(null);
}

View File

@@ -63,7 +63,7 @@ export class TestExperimentService extends ExperimentService {
}
}
suite.skip('Experiment Service', () => { // {{SQL CARBON EDIT}} Tests are flaky, and have been removed in VS Code so disabling until we catch up
suite('Experiment Service', () => {
let instantiationService: TestInstantiationService;
let testConfigurationService: TestConfigurationService;
let testObject: ExperimentService;

View File

@@ -36,7 +36,7 @@ const defaultTerminalConfig: Partial<ITerminalConfiguration> = {
unicodeVersion: '6'
};
suite.skip('Buffer Content Tracker', () => { // {{SQL CARBON EDIT}} skip failing suite
suite('Buffer Content Tracker', () => {
let instantiationService: TestInstantiationService;
let configurationService: TestConfigurationService;
let themeService: TestThemeService;

View File

@@ -94,7 +94,7 @@ suite('TerminalLinkManager', () => {
} as Partial<ITerminalCapabilityStore> as any, instantiationService.createInstance(TerminalLinkResolver));
});
suite.skip('getLinks and open recent link', () => { // {{SQL CARBON EDIT}} skip failing suite
suite('getLinks and open recent link', () => {
test('should return no links', async () => {
const links = await linkManager.getLinks();
equals(links.webLinks, []);

View File

@@ -13,7 +13,7 @@ import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices
import { TestExtensionService } from 'vs/workbench/test/common/workbenchTestServices';
suite.skip('Getting Started Markdown Renderer', () => { // {{SQL CARBON EDIT}} - disable suite
suite('Getting Started Markdown Renderer', () => {
test('renders theme picker markdown with images', async () => {
const fileService = new TestFileService();
const languageService = new LanguageService();

View File

@@ -23,7 +23,7 @@ class ConfigurationCache implements IConfigurationCache {
async remove({ type, key }: ConfigurationKey): Promise<void> { this.cache.delete(`${type}:${key}`); }
}
suite.skip('DefaultConfiguration', () => { // {{SQL CARBON EDIT}} skip failing suite
suite('DefaultConfiguration', () => {
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
const cacheKey: ConfigurationKey = { type: 'defaults', key: 'configurationDefaultsOverrides' };

View File

@@ -56,7 +56,7 @@ class ConfigurationCache implements IConfigurationCache {
async remove(): Promise<void> { }
}
suite.skip('ConfigurationEditing', () => { // {{SQL CARBON EDIT}} skip suite
suite('ConfigurationEditing', () => {
let instantiationService: TestInstantiationService;
let userDataProfileService: IUserDataProfileService;

View File

@@ -1518,7 +1518,7 @@ suite.skip('WorkspaceConfigurationService - Folder', () => { // {{SQL CARBON EDI
}));
});
suite.skip('WorkspaceConfigurationService - Profiles', () => { // {{SQL CARBON EDIT}} - skip failing suite
suite('WorkspaceConfigurationService - Profiles', () => {
let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IBrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, instantiationService: TestInstantiationService;
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
@@ -2489,7 +2489,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED
});
suite.skip('WorkspaceConfigurationService - Remote Folder', () => { // {{SQL CARBON EDIT}} - disable suite
suite('WorkspaceConfigurationService - Remote Folder', () => {
let testObject: WorkspaceService, folder: URI,
machineSettingsResource: URI, remoteSettingsResource: URI, fileSystemProvider: InMemoryFileSystemProvider, resolveRemoteEnvironment: () => void,

View File

@@ -58,7 +58,7 @@ const nullContext = {
getExecPath: () => undefined
};
suite.skip('Configuration Resolver Service', () => { // {{SQL CARBON EDIT}} - skip failing suite
suite('Configuration Resolver Service', () => {
let configurationResolverService: IConfigurationResolverService | null;
const envVariables: { [key: string]: string } = { key1: 'Value for key1', key2: 'Value for key2' };
// let environmentService: MockWorkbenchEnvironmentService;

View File

@@ -14,7 +14,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor
import { IEditorResolverService, ResolvedStatus, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService';
import { createEditorPart, ITestInstantiationService, TestFileEditorInput, TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices';
suite.skip('EditorResolverService', () => { // {{SQL CARBON EDIT}} - disable suite
suite('EditorResolverService', () => {
const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorResolverService';
const disposables = new DisposableStore();

View File

@@ -111,7 +111,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService {
}
}
suite.skip('ExtensionEnablementService Test', () => {
suite('ExtensionEnablementService Test', () => {
let instantiationService: TestInstantiationService;
let testObject: IWorkbenchExtensionEnablementService;

View File

@@ -128,7 +128,7 @@ suite('BrowserExtensionService', () => {
});
});
suite.skip('ExtensionService', () => { // {{SQL CARBON EDIT}} - disable failing suite
suite('ExtensionService', () => {
class MyTestExtensionService extends AbstractExtensionService {

View File

@@ -29,7 +29,7 @@ import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
suite.skip('HistoryService', function () { // {{SQL CARBON EDIT}} skip suite
suite('HistoryService', function () {
const TEST_EDITOR_ID = 'MyTestEditorForEditorHistory';
const TEST_EDITOR_INPUT_ID = 'testEditorInputForHistoyService';