diff --git a/build/azure-pipelines/darwin/sql-product-build-darwin.yml b/build/azure-pipelines/darwin/sql-product-build-darwin.yml
index 96c4e861d2..b1d78cff33 100644
--- a/build/azure-pipelines/darwin/sql-product-build-darwin.yml
+++ b/build/azure-pipelines/darwin/sql-product-build-darwin.yml
@@ -113,6 +113,21 @@ steps:
displayName: Run unit tests
condition: and(succeeded(), eq(variables['RUN_TESTS'], 'true'))
+ - script: |
+ set -e
+ APP_ROOT=$(agent.builddirectory)/azuredatastudio-darwin
+ APP_NAME="`ls $APP_ROOT | head -n 1`"
+ yarn smoketest --build "$APP_ROOT/$APP_NAME" --screenshots "$(build.artifactstagingdirectory)/smokeshots"
+ displayName: Run smoke tests (Electron)
+ condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
+
+ - script: |
+ set -e
+ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/azuredatastudio-reh-web-darwin" \
+ yarn smoketest --web --headless --screenshots "$(build.artifactstagingdirectory)/smokeshots"
+ displayName: Run smoke tests (Browser)
+ condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'))
+
- script: |
set -e
pushd ../azuredatastudio-darwin
diff --git a/build/builtin/browser-main.js b/build/builtin/browser-main.js
index fb4a4e28f2..9d52b75fb6 100644
--- a/build/builtin/browser-main.js
+++ b/build/builtin/browser-main.js
@@ -6,7 +6,6 @@
const fs = require('fs');
const path = require('path');
const os = require('os');
-// @ts-ignore review
const { remote } = require('electron');
const dialog = remote.dialog;
diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js
index 3ca8dbb731..948627332f 100644
--- a/build/gulpfile.editor.js
+++ b/build/gulpfile.editor.js
@@ -455,10 +455,8 @@ function createTscCompileTask(watch) {
// e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'.
let fullpath = path.join(root, match[1]);
let message = match[3];
- // @ts-ignore
reporter(fullpath + message);
} else {
- // @ts-ignore
reporter(str);
}
}
diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js
index af59df1239..5c96a4a4b2 100644
--- a/build/gulpfile.vscode.js
+++ b/build/gulpfile.vscode.js
@@ -36,10 +36,8 @@ const { compileBuildTask } = require('./gulpfile.compile');
const { compileExtensionsBuildTask } = require('./gulpfile.extensions');
const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname));
-
const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n));
-// {{SQL CARBON EDIT}}
-const nodeModules = [
+const nodeModules = [ // {{SQL CARBON EDIT}}
'electron',
'original-fs',
'rxjs/Observable',
diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js
index 1e3f338da3..57719d9fc9 100644
--- a/build/gulpfile.vscode.linux.js
+++ b/build/gulpfile.vscode.linux.js
@@ -92,9 +92,7 @@ function prepareDebPackage(arch) {
const postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' })
.pipe(replace('@@NAME@@', product.applicationName))
.pipe(replace('@@ARCHITECTURE@@', debArch))
- // @ts-ignore JSON checking: quality is optional
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
- // @ts-ignore JSON checking: updateUrl is optional
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
.pipe(rename('DEBIAN/postinst'));
@@ -169,9 +167,7 @@ function prepareRpmPackage(arch) {
.pipe(replace('@@RELEASE@@', linuxPackageRevision))
.pipe(replace('@@ARCHITECTURE@@', rpmArch))
.pipe(replace('@@LICENSE@@', product.licenseName))
- // @ts-ignore JSON checking: quality is optional
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
- // @ts-ignore JSON checking: updateUrl is optional
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
.pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', ')))
.pipe(rename('SPECS/' + product.applicationName + '.spec'));
diff --git a/build/lib/watch/watch-win32.js b/build/lib/watch/watch-win32.js
index 4ceb09c166..acaa50b8c3 100644
--- a/build/lib/watch/watch-win32.js
+++ b/build/lib/watch/watch-win32.js
@@ -25,7 +25,6 @@ function watch(root) {
var child = cp.spawn(watcherPath, [root]);
child.stdout.on('data', function (data) {
- // @ts-ignore
var lines = data.toString('utf8').split('\n');
for (var i = 0; i < lines.length; i++) {
var line = lines[i].trim();
@@ -47,7 +46,6 @@ function watch(root) {
path: changePathFull,
base: root
});
- //@ts-ignore
file.event = toChangeType(changeType);
result.emit('data', file);
}
@@ -106,4 +104,4 @@ module.exports = function (pattern, options) {
});
}))
.pipe(rebase);
-};
\ No newline at end of file
+};
diff --git a/cglicenses.json b/cglicenses.json
index 76e6fe49f0..0d588440ae 100644
--- a/cglicenses.json
+++ b/cglicenses.json
@@ -89,5 +89,85 @@
"prependLicenseText": [
"Copyright (c) Microsoft Corporation. All rights reserved."
]
+ },
+ {
+ // Reason: The license at https://github.com/reem/rust-unreachable/blob/master/LICENSE-MIT
+ // cannot be found by the OSS tool automatically.
+ "name": "reem/rust-unreachable",
+ "fullLicenseText": [
+ "Copyright (c) 2015 The rust-unreachable Developers",
+ "",
+ "Permission is hereby granted, free of charge, to any person obtaining a copy",
+ "of this software and associated documentation files (the \"Software\"), to deal",
+ "in the Software without restriction, including without limitation the rights",
+ "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
+ "copies of the Software, and to permit persons to whom the Software is",
+ "furnished to do so, subject to the following conditions:",
+ "",
+ "The above copyright notice and this permission notice shall be included in all",
+ "copies or substantial portions of the Software.",
+ "",
+ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
+ "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+ "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
+ "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+ "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
+ "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
+ "SOFTWARE."
+ ]
+ },
+ {
+ // Reason: The license at https://github.com/reem/rust-void/blob/master/LICENSE-MIT
+ // cannot be found by the OSS tool automatically.
+ "name": "reem/rust-void",
+ "fullLicenseText": [
+ "Copyright (c) 2015 The rust-void Developers",
+ "",
+ "Permission is hereby granted, free of charge, to any person obtaining a copy",
+ "of this software and associated documentation files (the \"Software\"), to deal",
+ "in the Software without restriction, including without limitation the rights",
+ "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
+ "copies of the Software, and to permit persons to whom the Software is",
+ "furnished to do so, subject to the following conditions:",
+ "",
+ "The above copyright notice and this permission notice shall be included in all",
+ "copies or substantial portions of the Software.",
+ "",
+ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
+ "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+ "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
+ "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+ "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
+ "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
+ "SOFTWARE."
+ ]
+ },
+ {
+ // Reason: The license at https://github.com/mrhooray/crc-rs/blob/master/LICENSE-MIT
+ // cannot be found by the OSS tool automatically.
+ "name": "mrhooray/crc-rs",
+ "fullLicenseText": [
+ "MIT License",
+ "",
+ "Copyright (c) 2017 crc-rs Developers",
+ "",
+ "Permission is hereby granted, free of charge, to any person obtaining a copy",
+ "of this software and associated documentation files (the \"Software\"), to deal",
+ "in the Software without restriction, including without limitation the rights",
+ "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
+ "copies of the Software, and to permit persons to whom the Software is",
+ "furnished to do so, subject to the following conditions:",
+ "",
+ "The above copyright notice and this permission notice shall be included in all",
+ "copies or substantial portions of the Software.",
+ "",
+ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
+ "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+ "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
+ "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+ "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
+ "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
+ "SOFTWARE."
+ ]
}
]
diff --git a/extensions/azurecore/extension.webpack.config.js b/extensions/azurecore/extension.webpack.config.js
index efc18ebf4a..abe993cd92 100644
--- a/extensions/azurecore/extension.webpack.config.js
+++ b/extensions/azurecore/extension.webpack.config.js
@@ -8,13 +8,30 @@
'use strict';
const withDefaults = require('../shared.webpack.config');
+const fs = require('fs');
+const path = require('path');
+
+const externals = {
+ 'node-fetch': 'commonjs node-fetch',
+ 'bufferutil': 'commonjs bufferutil',
+ 'utf-8-validate': 'commonjs utf-8-validate',
+ 'keytar': 'commonjs keytar',
+};
+
+// conditionally add ws if we are going to be running in a node environment
+const yarnrcPath = path.join(__dirname, '.yarnrc');
+if (fs.existsSync(yarnrcPath)) {
+ const yarnrc = fs.readFileSync(yarnrcPath).toString();
+ const properties = yarnrc.split(/\r?\n/).map(r => r.split(' '));
+ if (properties.find(r => r[0] === 'runtime')[1] === '"node"') {
+ externals['ws'] = 'commonjs ws';
+ }
+}
module.exports = withDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
},
- externals: {
- 'keytar': 'commonjs keytar'
- }
+ externals: externals
});
diff --git a/extensions/azurecore/package.json b/extensions/azurecore/package.json
index b793d4fe69..c36efc6ccf 100644
--- a/extensions/azurecore/package.json
+++ b/extensions/azurecore/package.json
@@ -143,6 +143,14 @@
"title": "%azure.resource.selectsubscriptions.title%",
"icon": "$(filter)"
},
+ {
+ "command": "azure.resource.startterminal",
+ "title": "%azure.resource.startterminal.title%",
+ "icon": {
+ "dark": "resources/dark/console.svg",
+ "light": "resources/light/console.svg"
+ }
+ },
{
"command": "azure.resource.connectsqlserver",
"title": "%azure.resource.connectsqlserver.title%",
@@ -217,15 +225,40 @@
"when": "viewItem == azure.resource.itemType.account",
"group": "inline"
},
+ {
+ "command": "azure.resource.selectsubscriptions",
+ "when": "viewItem == azure.resource.itemType.account",
+ "group": "azurecore"
+ },
{
"command": "azure.resource.refresh",
"when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer)$/",
"group": "inline"
},
+ {
+ "command": "azure.resource.refresh",
+ "when": "viewItem =~ /^azure\\.resource\\.itemType\\.(?:account|subscription|databaseContainer|databaseServerContainer)$/",
+ "group": "azurecore"
+ },
{
"command": "azure.resource.connectsqlserver",
"when": "viewItem == azure.resource.itemType.databaseServer || viewItem == azure.resource.itemType.database || viewItem == azure.resource.itemType.sqlInstance",
"group": "inline"
+ },
+ {
+ "command": "azure.resource.connectsqlserver",
+ "when": "viewItem == azure.resource.itemType.databaseServer || viewItem == azure.resource.itemType.database || viewItem == azure.resource.itemType.sqlInstance",
+ "group": "azurecore"
+ },
+ {
+ "command": "azure.resource.startterminal",
+ "when": "viewItem == azure.resource.itemType.account",
+ "group": "inline"
+ },
+ {
+ "command": "azure.resource.startterminal",
+ "when": "viewItem == azure.resource.itemType.account",
+ "group": "azurecore"
}
]
},
@@ -234,11 +267,10 @@
"dependencies": {
"@azure/arm-resourcegraph": "^2.0.0",
"@azure/arm-subscriptions": "1.0.0",
- "adal-node": "^0.2.1",
"axios": "^0.19.2",
"qs": "^6.9.1",
- "request": "2.88.0",
- "vscode-nls": "^4.0.0"
+ "vscode-nls": "^4.0.0",
+ "ws": "^7.2.0"
},
"devDependencies": {
"@types/keytar": "^4.4.2",
@@ -246,6 +278,7 @@
"@types/node": "^12.11.7",
"@types/qs": "^6.9.1",
"@types/request": "^2.48.1",
+ "@types/ws": "^6.0.4",
"mocha": "^5.2.0",
"mocha-junit-reporter": "^1.17.0",
"mocha-multi-reporters": "^1.1.7",
diff --git a/extensions/azurecore/package.nls.json b/extensions/azurecore/package.nls.json
index 13e57263d0..e2a7c1fc4b 100644
--- a/extensions/azurecore/package.nls.json
+++ b/extensions/azurecore/package.nls.json
@@ -10,6 +10,7 @@
"azure.resource.refresh.title": "Refresh",
"azure.resource.signin.title": "Azure: Sign In",
"azure.resource.selectsubscriptions.title": "Select Subscriptions",
+ "azure.resource.startterminal.title": "Start Cloud Shell",
"azure.resource.connectsqlserver.title": "Connect",
"azure.resource.connectsqldb.title": "Add to Servers",
diff --git a/extensions/azurecore/resources/dark/console.svg b/extensions/azurecore/resources/dark/console.svg
new file mode 100644
index 0000000000..1e2d3b4ee1
--- /dev/null
+++ b/extensions/azurecore/resources/dark/console.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/azurecore/resources/light/console.svg b/extensions/azurecore/resources/light/console.svg
new file mode 100644
index 0000000000..429cb22b71
--- /dev/null
+++ b/extensions/azurecore/resources/light/console.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/azurecore/src/account-provider/auths/azureAuth.ts b/extensions/azurecore/src/account-provider/auths/azureAuth.ts
index 4e6dfa7e14..cc5e6b5157 100644
--- a/extensions/azurecore/src/account-provider/auths/azureAuth.ts
+++ b/extensions/azurecore/src/account-provider/auths/azureAuth.ts
@@ -124,6 +124,7 @@ export abstract class AzureAuth implements vscode.Disposable {
this.metadata.settings.sqlResource,
this.metadata.settings.graphResource,
this.metadata.settings.ossRdbmsResource,
+ this.metadata.settings.microsoftResource,
this.metadata.settings.azureKeyVaultResource
];
@@ -137,32 +138,47 @@ export abstract class AzureAuth implements vscode.Disposable {
public dispose() { }
- public async refreshAccess(account: azdata.Account): Promise {
- const response = await this.getCachedToken(account.key);
+ public async refreshAccess(oldAccount: azdata.Account): Promise {
+ const response = await this.getCachedToken(oldAccount.key);
if (!response) {
- account.isStale = true;
- return account;
+ oldAccount.isStale = true;
+ return oldAccount;
}
const refreshToken = response.refreshToken;
if (!refreshToken || !refreshToken.key) {
- account.isStale = true;
- return account;
+ oldAccount.isStale = true;
+ return oldAccount;
}
try {
- await this.refreshAccessToken(account.key, refreshToken);
+ // Refresh the access token
+ const tokenResponse = await this.refreshAccessToken(oldAccount.key, refreshToken);
+ const tenants = await this.getTenants(tokenResponse.accessToken);
+
+ // Recreate account object
+ const newAccount = this.createAccount(tokenResponse.tokenClaims, tokenResponse.accessToken.key, tenants);
+
+ const subscriptions = await this.getSubscriptions(newAccount);
+ newAccount.properties.subscriptions = subscriptions;
+
+ return newAccount;
} catch (ex) {
+ oldAccount.isStale = true;
if (ex.message) {
await vscode.window.showErrorMessage(ex.message);
}
console.log(ex);
}
- return account;
+ return oldAccount;
}
public async getSecurityToken(account: azdata.Account, azureResource: azdata.AzureResource): Promise {
+ if (account.isStale === true) {
+ return undefined;
+ }
+
const resource = this.resources.find(s => s.azureResourceId === azureResource);
if (!resource) {
return undefined;
@@ -199,8 +215,13 @@ export abstract class AzureAuth implements vscode.Disposable {
if (!baseToken) {
return undefined;
}
+ try {
+ await this.refreshAccessToken(account.key, baseToken.refreshToken, tenant, resource);
+ } catch (ex) {
+ account.isStale = true;
+ return undefined;
+ }
- await this.refreshAccessToken(account.key, baseToken.refreshToken, tenant, resource);
cachedTokens = await this.getCachedToken(account.key, resource.id, tenant.id);
if (!cachedTokens) {
return undefined;
@@ -349,8 +370,7 @@ export abstract class AzureAuth implements vscode.Disposable {
return { accessToken, refreshToken, tokenClaims };
} catch (err) {
- console.dir(err);
- const msg = localize('azure.noToken', "Retrieving the token failed.");
+ const msg = localize('azure.noToken', "Retrieving the Azure token failed. Please sign in again.");
vscode.window.showErrorMessage(msg);
throw new Error(err);
}
@@ -365,7 +385,7 @@ export abstract class AzureAuth implements vscode.Disposable {
}
}
- private async refreshAccessToken(account: azdata.AccountKey, rt: RefreshToken, tenant?: Tenant, resource?: Resource): Promise {
+ private async refreshAccessToken(account: azdata.AccountKey, rt: RefreshToken, tenant?: Tenant, resource?: Resource): Promise {
const postData: { [key: string]: string } = {
grant_type: 'refresh_token',
refresh_token: rt.token,
@@ -377,7 +397,10 @@ export abstract class AzureAuth implements vscode.Disposable {
postData.resource = resource.endpoint;
}
- const { accessToken, refreshToken } = await this.getToken(postData, tenant?.id, resource?.id);
+ const getTokenResponse = await this.getToken(postData, tenant?.id, resource?.id);
+
+ const accessToken = getTokenResponse?.accessToken;
+ const refreshToken = getTokenResponse?.refreshToken;
if (!accessToken || !refreshToken) {
console.log('Access or refresh token were undefined');
@@ -385,7 +408,9 @@ export abstract class AzureAuth implements vscode.Disposable {
throw new Error(msg);
}
- return this.setCachedToken(account, accessToken, refreshToken, resource?.id, tenant?.id);
+ await this.setCachedToken(account, accessToken, refreshToken, resource?.id, tenant?.id);
+
+ return getTokenResponse;
}
diff --git a/extensions/azurecore/src/account-provider/auths/azureAuthCodeGrant.ts b/extensions/azurecore/src/account-provider/auths/azureAuthCodeGrant.ts
index ff26b261b3..0083d8d0d3 100644
--- a/extensions/azurecore/src/account-provider/auths/azureAuthCodeGrant.ts
+++ b/extensions/azurecore/src/account-provider/auths/azureAuthCodeGrant.ts
@@ -69,7 +69,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
serverPort = await this.server.startup();
} catch (err) {
const msg = localize('azure.serverCouldNotStart', 'Server could not start. This could be a permissions error or an incompatibility on your system. You can try enabling device code authentication from settings.');
- await vscode.window.showErrorMessage(msg);
+ vscode.window.showErrorMessage(msg);
console.dir(err);
return undefined;
}
@@ -181,7 +181,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
refreshToken = rt;
} catch (ex) {
if (ex.msg) {
- await vscode.window.showErrorMessage(ex.msg);
+ vscode.window.showErrorMessage(ex.msg);
}
console.log(ex);
}
@@ -199,7 +199,7 @@ export class AzureAuthCodeGrant extends AzureAuth {
} catch (ex) {
console.log(ex);
if (ex.msg) {
- await vscode.window.showErrorMessage(ex.msg);
+ vscode.window.showErrorMessage(ex.msg);
authCompleteDeferred.reject(ex);
} else {
authCompleteDeferred.reject(new Error('There was an issue when storing the cache.'));
diff --git a/extensions/azurecore/src/account-provider/azureAccountProvider.ts b/extensions/azurecore/src/account-provider/azureAccountProvider.ts
index 4a8f997a57..ba4630054f 100644
--- a/extensions/azurecore/src/account-provider/azureAccountProvider.ts
+++ b/extensions/azurecore/src/account-provider/azureAccountProvider.ts
@@ -128,7 +128,7 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
if (this.authMappings.size === 0) {
console.log('No auth method was enabled.');
- await vscode.window.showErrorMessage(noAuthAvailable);
+ vscode.window.showErrorMessage(noAuthAvailable);
return { canceled: true };
}
@@ -145,7 +145,7 @@ export class AzureAccountProvider implements azdata.AccountProvider, vscode.Disp
if (!pick) {
console.log('No auth method was selected.');
- await vscode.window.showErrorMessage(noAuthSelected);
+ vscode.window.showErrorMessage(noAuthSelected);
return { canceled: true };
}
diff --git a/extensions/azurecore/src/account-provider/interfaces.ts b/extensions/azurecore/src/account-provider/interfaces.ts
index edf50be2ff..6cbcd3d13c 100644
--- a/extensions/azurecore/src/account-provider/interfaces.ts
+++ b/extensions/azurecore/src/account-provider/interfaces.ts
@@ -69,6 +69,11 @@ interface Settings {
*/
signInResourceId?: string;
+ /**
+ * Information that describes the Microsoft resource management resource
+ */
+ microsoftResource?: Resource
+
/**
* Information that describes the AAD graph resource
*/
diff --git a/extensions/azurecore/src/account-provider/providerSettings.ts b/extensions/azurecore/src/account-provider/providerSettings.ts
index 90c4c48e36..6e75c08125 100644
--- a/extensions/azurecore/src/account-provider/providerSettings.ts
+++ b/extensions/azurecore/src/account-provider/providerSettings.ts
@@ -18,6 +18,11 @@ const publicAzureSettings: ProviderSettings = {
host: 'https://login.microsoftonline.com/',
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
signInResourceId: 'https://management.core.windows.net/',
+ microsoftResource: {
+ id: 'marm',
+ endpoint: 'https://management.core.windows.net/',
+ azureResourceId: AzureResource.MicrosoftResourceManagement
+ },
graphResource: {
id: 'graph',
endpoint: 'https://graph.microsoft.com',
@@ -62,6 +67,11 @@ const usGovAzureSettings: ProviderSettings = {
host: 'https://login.microsoftonline.us/',
clientId: 'a69788c6-1d43-44ed-9ca3-b83e194da255',
signInResourceId: 'https://management.core.usgovcloudapi.net/',
+ microsoftResource: {
+ id: 'marm',
+ endpoint: 'https://management.core.usgovcloudapi.net/',
+ azureResourceId: AzureResource.MicrosoftResourceManagement
+ },
graphResource: {
id: 'graph',
endpoint: 'https://graph.windows.net',
diff --git a/extensions/azurecore/src/account-provider/tokenCache.ts b/extensions/azurecore/src/account-provider/tokenCache.ts
deleted file mode 100644
index 2e3edef727..0000000000
--- a/extensions/azurecore/src/account-provider/tokenCache.ts
+++ /dev/null
@@ -1,317 +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 * as adal from 'adal-node';
-import * as azdata from 'azdata';
-import * as crypto from 'crypto';
-import { promises as fs } from 'fs';
-
-export default class TokenCache implements adal.TokenCache {
- private static CipherAlgorithm = 'aes-256-cbc';
- private static CipherAlgorithmIvLength = 16;
- private static CipherKeyLength = 32;
- private static FsOptions = { encoding: 'ascii' };
-
- private _activeOperation: Thenable;
-
- constructor(
- private _credentialProvider: azdata.CredentialProvider,
- private _credentialServiceKey: string,
- private _cacheSerializationPath: string
- ) {
- }
-
- // PUBLIC METHODS //////////////////////////////////////////////////////
- public add(entries: adal.TokenResponse[], callback: (error: Error, result: boolean) => void): void {
- let self = this;
-
- this.doOperation(() => {
- return self.readCache()
- .then(cache => self.addToCache(cache, entries))
- .then(updatedCache => self.writeCache(updatedCache))
- .then(
- () => callback(null, true),
- (err) => callback(err, false)
- );
- });
- }
-
- /**
- * Wrapper to make callback-based add method into a thenable method
- * @param entries Entries to add into the cache
- * @returns Promise to return the result of adding the tokens to the cache
- * Rejected if an error was sent in the callback
- */
- public addThenable(entries: adal.TokenResponse[]): Thenable {
- let self = this;
-
- return new Promise((resolve, reject) => {
- self.add(entries, (error: Error, results: boolean) => {
- if (error) {
- reject(error);
- } else {
- resolve(results);
- }
- });
- });
- }
-
- public async clear(): Promise {
-
- // 1) Delete encrypted serialization file
- // If we got an 'ENOENT' response, the file doesn't exist, which is fine
- // 3) Delete the encryption key
- try {
- await fs.unlink(this._cacheSerializationPath);
- } catch (err) {
- if (err.code !== 'ENOENT') {
- throw err;
- }
- }
- await this._credentialProvider.deleteCredential(this._credentialServiceKey);
- }
-
- public find(query: any, callback: (error: Error, results: any[]) => void): void {
- let self = this;
-
- this.doOperation(async () => {
- try {
- const cache = await self.readCache();
- const filtered = cache.filter(entry => {
- return TokenCache.findByPartial(entry, query);
- });
-
- callback(null, filtered);
- } catch (ex) {
- console.log(ex);
- callback(ex, null);
- }
- });
- }
-
-
- /**
- * Wrapper to make callback-based find method into a thenable method
- * @param query Partial object to use to look up tokens. Ideally should be partial of adal.TokenResponse
- * @returns Promise to return the matching adal.TokenResponse objects.
- * Rejected if an error was sent in the callback
- */
- public findThenable(query: any): Thenable {
- let self = this;
-
- return new Promise((resolve, reject) => {
- self.find(query, (error: Error, results: any[]) => {
- if (error) {
- reject(error);
- } else {
- resolve(results);
- }
- });
- });
- }
-
- public remove(entries: adal.TokenResponse[], callback: (error: Error, result: null) => void): void {
- let self = this;
-
- this.doOperation(() => {
- return this.readCache()
- .then(cache => self.removeFromCache(cache, entries))
- .then(updatedCache => self.writeCache(updatedCache))
- .then(
- () => callback(null, null),
- (err) => callback(err, null)
- );
- });
- }
-
- /**
- * Wrapper to make callback-based remove method into a thenable method
- * @param entries Array of entries to remove from the token cache
- * @returns Promise to remove the given tokens from the token cache
- * Rejected if an error was sent in the callback
- */
- public removeThenable(entries: adal.TokenResponse[]): Thenable {
- let self = this;
-
- return new Promise((resolve, reject) => {
- self.remove(entries, (error: Error, result: null) => {
- if (error) {
- reject(error);
- } else {
- resolve();
- }
- });
- });
- }
-
- // PRIVATE METHODS /////////////////////////////////////////////////////
- private static findByKeyHelper(entry1: adal.TokenResponse, entry2: adal.TokenResponse): boolean {
- return entry1._authority === entry2._authority
- && entry1._clientId === entry2._clientId
- && entry1.userId === entry2.userId
- && entry1.resource === entry2.resource;
- }
-
- private static findByPartial(entry: adal.TokenResponse, query: { [key: string]: any }): boolean {
- for (let key in query) {
- if (entry[key] === undefined || entry[key] !== query[key]) {
- return false;
- }
- }
- return true;
- }
-
- private doOperation(op: () => Thenable): void {
- // Initialize the active operation to an empty promise if necessary
- let activeOperation = this._activeOperation || Promise.resolve(null);
-
- // Chain the operation to perform to the end of the existing promise
- activeOperation = activeOperation.then(op);
-
- // Add a catch at the end to make sure we can continue after any errors
- activeOperation = activeOperation.then(null, err => {
- console.error(`Failed to perform token cache operation: ${err}`);
- });
-
- // Point the current active operation to this one
- this._activeOperation = activeOperation;
- }
-
- private addToCache(cache: adal.TokenResponse[], entries: adal.TokenResponse[]): adal.TokenResponse[] {
- // First remove entries from the db that are being updated
- cache = this.removeFromCache(cache, entries);
-
- // Then add the new entries to the cache
- entries.forEach((entry: adal.TokenResponse) => {
- cache.push(entry);
- });
-
- return cache;
- }
-
- private getOrCreateEncryptionParams(): Thenable {
- let self = this;
-
- return this._credentialProvider.readCredential(this._credentialServiceKey)
- .then(credential => {
- if (credential.password) {
- // We already have encryption params, deserialize them
- let splitValues = credential.password.split('|');
- if (splitValues.length === 2 && splitValues[0] && splitValues[1]) {
- try {
- return {
- key: Buffer.from(splitValues[0], 'hex'),
- initializationVector: Buffer.from(splitValues[1], 'hex')
- };
- } catch (e) {
- // Swallow the error and fall through to generate new params
- console.warn('Failed to deserialize encryption params, new ones will be generated.');
- }
- }
- }
-
- // We haven't stored encryption values, so generate them
- let encryptKey = crypto.randomBytes(TokenCache.CipherKeyLength);
- let initializationVector = crypto.randomBytes(TokenCache.CipherAlgorithmIvLength);
-
- // Serialize the values
- let serializedValues = `${encryptKey.toString('hex')}|${initializationVector.toString('hex')}`;
- return self._credentialProvider.saveCredential(self._credentialServiceKey, serializedValues)
- .then(() => {
- return {
- key: encryptKey,
- initializationVector: initializationVector
- };
- });
- });
- }
-
- private async readCache(): Promise {
- let self = this;
-
- // NOTE: File system operations are performed synchronously to avoid annoying nested callbacks
- // 1) Get the encryption key
- // 2) Read the encrypted token cache file
- // 3) Decrypt the file contents
- // 4) Deserialize and return
- return this.getOrCreateEncryptionParams()
- .then(async encryptionParams => {
- try {
- return self.decryptCache('utf8', encryptionParams);
- } catch (e) {
- try {
- // try to parse using 'binary' encoding and rewrite cache as UTF8
- let response = await self.decryptCache('binary', encryptionParams);
- self.writeCache(response);
- return response;
- } catch (e) {
- throw e;
- }
- }
- })
- .then(null, err => {
- // If reading the token cache fails, we'll just assume the tokens are garbage
- console.warn(`Failed to read token cache: ${err}`);
- return [];
- });
- }
-
- private async decryptCache(encoding: crypto.Utf8AsciiBinaryEncoding, encryptionParams: EncryptionParams): Promise {
- let cacheCipher = await fs.readFile(this._cacheSerializationPath, TokenCache.FsOptions);
- let decipher = crypto.createDecipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
- let cacheJson = decipher.update(cacheCipher.toString(), 'hex', encoding);
- cacheJson += decipher.final(encoding);
-
- // Deserialize the JSON into the array of tokens
- let cacheObj = JSON.parse(cacheJson);
- for (const obj of cacheObj) {
- // Rehydrate Date objects since they will always serialize as a string
- obj.expiresOn = new Date(obj.expiresOn);
- }
-
- return cacheObj;
- }
-
- private removeFromCache(cache: adal.TokenResponse[], entries: adal.TokenResponse[]): adal.TokenResponse[] {
- entries.forEach((entry: adal.TokenResponse) => {
- // Check to see if the entry exists
- let match = cache.findIndex(entry2 => TokenCache.findByKeyHelper(entry, entry2));
- if (match >= 0) {
- // Entry exists, remove it from cache
- cache.splice(match, 1);
- }
- });
-
- return cache;
- }
-
- private writeCache(cache: adal.TokenResponse[]): Thenable {
- let self = this;
- // NOTE: File system operations are being done synchronously to avoid annoying callback nesting
- // 1) Get (or generate) the encryption key
- // 2) Stringify the token cache entries
- // 4) Encrypt the JSON
- // 3) Write to the file
- return this.getOrCreateEncryptionParams()
- .then(async encryptionParams => {
- try {
- let cacheJson = JSON.stringify(cache);
-
- let cipher = crypto.createCipheriv(TokenCache.CipherAlgorithm, encryptionParams.key, encryptionParams.initializationVector);
- let cacheCipher = cipher.update(cacheJson, 'utf8', 'hex');
- cacheCipher += cipher.final('hex');
-
- await fs.writeFile(self._cacheSerializationPath, cacheCipher, TokenCache.FsOptions);
- } catch (e) {
- throw e;
- }
- });
- }
-}
-
-interface EncryptionParams {
- key: Buffer;
- initializationVector: Buffer;
-}
diff --git a/extensions/azurecore/src/azureResource/commands.ts b/extensions/azurecore/src/azureResource/commands.ts
index 0c38923107..49df4198ce 100644
--- a/extensions/azurecore/src/azureResource/commands.ts
+++ b/extensions/azurecore/src/azureResource/commands.ts
@@ -16,13 +16,58 @@ import { TreeNode } from './treeNode';
import { AzureResourceCredentialError } from './errors';
import { AzureResourceTreeProvider } from './tree/treeProvider';
import { AzureResourceAccountTreeNode } from './tree/accountTreeNode';
-import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService } from '../azureResource/interfaces';
+import { IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureTerminalService } from '../azureResource/interfaces';
import { AzureResourceServiceNames } from './constants';
import { AzureResourceGroupService } from './providers/resourceGroup/resourceGroupService';
import { GetSubscriptionsResult, GetResourceGroupsResult } from '../azurecore';
import { isArray } from 'util';
+import { AzureAccount, Tenant } from '../account-provider/interfaces';
export function registerAzureResourceCommands(appContext: AppContext, tree: AzureResourceTreeProvider): void {
+ appContext.apiWrapper.registerCommand('azure.resource.startterminal', async (node?: TreeNode) => {
+ try {
+ if (!node || !(node instanceof AzureResourceAccountTreeNode)) {
+ return;
+ }
+
+ const accountNode = node as AzureResourceAccountTreeNode;
+ const azureAccount = accountNode.account as AzureAccount;
+
+ const tokens = await appContext.apiWrapper.getSecurityToken(azureAccount, azdata.AzureResource.MicrosoftResourceManagement);
+
+ const terminalService = appContext.getService(AzureResourceServiceNames.terminalService);
+
+ const listOfTenants = azureAccount.properties.tenants.map(t => t.displayName);
+
+ if (listOfTenants.length === 0) {
+ window.showErrorMessage(localize('azure.noTenants', "A tenant is required for this feature. Your Azure subscription seems to have no tenants."));
+ return;
+ }
+
+ let tenant: Tenant;
+ window.setStatusBarMessage(localize('azure.startingCloudShell', "Starting cloud shell…"), 5000);
+
+ if (listOfTenants.length === 1) {
+ // Don't show quickpick for a single option
+ tenant = azureAccount.properties.tenants[0];
+ } else {
+ const pickedTenant = await window.showQuickPick(listOfTenants, { canPickMany: false });
+
+ if (!pickedTenant) {
+ window.showErrorMessage(localize('azure.mustPickTenant', "You must select a tenant for this feature to work."));
+ return;
+ }
+
+ // The tenant the user picked
+ tenant = azureAccount.properties.tenants[listOfTenants.indexOf(pickedTenant)];
+ }
+
+ await terminalService.getOrCreateCloudConsole(azureAccount, tenant, tokens);
+ } catch (ex) {
+ console.error(ex);
+ window.showErrorMessage(ex);
+ }
+ });
// Resource Management commands
appContext.apiWrapper.registerCommand('azure.accounts.getSubscriptions', async (account?: azdata.Account, ignoreErrors: boolean = false): Promise => {
@@ -98,6 +143,7 @@ export function registerAzureResourceCommands(appContext: AppContext, tree: Azur
});
// Resource Tree commands
+
appContext.apiWrapper.registerCommand('azure.resource.selectsubscriptions', async (node?: TreeNode) => {
if (!(node instanceof AzureResourceAccountTreeNode)) {
return;
diff --git a/extensions/azurecore/src/azureResource/constants.ts b/extensions/azurecore/src/azureResource/constants.ts
index b7882700cc..bea9844b36 100644
--- a/extensions/azurecore/src/azureResource/constants.ts
+++ b/extensions/azurecore/src/azureResource/constants.ts
@@ -20,5 +20,6 @@ export enum AzureResourceServiceNames {
accountService = 'AzureResourceAccountService',
subscriptionService = 'AzureResourceSubscriptionService',
subscriptionFilterService = 'AzureResourceSubscriptionFilterService',
- tenantService = 'AzureResourceTenantService'
+ tenantService = 'AzureResourceTenantService',
+ terminalService = 'AzureTerminalService',
}
diff --git a/extensions/azurecore/src/azureResource/interfaces.ts b/extensions/azurecore/src/azureResource/interfaces.ts
index 8664c7ba19..35a11026d2 100644
--- a/extensions/azurecore/src/azureResource/interfaces.ts
+++ b/extensions/azurecore/src/azureResource/interfaces.ts
@@ -9,6 +9,7 @@ import { Account, DidChangeAccountsParams } from 'azdata';
import { Event } from 'vscode';
import { azureResource } from './azure-resource';
+import { AzureAccount, AzureAccountSecurityToken, Tenant } from '../account-provider/interfaces';
export interface IAzureResourceAccountService {
getAccounts(): Promise;
@@ -24,6 +25,10 @@ export interface IAzureResourceSubscriptionFilterService {
saveSelectedSubscriptions(account: Account, selectedSubscriptions: azureResource.AzureResourceSubscription[]): Promise;
}
+export interface IAzureTerminalService {
+ getOrCreateCloudConsole(account: AzureAccount, tenant: Tenant, tokens: { [key: string]: AzureAccountSecurityToken }): Promise;
+}
+
export interface IAzureResourceCacheService {
generateKey(id: string): string;
diff --git a/extensions/azurecore/src/azureResource/services/terminalService.ts b/extensions/azurecore/src/azureResource/services/terminalService.ts
new file mode 100644
index 0000000000..1cef9f272a
--- /dev/null
+++ b/extensions/azurecore/src/azureResource/services/terminalService.ts
@@ -0,0 +1,196 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as vscode from 'vscode';
+import * as nls from 'vscode-nls';
+import axios, { AxiosRequestConfig } from 'axios';
+import * as WS from 'ws';
+
+import { IAzureTerminalService } from '../interfaces';
+import { AzureAccount, AzureAccountSecurityToken, Tenant } from '../../account-provider/interfaces';
+
+const localize = nls.loadMessageBundle();
+export class AzureTerminalService implements IAzureTerminalService {
+ private readonly apiVersion = '?api-version=2018-10-01';
+
+ public constructor(context: vscode.ExtensionContext) {
+
+ }
+
+ public async getOrCreateCloudConsole(account: AzureAccount, tenant: Tenant, tokens: { [key: string]: AzureAccountSecurityToken }): Promise {
+ const token = tokens[tenant.id].token;
+ const settings: AxiosRequestConfig = {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
+ },
+ validateStatus: () => true
+ };
+
+ const metadata = account.properties.providerSettings;
+
+ const userSettingsUri = this.getConsoleUserSettingsUri(metadata.settings.armResource.endpoint);
+ const userSettingsResult = await axios.get(userSettingsUri, settings);
+
+ const preferredShell = userSettingsResult.data?.properties?.preferredShellType ?? 'bash';
+ const preferredLocation = userSettingsResult.data?.properties?.preferredLocation;
+
+ const consoleRequestUri = this.getConsoleRequestUri(metadata.settings.armResource.endpoint);
+ if (preferredLocation) {
+ settings.headers['x-ms-console-preferred-location'] = preferredLocation;
+ }
+
+ const provisionResult = await axios.put(consoleRequestUri, {}, settings);
+
+ if (provisionResult.data?.properties?.provisioningState !== 'Succeeded') {
+ throw new Error(provisionResult.data);
+ }
+ const consoleUri = provisionResult.data.properties.uri;
+
+ return this.createTerminal(consoleUri, token, account.displayInfo.displayName, preferredShell);
+ }
+
+
+ private async createTerminal(provisionedUri: string, token: string, accountDisplayName: string, preferredShell: string): Promise {
+ class ShellType implements vscode.QuickPickItem {
+ constructor(public readonly label: string, public readonly value: string) {
+ }
+ }
+
+ const shells = [new ShellType('PowerShell', 'pwsh'), new ShellType('Bash', 'bash'),];
+ const idx = shells.findIndex(s => s.value === preferredShell);
+
+ const prefShell = shells.splice(idx, 1);
+ shells.unshift(prefShell[0]);
+
+ let shell = await vscode.window.showQuickPick(shells, {
+ canPickMany: false,
+ placeHolder: localize('azure.selectShellType', "Select Bash or PowerShell for Azure Cloud Shell")
+ });
+
+ if (!shell) {
+ vscode.window.showErrorMessage(localize('azure.shellTypeRequired', "You must pick a shell type"));
+ return;
+ }
+
+ const terminalName = localize('azure.cloudShell', "Azure Cloud Shell (Preview)") + ` ${shell} (${accountDisplayName})`;
+
+ const azureTerminal = new AzureTerminal(provisionedUri, token, shell.value);
+ const terminal = vscode.window.createTerminal({
+ name: terminalName,
+ pty: azureTerminal
+ });
+
+ terminal.show();
+ }
+
+ public getConsoleRequestUri(armEndpoint: string): string {
+ return `${armEndpoint}/providers/Microsoft.Portal/consoles/default${this.apiVersion}`;
+ }
+
+ public getConsoleUserSettingsUri(armEndpoint: string): string {
+ return `${armEndpoint}/providers/Microsoft.Portal/userSettings/cloudconsole${this.apiVersion}`;
+ }
+}
+
+class AzureTerminal implements vscode.Pseudoterminal {
+ private readonly writeEmitter: vscode.EventEmitter;
+ public readonly onDidWrite: vscode.Event;
+
+ private socket: WS;
+ private intervalTimer: NodeJS.Timer;
+ private terminalDimensions: vscode.TerminalDimensions;
+
+ constructor(private readonly consoleUri: string, private readonly token: string, private shell: string) {
+ this.writeEmitter = new vscode.EventEmitter();
+ this.onDidWrite = this.writeEmitter.event;
+ }
+
+ handleInput(data: string): void {
+ this.socket?.send(data);
+ }
+
+ async open(initialDimensions: vscode.TerminalDimensions): Promise {
+ return this.resetTerminalSize(initialDimensions);
+ }
+
+ close(): void {
+ if (!this.socket) { return; }
+
+ this.socket.removeAllListeners('open');
+ this.socket.removeAllListeners('message');
+ this.socket.removeAllListeners('close');
+
+ this.socket.terminate();
+ if (this.intervalTimer) {
+ clearInterval(this.intervalTimer);
+ }
+ }
+
+ async setDimensions(dimensions: vscode.TerminalDimensions): Promise {
+ return this.resetTerminalSize(dimensions);
+ }
+
+ private async resetTerminalSize(dimensions: vscode.TerminalDimensions): Promise {
+ try {
+
+ if (!this.terminalDimensions) { // first time
+ this.writeEmitter.fire(localize('azure.connectingShellTerminal', "Connecting terminal...\n"));
+ }
+
+ if (dimensions) {
+ this.terminalDimensions = dimensions;
+ }
+
+ // Close the shell before this and restablish a new connection
+ this.close();
+
+ const terminalUri = await this.establishTerminal(this.terminalDimensions);
+ this.socket = new WS(terminalUri);
+
+ this.socket.on('message', (data: WS.Data) => {
+ // Write to the console
+ this.writeEmitter.fire(data.toString());
+ });
+
+ this.socket.on('close', () => {
+ this.writeEmitter.fire(localize('azure.shellClosed', "Shell closed.\n"));
+ this.close();
+ });
+
+ // Keep alives
+ this.intervalTimer = setInterval(() => {
+ this.socket.ping();
+ }, 5000);
+ } catch (ex) {
+ console.log(ex);
+ }
+ }
+
+
+ private async establishTerminal(dimensions: vscode.TerminalDimensions): Promise {
+ const terminalResult = await axios.post(`${this.consoleUri}/terminals?rows=${dimensions.rows}&cols=${dimensions.columns}&shell=${this.shell}`, undefined, {
+ headers: {
+ 'Accept': 'application/json',
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${this.token}`
+ }
+ });
+
+ const terminalUri = terminalResult.data?.socketUri;
+
+ if (terminalResult.data.error) {
+ vscode.window.showErrorMessage(terminalResult.data.error.message);
+ }
+
+ if (!terminalUri) {
+ console.log(terminalResult);
+ throw new Error(terminalResult.data);
+ }
+
+ return terminalUri;
+ }
+}
diff --git a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
index d411178455..fbc32fb663 100644
--- a/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
+++ b/extensions/azurecore/src/azureResource/tree/accountTreeNode.ts
@@ -79,6 +79,16 @@ export class AzureResourceAccountTreeNode extends AzureResourceContainerTreeNode
if (subscriptions.length === 0) {
return [AzureResourceMessageTreeNode.create(AzureResourceAccountTreeNode.noSubscriptionsLabel, this)];
} else {
+ // Filter out everything that we can't authenticate to.
+ subscriptions = subscriptions.filter(s => {
+ const token = tokens[s.id];
+ if (!token) {
+ console.info(`Account does not have permissions to view subscription ${JSON.stringify(s)}.`);
+ return false;
+ }
+ return true;
+ });
+
let subTreeNodes = await Promise.all(subscriptions.map(async (subscription) => {
const token = tokens[subscription.id];
const tenantId = await this._tenantService.getTenantId(subscription, this.account, new TokenCredentials(token.token, token.tokenType));
diff --git a/extensions/azurecore/src/extension.ts b/extensions/azurecore/src/extension.ts
index 21300c7b7e..d27f86f86e 100644
--- a/extensions/azurecore/src/extension.ts
+++ b/extensions/azurecore/src/extension.ts
@@ -17,7 +17,7 @@ import { AzureResourceDatabaseServerService } from './azureResource/providers/da
import { AzureResourceDatabaseProvider } from './azureResource/providers/database/databaseProvider';
import { AzureResourceDatabaseService } from './azureResource/providers/database/databaseService';
import { AzureResourceService } from './azureResource/resourceService';
-import { IAzureResourceCacheService, IAzureResourceAccountService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceTenantService } from './azureResource/interfaces';
+import { IAzureResourceCacheService, IAzureResourceAccountService, IAzureResourceSubscriptionService, IAzureResourceSubscriptionFilterService, IAzureResourceTenantService, IAzureTerminalService } from './azureResource/interfaces';
import { AzureResourceServiceNames } from './azureResource/constants';
import { AzureResourceAccountService } from './azureResource/services/accountService';
import { AzureResourceSubscriptionService } from './azureResource/services/subscriptionService';
@@ -30,6 +30,7 @@ import { SqlInstanceResourceService } from './azureResource/providers/sqlinstanc
import { SqlInstanceProvider } from './azureResource/providers/sqlinstance/sqlInstanceProvider';
import { PostgresServerProvider } from './azureResource/providers/postgresServer/postgresServerProvider';
import { PostgresServerService } from './azureResource/providers/postgresServer/postgresServerService';
+import { AzureTerminalService } from './azureResource/services/terminalService';
import { SqlInstanceArcProvider } from './azureResource/providers/sqlinstanceArc/sqlInstanceArcProvider';
import { SqlInstanceArcResourceService } from './azureResource/providers/sqlinstanceArc/sqlInstanceArcService';
import { PostgresServerArcProvider } from './azureResource/providers/postgresArcServer/postgresServerProvider';
@@ -145,6 +146,7 @@ function registerAzureServices(appContext: AppContext): void {
appContext.registerService(AzureResourceServiceNames.subscriptionService, new AzureResourceSubscriptionService());
appContext.registerService(AzureResourceServiceNames.subscriptionFilterService, new AzureResourceSubscriptionFilterService(new AzureResourceCacheService(extensionContext)));
appContext.registerService(AzureResourceServiceNames.tenantService, new AzureResourceTenantService());
+ appContext.registerService(AzureResourceServiceNames.terminalService, new AzureTerminalService(extensionContext));
}
async function onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent, apiWrapper: ApiWrapper): Promise {
diff --git a/extensions/azurecore/src/test/account-provider/tokenCache.test.ts b/extensions/azurecore/src/test/account-provider/tokenCache.test.ts
deleted file mode 100644
index 460ce5beda..0000000000
--- a/extensions/azurecore/src/test/account-provider/tokenCache.test.ts
+++ /dev/null
@@ -1,36 +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 * as should from 'should';
-import * as os from 'os';
-import * as adal from 'adal-node';
-import * as path from 'path';
-import 'mocha';
-
-import CredentialServiceTokenCache from '../../account-provider/tokenCache';
-import { CredentialsTestProvider } from '../stubs/credentialsTestProvider';
-
-describe('AccountProvider.TokenCache', function (): void {
- it('Can save and load tokens', async function (): Promise {
- const tokenResponse: adal.TokenResponse = {
- tokenType: 'testTokenType',
- expiresIn: 0,
- expiresOn: new Date(),
- resource: 'testResource',
- accessToken: 'testAccessToken'
- };
-
- const tokenCacheKey = 'azureTokenCache-testkey';
- const tokenCachePath = path.join(os.tmpdir(), tokenCacheKey);
- const credentialProvider = new CredentialsTestProvider();
- credentialProvider.saveCredential(tokenCacheKey, undefined);
- const tokenCache = new CredentialServiceTokenCache(credentialProvider, tokenCacheKey, tokenCachePath);
- const addResult = await tokenCache.addThenable([tokenResponse]);
- should(addResult).true('TokenResponse not added correctly');
-
- const results = await tokenCache.findThenable({ tokenType: 'testTokenType' });
- should(results).deepEqual([tokenResponse]);
- });
-});
diff --git a/extensions/azurecore/yarn.lock b/extensions/azurecore/yarn.lock
index 526a253f04..6c8d469085 100644
--- a/extensions/azurecore/yarn.lock
+++ b/extensions/azurecore/yarn.lock
@@ -28,7 +28,7 @@
"@azure/ms-rest-js" "^1.8.10"
tslib "^1.9.3"
-"@azure/ms-rest-js@^1.1.0":
+"@azure/ms-rest-js@^1.1.0", "@azure/ms-rest-js@^1.8.1", "@azure/ms-rest-js@^1.8.10":
version "1.8.14"
resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-1.8.14.tgz#657fc145db20b6eb3d58d1a2055473aa72eb609d"
integrity sha512-IrCPN22c8RbKWA06ZXuFwwEb15cSnr0zZ6J8Fspp9ns1SSNTERf7hv+gWvTIis1FlwHy42Mfk8hVu0/r3a0AWA==
@@ -42,32 +42,11 @@
uuid "^3.2.1"
xml2js "^0.4.19"
-"@azure/ms-rest-js@^1.8.1", "@azure/ms-rest-js@^1.8.10":
- version "1.8.13"
- resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-1.8.13.tgz#ed0cd86469697378cd39d79d5589e877a3bc87a6"
- integrity sha512-jAa6Y2XrvwbEqkaEXDHK+ReNo0WnCPS+LgQ1dRAJUUNxK4CghF5u+SXsVtPENritilVE7FVteqsLOtlhTk+haA==
- dependencies:
- "@types/tunnel" "0.0.0"
- axios "^0.19.0"
- form-data "^2.3.2"
- tough-cookie "^2.4.3"
- tslib "^1.9.2"
- tunnel "0.0.6"
- uuid "^3.2.1"
- xml2js "^0.4.19"
-
"@types/caseless@*":
version "0.12.2"
resolved "https://registry.yarnpkg.com/@types/caseless/-/caseless-0.12.2.tgz#f65d3d6389e01eeb458bd54dc8f52b95a9463bc8"
integrity sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w==
-"@types/form-data@*":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e"
- integrity sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==
- dependencies:
- "@types/node" "*"
-
"@types/keytar@^4.4.2":
version "4.4.2"
resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.2.tgz#49ef917d6cbb4f19241c0ab50cd35097b5729b32"
@@ -76,24 +55,19 @@
keytar "*"
"@types/mocha@^5.2.5":
- version "5.2.5"
- resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.5.tgz#8a4accfc403c124a0bafe8a9fc61a05ec1032073"
- integrity sha512-lAVp+Kj54ui/vLUFxsJTMtWvZraZxum3w3Nwkble2dNuV5VnPA+Mi2oGX9XYJAaIvZi3tn3cbjS/qcJXRb6Bww==
+ version "5.2.7"
+ resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
+ integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
"@types/node@*":
- version "10.14.4"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.4.tgz#1c586b991457cbb58fef51bc4e0cfcfa347714b5"
- integrity sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==
+ version "13.9.5"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.9.5.tgz#59738bf30b31aea1faa2df7f4a5f55613750cf00"
+ integrity sha512-hkzMMD3xu6BrJpGVLeQ3htQQNAcOrJjX7WFmtK8zWQpz2UJf13LCFF2ALA7c9OVdvc2vQJeDdjfR35M0sBCxvw==
"@types/node@^12.11.7":
- version "12.12.7"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
- integrity sha512-E6Zn0rffhgd130zbCbAr/JdXfXkoOUFAKNs/rF8qnafSJ8KYaA/j3oz7dcwal+lYjLA7xvdd5J4wdYpCTlP8+w==
-
-"@types/node@^8.0.47":
- version "8.10.45"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.45.tgz#4c49ba34106bc7dced77ff6bae8eb6543cde8351"
- integrity sha512-tGVTbA+i3qfXsLbq9rEq/hezaHY55QxQLeXQL2ejNgFAxxrgu8eMmYIOsRcl7hN1uTLVsKOOYacV/rcJM3sfgQ==
+ version "12.12.32"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.32.tgz#0ccc836d273e8a3cddf568daf22729cfa57c1925"
+ integrity sha512-44/reuCrwiQEsXud3I5X3sqI5jIXAmHB5xoiyKUw965olNHF3IWKjBLKK3F9LOSUZmK+oDt8jmyO637iX+hMgA==
"@types/qs@^6.9.1":
version "6.9.1"
@@ -101,19 +75,19 @@
integrity sha512-lhbQXx9HKZAPgBkISrBcmAcMpZsmpe/Cd/hY7LGZS5OfkySUBItnPZHgQPssWYUET8elF+yCFBbP1Q0RZPTdaw==
"@types/request@^2.48.1":
- version "2.48.1"
- resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.1.tgz#e402d691aa6670fbbff1957b15f1270230ab42fa"
- integrity sha512-ZgEZ1TiD+KGA9LiAAPPJL68Id2UWfeSO62ijSXZjFJArVV+2pKcsVHmrcu+1oiE3q6eDGiFiSolRc4JHoerBBg==
+ version "2.48.4"
+ resolved "https://registry.yarnpkg.com/@types/request/-/request-2.48.4.tgz#df3d43d7b9ed3550feaa1286c6eabf0738e6cf7e"
+ integrity sha512-W1t1MTKYR8PxICH+A4HgEIPuAC3sbljoEVfyZbeFJJDbr30guDspJri2XOaM2E+Un7ZjrihaDi7cf6fPa2tbgw==
dependencies:
"@types/caseless" "*"
- "@types/form-data" "*"
"@types/node" "*"
"@types/tough-cookie" "*"
+ form-data "^2.5.0"
"@types/tough-cookie@*":
- version "2.3.5"
- resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.5.tgz#9da44ed75571999b65c37b60c9b2b88db54c585d"
- integrity sha512-SCcK7mvGi3+ZNz833RRjFIxrn4gI1PPR3NtuIS+6vMkvmsGjosqTJwRt5bAEFLRz+wtJMWv8+uOnZf2hi2QXTg==
+ version "2.3.6"
+ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.6.tgz#c880579e087d7a0db13777ff8af689f4ffc7b0d5"
+ integrity sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ==
"@types/tunnel@0.0.0":
version "0.0.0"
@@ -122,6 +96,13 @@
dependencies:
"@types/node" "*"
+"@types/ws@^6.0.4":
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1"
+ integrity sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==
+ dependencies:
+ "@types/node" "*"
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -132,31 +113,6 @@ abbrev@1.0.x:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU=
-adal-node@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.2.1.tgz#19e401bd579977448c1a77ce0e5b4c9accdc334e"
- integrity sha512-C/oasZuTy0NIqh5wPWjG/09XaG+zS7elC8upf1ZVExt9lSRncme4Ejbx8CKYk+wsGgj609y84txtRAXQVvqApg==
- dependencies:
- "@types/node" "^8.0.47"
- async "^2.6.3"
- date-utils "*"
- jws "3.x.x"
- request "^2.88.0"
- underscore ">= 1.3.1"
- uuid "^3.1.0"
- xmldom ">= 0.1.x"
- xpath.js "~1.1.0"
-
-ajv@^6.5.5:
- version "6.10.0"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
- integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==
- dependencies:
- fast-deep-equal "^2.0.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
amdefine@>=0.0.4, amdefine@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -234,54 +190,17 @@ array-slice@^0.2.3:
resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-0.2.3.tgz#dd3cfb80ed7973a75117cdac69b0b99ec86186f5"
integrity sha1-3Tz7gO15c6dRF82sabC5nshhhvU=
-asn1@~0.2.3:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136"
- integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==
- dependencies:
- safer-buffer "~2.1.0"
-
-assert-plus@1.0.0, assert-plus@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
- integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
-
async@1.x:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
-async@^2.6.3:
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
- integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
- dependencies:
- lodash "^4.17.14"
-
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
-aws-sign2@~0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
- integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
-
-aws4@^1.8.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
- integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
-
-axios@^0.19.0:
- version "0.19.0"
- resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
- integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
- dependencies:
- follow-redirects "1.5.10"
- is-buffer "^2.0.2"
-
-axios@^0.19.2:
+axios@^0.19.0, axios@^0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
@@ -298,13 +217,6 @@ base64-js@^1.0.2:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
-bcrypt-pbkdf@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
- integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=
- dependencies:
- tweetnacl "^0.14.3"
-
bl@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
@@ -327,11 +239,6 @@ browser-stdout@1.3.1:
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
-buffer-equal-constant-time@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
- integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=
-
buffer-from@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
@@ -350,11 +257,6 @@ callsite@^1.0.0:
resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20"
integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA=
-caseless@~0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
- integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
-
charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -375,10 +277,10 @@ code-point-at@^1.0.0:
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-combined-stream@^1.0.6, combined-stream@~1.0.6:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.7.tgz#2d1d24317afb8abe95d6d2c0b07b57813539d828"
- integrity sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==
+combined-stream@^1.0.6:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+ integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
@@ -402,7 +304,7 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
-core-util-is@1.0.2, core-util-is@~1.0.0:
+core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@@ -412,18 +314,6 @@ crypt@~0.0.1:
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
-dashdash@^1.12.0:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
- integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=
- dependencies:
- assert-plus "^1.0.0"
-
-date-utils@*:
- version "1.2.21"
- resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64"
- integrity sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=
-
debug@3.1.0, debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
@@ -469,13 +359,6 @@ deep-is@~0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
-define-properties@^1.1.2, define-properties@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
- integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
- dependencies:
- object-keys "^1.0.12"
-
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
@@ -496,21 +379,6 @@ diff@3.5.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
-ecc-jsbn@~0.1.1:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
- integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=
- dependencies:
- jsbn "~0.1.0"
- safer-buffer "^2.1.0"
-
-ecdsa-sig-formatter@1.0.10:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3"
- integrity sha1-HFlQAPBKiJffuFAAiSoPTDOvhsM=
- dependencies:
- safe-buffer "^5.0.1"
-
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -518,31 +386,6 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1:
dependencies:
once "^1.4.0"
-es-abstract@^1.5.1:
- version "1.16.0"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d"
- integrity sha512-xdQnfykZ9JMEiasTAJZJdMWCQ1Vm00NBw79/AWi7ELfZuuPCSOMDZbT9mkOfSctVtfhb+sAAzrm+j//GjjLHLg==
- dependencies:
- es-to-primitive "^1.2.0"
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.0"
- is-callable "^1.1.4"
- is-regex "^1.0.4"
- object-inspect "^1.6.0"
- object-keys "^1.1.1"
- string.prototype.trimleft "^2.1.0"
- string.prototype.trimright "^2.1.0"
-
-es-to-primitive@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377"
- integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==
- dependencies:
- is-callable "^1.1.4"
- is-date-object "^1.0.1"
- is-symbol "^1.0.2"
-
escape-string-regexp@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -587,31 +430,6 @@ extend-shallow@^1.1.2:
dependencies:
kind-of "^1.1.0"
-extend@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
- integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-
-extsprintf@1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
- integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
-
-extsprintf@^1.2.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
- integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
-
-fast-deep-equal@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
- integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=
-
-fast-json-stable-stringify@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
- integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
-
fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
@@ -624,12 +442,7 @@ follow-redirects@1.5.10:
dependencies:
debug "=3.1.0"
-forever-agent@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
- integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
-
-form-data@^2.3.2:
+form-data@^2.3.2, form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
@@ -638,15 +451,6 @@ form-data@^2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
-form-data@~2.3.2:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
- integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.6"
- mime-types "^2.1.12"
-
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
@@ -657,11 +461,6 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-function-bind@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
- integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-
gauge@~2.7.3:
version "2.7.4"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
@@ -676,13 +475,6 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
-getpass@^0.1.1:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
- integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=
- dependencies:
- assert-plus "^1.0.0"
-
github-from-package@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
@@ -712,9 +504,9 @@ glob@^5.0.15:
path-is-absolute "^1.0.0"
glob@^7.1.2:
- version "7.1.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
- integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
+ version "7.1.6"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
+ integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
@@ -729,9 +521,9 @@ growl@1.10.5:
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
handlebars@^4.0.1:
- version "4.7.2"
- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.2.tgz#01127b3840156a0927058779482031afe0e730d7"
- integrity sha512-4PwqDL2laXtTWZghzzCtunQUTLbo31pcCJrd/B/9JP8XbhVzpS5ZXuKqlOzsd1rtcaLo4KqAn8nl8mkknS4MHw==
+ version "4.7.3"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.3.tgz#8ece2797826886cf8082d1726ff21d2a022550ee"
+ integrity sha512-SRGwSYuNfx8DwHD/6InAPzD6RgeruWLT+B8e8a7gGs8FWgHzlExpTFMEq2IA6QpAfOClpKHy6+8IqTjeBCu6Kg==
dependencies:
neo-async "^2.6.0"
optimist "^0.6.1"
@@ -739,19 +531,6 @@ handlebars@^4.0.1:
optionalDependencies:
uglify-js "^3.1.4"
-har-schema@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
- integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=
-
-har-validator@~5.1.0:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080"
- integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==
- dependencies:
- ajv "^6.5.5"
- har-schema "^2.0.0"
-
has-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
@@ -762,37 +541,16 @@ has-flag@^3.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
-has-symbols@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
- integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
-
has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
-has@^1.0.1, has@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
- integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
- dependencies:
- function-bind "^1.1.1"
-
he@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
-http-signature@~1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
- integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=
- dependencies:
- assert-plus "^1.0.0"
- jsprim "^1.2.2"
- sshpk "^1.7.0"
-
ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
@@ -806,12 +564,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@~2.0.1:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
-
-inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
+inherits@2, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -821,26 +574,11 @@ ini@~1.3.0:
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==
-is-buffer@^2.0.2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
- integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
-
is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-is-callable@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
- integrity sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==
-
-is-date-object@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
- integrity sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=
-
is-fullwidth-code-point@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
@@ -853,25 +591,6 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
-is-regex@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491"
- integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=
- dependencies:
- has "^1.0.1"
-
-is-symbol@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.2.tgz#a055f6ae57192caee329e7a860118b497a950f38"
- integrity sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==
- dependencies:
- has-symbols "^1.0.0"
-
-is-typedarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
- integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
-
isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -882,11 +601,6 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-isstream@~0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
- integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
-
istanbul@0.4.5, istanbul@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b"
@@ -915,53 +629,6 @@ js-yaml@3.x:
argparse "^1.0.7"
esprima "^4.0.0"
-jsbn@~0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
- integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
-
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema@0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
- integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=
-
-json-stringify-safe@~5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
- integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
-
-jsprim@^1.2.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
- integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=
- dependencies:
- assert-plus "1.0.0"
- extsprintf "1.3.0"
- json-schema "0.2.3"
- verror "1.10.0"
-
-jwa@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.2.0.tgz#606da70c1c6d425cad329c77c99f2df2a981489a"
- integrity sha512-Grku9ZST5NNQ3hqNUodSkDfEBqAmGA1R8yiyPHOnLzEKI0GaCQC/XhFmsheXYuXzFQJdILbh+lYBiliqG5R/Vg==
- dependencies:
- buffer-equal-constant-time "1.0.1"
- ecdsa-sig-formatter "1.0.10"
- safe-buffer "^5.0.1"
-
-jws@3.x.x:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.1.tgz#d79d4216a62c9afa0a3d5e8b5356d75abdeb2be5"
- integrity sha512-bGA2omSrFUkd72dhh05bIAN832znP4wOU3lfuXtRBuGTbsmNmDXMQg28f0Vsxaxgk4myF5YkKQpz6qeRpMgX9g==
- dependencies:
- jwa "^1.2.0"
- safe-buffer "^5.0.1"
-
keytar@*:
version "5.4.0"
resolved "https://registry.yarnpkg.com/keytar/-/keytar-5.4.0.tgz#71d8209e7dd2fe99008c243791350a6bd6ceab67"
@@ -983,7 +650,7 @@ levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
-lodash@^4.16.4, lodash@^4.17.14, lodash@^4.17.4:
+lodash@^4.16.4, lodash@^4.17.4:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -997,17 +664,17 @@ md5@^2.1.0:
crypt "~0.0.1"
is-buffer "~1.1.1"
-mime-db@1.40.0:
- version "1.40.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32"
- integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==
+mime-db@1.43.0:
+ version "1.43.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58"
+ integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==
-mime-types@^2.1.12, mime-types@~2.1.19:
- version "2.1.24"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81"
- integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==
+mime-types@^2.1.12:
+ version "2.1.26"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06"
+ integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==
dependencies:
- mime-db "1.40.0"
+ mime-db "1.43.0"
mimic-response@^2.0.0:
version "2.1.0"
@@ -1036,24 +703,29 @@ minimist@~0.0.1:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=
-mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@~0.5.1:
+mkdirp-classic@^0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b"
+ integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==
+
+mkdirp@0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
dependencies:
minimist "0.0.8"
-mkdirp@^0.5.1:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c"
- integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==
+mkdirp@0.5.x, mkdirp@^0.5.1, mkdirp@~0.5.1:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512"
+ integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==
dependencies:
minimist "^1.2.5"
mocha-junit-reporter@^1.17.0:
- version "1.23.1"
- resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.1.tgz#ba11519c0b967f404e4123dd69bc4ba022ab0f12"
- integrity sha512-qeDvKlZyAH2YJE1vhryvjUQ06t2hcnwwu4k5Ddwn0GQINhgEYFhlGM0DwYCVUHq5cuo32qAW6HDsTHt7zz99Ng==
+ version "1.23.3"
+ resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981"
+ integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==
dependencies:
debug "^2.2.0"
md5 "^2.1.0"
@@ -1145,34 +817,11 @@ number-is-nan@^1.0.0:
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-oauth-sign@~0.9.0:
- version "0.9.0"
- resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
- integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-
object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
-object-inspect@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b"
- integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==
-
-object-keys@^1.0.12, object-keys@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
- integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-
-object.getownpropertydescriptors@^2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16"
- integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=
- dependencies:
- define-properties "^1.1.2"
- es-abstract "^1.5.1"
-
once@1.x, once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -1205,11 +854,6 @@ path-is-absolute@^1.0.0:
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
- integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=
-
plugin-error@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-0.1.2.tgz#3b9bb3335ccf00f425e07437e19276967da47ace"
@@ -1262,15 +906,10 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
-psl@^1.1.24:
- version "1.1.31"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184"
- integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==
-
psl@^1.1.28:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2"
- integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
+ integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
pump@^3.0.0:
version "3.0.0"
@@ -1280,25 +919,15 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
-punycode@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
- integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
-
-punycode@^2.1.0, punycode@^2.1.1:
+punycode@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qs@^6.9.1:
- version "6.9.1"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
- integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
-
-qs@~6.5.2:
- version "6.5.2"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
- integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
+ version "6.9.3"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.3.tgz#bfadcd296c2d549f1dffa560619132c977f5008e"
+ integrity sha512-EbZYNarm6138UKKq46tdx08Yo/q9ZhFoAXAI1meAFd2GtbRDhbZY2WQSICskT0c5q99aFzLG1D4nvTk9tqfXIw==
rc@^1.2.7:
version "1.2.8"
@@ -1356,51 +985,20 @@ remap-istanbul@^0.11.1:
source-map "^0.6.1"
through2 "2.0.1"
-request@2.88.0, request@^2.88.0:
- version "2.88.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
- integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
- dependencies:
- aws-sign2 "~0.7.0"
- aws4 "^1.8.0"
- caseless "~0.12.0"
- combined-stream "~1.0.6"
- extend "~3.0.2"
- forever-agent "~0.6.1"
- form-data "~2.3.2"
- har-validator "~5.1.0"
- http-signature "~1.2.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.19"
- oauth-sign "~0.9.0"
- performance-now "^2.1.0"
- qs "~6.5.2"
- safe-buffer "^5.1.2"
- tough-cookie "~2.4.3"
- tunnel-agent "^0.6.0"
- uuid "^3.3.2"
-
resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
-safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
- integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-
-safe-buffer@~5.2.0:
+safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519"
integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==
-safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
sax@>=0.6.0:
version "1.2.4"
@@ -1446,9 +1044,9 @@ should-type@^1.3.0, should-type@^1.4.0:
integrity sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=
should-util@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063"
- integrity sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.1.tgz#fb0d71338f532a3a149213639e2d32cbea8bcb28"
+ integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==
should@^13.2.1:
version "13.2.3"
@@ -1462,9 +1060,9 @@ should@^13.2.1:
should-util "^1.0.0"
signal-exit@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
- integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
+ integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
simple-concat@^1.0.0:
version "1.0.0"
@@ -1505,21 +1103,6 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-sshpk@^1.7.0:
- version "1.16.1"
- resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
- integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==
- dependencies:
- asn1 "~0.2.3"
- assert-plus "^1.0.0"
- bcrypt-pbkdf "^1.0.0"
- dashdash "^1.12.0"
- ecc-jsbn "~0.1.1"
- getpass "^0.1.1"
- jsbn "~0.1.0"
- safer-buffer "^2.0.2"
- tweetnacl "~0.14.0"
-
string-width@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -1537,22 +1120,6 @@ string-width@^1.0.1:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
-string.prototype.trimleft@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz#6cc47f0d7eb8d62b0f3701611715a3954591d634"
- integrity sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==
- dependencies:
- define-properties "^1.1.3"
- function-bind "^1.1.1"
-
-string.prototype.trimright@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz#669d164be9df9b6f7559fa8e89945b168a5a6c58"
- integrity sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==
- dependencies:
- define-properties "^1.1.3"
- function-bind "^1.1.1"
-
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -1606,12 +1173,12 @@ supports-color@^3.1.0:
has-flag "^1.0.0"
tar-fs@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
- integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2"
+ integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA==
dependencies:
chownr "^1.1.1"
- mkdirp "^0.5.1"
+ mkdirp-classic "^0.5.2"
pump "^3.0.0"
tar-stream "^2.0.0"
@@ -1642,18 +1209,10 @@ tough-cookie@^2.4.3:
psl "^1.1.28"
punycode "^2.1.1"
-tough-cookie@~2.4.3:
- version "2.4.3"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
- integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==
- dependencies:
- psl "^1.1.24"
- punycode "^1.4.1"
-
tslib@^1.9.2, tslib@^1.9.3:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
- integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
+ integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tunnel-agent@^0.6.0:
version "0.6.0"
@@ -1667,11 +1226,6 @@ tunnel@0.0.6:
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
-tweetnacl@^0.14.3, tweetnacl@~0.14.0:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
- integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=
-
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -1689,56 +1243,27 @@ typemoq@^2.1.0:
postinstall-build "^5.0.1"
uglify-js@^3.1.4:
- version "3.7.6"
- resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.6.tgz#0783daa867d4bc962a37cc92f67f6e3238c47485"
- integrity sha512-yYqjArOYSxvqeeiYH2VGjZOqq6SVmhxzaPjJC1W2F9e+bqvFL9QXQ2osQuKUFjM2hGjKG2YclQnRKWQSt/nOTQ==
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.8.0.tgz#f3541ae97b2f048d7e7e3aa4f39fd8a1f5d7a805"
+ integrity sha512-ugNSTT8ierCsDHso2jkBHXYrU8Y5/fY2ZUprfrJUiD7YpuFvV4jODLFmb3h4btQjqr5Nh4TX4XtgDfCU1WdioQ==
dependencies:
commander "~2.20.3"
source-map "~0.6.1"
-"underscore@>= 1.3.1":
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
- integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
-
-uri-js@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
- integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==
- dependencies:
- punycode "^2.1.0"
-
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-util.promisify@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030"
- integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==
- dependencies:
- define-properties "^1.1.2"
- object.getownpropertydescriptors "^2.0.3"
-
-uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
- integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
-
-verror@1.10.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
- integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=
- dependencies:
- assert-plus "^1.0.0"
- core-util-is "1.0.2"
- extsprintf "^1.2.0"
+uuid@^3.2.1:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+ integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
vscode-nls@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002"
- integrity sha512-qCfdzcH+0LgQnBpZA53bA32kzp9rpq/f66Som577ObeuDlFIrtbEJ+A/+CCxjIh4G8dpJYNCKIsxpRAHIfsbNw==
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167"
+ integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==
"vscodetestcover@github:corivera/vscodetestcover#1.0.5":
version "1.0.5"
@@ -1790,13 +1315,17 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+ws@^7.2.0:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46"
+ integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ==
+
xml2js@^0.4.19:
- version "0.4.22"
- resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.22.tgz#4fa2d846ec803237de86f30aa9b5f70b6600de02"
- integrity sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==
+ version "0.4.23"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
+ integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
- util.promisify "~1.0.0"
xmlbuilder "~11.0.0"
xml@^1.0.0:
@@ -1809,17 +1338,7 @@ xmlbuilder@~11.0.0:
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
-"xmldom@>= 0.1.x":
- version "0.1.27"
- resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
- integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk=
-
-xpath.js@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1"
- integrity sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==
-
xtend@~4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
- integrity sha1-pcbVMr5lbiPbgg77lDofBJmNY68=
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+ integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts
index 8a5f30c98e..9686329f2b 100644
--- a/extensions/image-preview/src/preview.ts
+++ b/extensions/image-preview/src/preview.ts
@@ -28,7 +28,7 @@ export class PreviewManager implements vscode.CustomEditorProvider {
) { }
public async openCustomDocument(uri: vscode.Uri) {
- return new vscode.CustomDocument(PreviewManager.viewType, uri);
+ return new vscode.CustomDocument(uri);
}
public async resolveCustomEditor(
diff --git a/extensions/integration-tests/package.json b/extensions/integration-tests/package.json
index dc21a80dee..d34987d36d 100644
--- a/extensions/integration-tests/package.json
+++ b/extensions/integration-tests/package.json
@@ -9,7 +9,8 @@
"azdata": "*"
},
"activationEvents": [
- "*"
+ "onFileSystem:memfs",
+ "onDebug"
],
"main": "./out/main",
"extensionDependencies": [
@@ -17,7 +18,8 @@
"Microsoft.import",
"Microsoft.profiler",
"Microsoft.mssql",
- "Microsoft.notebook"
+ "Microsoft.notebook",
+ "Microsoft.azuredatastudio-postgresql"
],
"contributes": {
"configuration": {
diff --git a/extensions/integration-tests/src/main.ts b/extensions/integration-tests/src/main.ts
index 59cc50ddb8..19c844f9ea 100644
--- a/extensions/integration-tests/src/main.ts
+++ b/extensions/integration-tests/src/main.ts
@@ -4,122 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
-import * as azdata from 'azdata';
-import { normalize, join } from 'path';
-import * as fs from 'fs';
-
-const TEST_SETUP_COMPLETED_TEXT: string = 'Test Setup Completed';
-const EXTENSION_LOADED_TEXT: string = 'Test Extension Loaded';
-const ALL_EXTENSION_LOADED_TEXT: string = 'All Extensions Loaded';
-
-let statusBarItemTimer: NodeJS.Timer;
export function activate(context: vscode.ExtensionContext) {
- let statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
- vscode.commands.registerCommand('test.setupIntegrationTest', async () => {
- let extensionInstallersFolder = normalize(join(__dirname, '../extensionInstallers'));
- console.info(`extensionInstallersFolder=${extensionInstallersFolder}`);
- // eslint-disable-next-line no-sync
- let installers = fs.readdirSync(extensionInstallersFolder);
- for (let i = 0; i < installers.length; i++) {
- if (installers[i].endsWith('.vsix')) {
- let installerFullPath = join(extensionInstallersFolder, installers[i]);
- console.info(`installing extension at ${installerFullPath}`);
- await azdata.extensions.install(installerFullPath);
- console.info(`extension has been installed successfully. vsix: ${installers[i]}`);
- }
- }
- await setConfiguration('workbench.enablePreviewFeatures', true);
- await setConfiguration('workbench.showConnectDialogOnStartup', false);
- await setConfiguration('test.testSetupCompleted', true);
- showStatusBarItem(statusBarItem, TEST_SETUP_COMPLETED_TEXT);
- });
-
- vscode.commands.registerCommand('test.waitForExtensionsToLoad', async () => {
- const expectedExtensions = ['Microsoft.agent', 'Microsoft.import', 'Microsoft.mssql', 'Microsoft.profiler', 'Microsoft.azuredatastudio-postgresql'];
- const commonFeatures: azdata.DataProviderType[] = [
- azdata.DataProviderType.AdminServicesProvider,
- azdata.DataProviderType.BackupProvider,
- azdata.DataProviderType.CapabilitiesProvider,
- azdata.DataProviderType.ConnectionProvider,
- azdata.DataProviderType.FileBrowserProvider,
- azdata.DataProviderType.MetadataProvider,
- azdata.DataProviderType.ObjectExplorerProvider,
- azdata.DataProviderType.ProfilerProvider,
- azdata.DataProviderType.QueryProvider,
- azdata.DataProviderType.RestoreProvider,
- azdata.DataProviderType.ScriptingProvider,
- azdata.DataProviderType.TaskServicesProvider];
-
- const features_mssql: azdata.DataProviderType[] = [
- azdata.DataProviderType.AgentServicesProvider,
- azdata.DataProviderType.IconProvider
- ];
-
- features_mssql.push(...commonFeatures);
-
- const providerFeatureMapping: { providerId: string, features: azdata.DataProviderType[] }[] = [
- {
- providerId: 'MSSQL',
- features: features_mssql
- }, {
- providerId: 'PGSQL',
- features: commonFeatures
- }];
-
- do {
- let extensions = vscode.extensions.all.filter(ext => { return expectedExtensions.indexOf(ext.id) !== -1; });
- const extensionsNotInReadyState: string[] = [];
-
- extensions.forEach(extension => {
- if (!extension.isActive) {
- extensionsNotInReadyState.push(extension.id);
- }
- });
-
- const providerTypesNotInReadyState: string[] = [];
- if (extensionsNotInReadyState.length === 0) {
- providerFeatureMapping.forEach(entry => {
- entry.features.forEach(feature => {
- const provider = azdata.dataprotocol.getProvider(entry.providerId, feature);
- if (!provider) {
- providerTypesNotInReadyState.push(`${entry.providerId}:${feature}`);
- }
- });
- });
- }
-
- if (extensionsNotInReadyState.length === 0 && providerTypesNotInReadyState.length === 0) {
- console.info('All extensions are ready');
- showStatusBarItem(statusBarItem, ALL_EXTENSION_LOADED_TEXT);
- break;
- } else if (extensionsNotInReadyState.length !== 0) {
- console.warn(`the following extensions are not ready: ${extensionsNotInReadyState.join(',')}`);
- } else {
- console.warn(`the following providers are not ready: ${providerTypesNotInReadyState.join(',')}`);
- }
- await new Promise(resolve => { setTimeout(resolve, 2000); });
- }
- while (true);
- });
- showStatusBarItem(statusBarItem, EXTENSION_LOADED_TEXT);
-}
-
-function showStatusBarItem(statusBarItem: vscode.StatusBarItem, text: string) {
- statusBarItem.text = text;
- statusBarItem.tooltip = text;
- statusBarItem.show();
- clearTimeout(statusBarItemTimer);
- statusBarItemTimer = setTimeout(function () {
- statusBarItem.hide();
- }, 5000);
}
// this method is called when your extension is deactivated
export function deactivate(): void {
}
-
-async function setConfiguration(name: string, value: any) {
- await vscode.workspace.getConfiguration().update(name, value, true);
-}
diff --git a/extensions/integration-tests/src/setup.test.ts b/extensions/integration-tests/src/setup.test.ts
deleted file mode 100644
index 9d81f7cd21..0000000000
--- a/extensions/integration-tests/src/setup.test.ts
+++ /dev/null
@@ -1,35 +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 'mocha';
-import * as vscode from 'vscode';
-import { isTestSetupCompleted } from './testContext';
-import * as assert from 'assert';
-import { getConfigValue, EnvironmentVariable_BDC_SERVER, EnvironmentVariable_BDC_USERNAME, EnvironmentVariable_BDC_PASSWORD, EnvironmentVariable_AZURE_PASSWORD, EnvironmentVariable_AZURE_SERVER, EnvironmentVariable_AZURE_USERNAME, EnvironmentVariable_STANDALONE_PASSWORD, EnvironmentVariable_STANDALONE_SERVER, EnvironmentVariable_STANDALONE_USERNAME, EnvironmentVariable_PYTHON_PATH } from './testConfig';
-
-assert(getConfigValue(EnvironmentVariable_BDC_SERVER) !== undefined &&
- getConfigValue(EnvironmentVariable_BDC_USERNAME) !== undefined &&
- getConfigValue(EnvironmentVariable_BDC_PASSWORD) !== undefined &&
- getConfigValue(EnvironmentVariable_AZURE_PASSWORD) !== undefined &&
- getConfigValue(EnvironmentVariable_AZURE_SERVER) !== undefined &&
- getConfigValue(EnvironmentVariable_AZURE_USERNAME) !== undefined &&
- getConfigValue(EnvironmentVariable_STANDALONE_PASSWORD) !== undefined &&
- getConfigValue(EnvironmentVariable_STANDALONE_SERVER) !== undefined &&
- getConfigValue(EnvironmentVariable_STANDALONE_USERNAME) !== undefined &&
- getConfigValue(EnvironmentVariable_PYTHON_PATH) !== undefined, 'Required environment variables are not set, if you see this error in the build pipeline, make sure the environment variables are set properly in the build definition, otherwise for local dev environment make sure you follow the instructions in the readme file.');
-
-if (!isTestSetupCompleted()) {
- suite('integration test setup', () => {
- test('test setup', async function () {
- this.timeout(5 * 60 * 1000);
- // Prepare the environment and make it ready for testing
- await vscode.commands.executeCommand('test.setupIntegrationTest');
- // Wait for the extensions to load
- await vscode.commands.executeCommand('test.waitForExtensionsToLoad');
- // Reload the window, this is required for some changes made by the 'test.setupIntegrationTest' to work
- await vscode.commands.executeCommand('workbench.action.reloadWindow');
- });
- });
-}
diff --git a/extensions/integration-tests/src/cms.test.ts b/extensions/integration-tests/src/tests/cms.test.ts
similarity index 96%
rename from extensions/integration-tests/src/cms.test.ts
rename to extensions/integration-tests/src/tests/cms.test.ts
index e0b5d0db4d..e439d7fb67 100644
--- a/extensions/integration-tests/src/cms.test.ts
+++ b/extensions/integration-tests/src/tests/cms.test.ts
@@ -6,12 +6,12 @@
import 'mocha';
import * as vscode from 'vscode';
import * as azdata from 'azdata';
-import * as mssql from '../../mssql';
-import * as utils from './utils';
-import * as uuid from './uuid';
-import { isTestSetupCompleted } from './testContext';
+import * as mssql from '../../../mssql';
+import * as utils from '../utils';
+import * as uuid from '../uuid';
+import { isTestSetupCompleted } from '../testContext';
import assert = require('assert');
-import { getStandaloneServer, TestServerProfile } from './testConfig';
+import { getStandaloneServer, TestServerProfile } from '../testConfig';
let cmsService: mssql.ICmsService;
let server: TestServerProfile;
diff --git a/extensions/integration-tests/src/dacpac.test.ts b/extensions/integration-tests/src/tests/dacpac.test.ts
similarity index 97%
rename from extensions/integration-tests/src/dacpac.test.ts
rename to extensions/integration-tests/src/tests/dacpac.test.ts
index aa472fb2a0..a541f83a52 100644
--- a/extensions/integration-tests/src/dacpac.test.ts
+++ b/extensions/integration-tests/src/tests/dacpac.test.ts
@@ -5,14 +5,14 @@
import 'mocha';
import * as azdata from 'azdata';
-import * as utils from './utils';
+import * as utils from '../utils';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
-import * as mssql from '../../mssql';
+import * as mssql from '../../../mssql';
import * as vscode from 'vscode';
-import { isTestSetupCompleted } from './testContext';
-import { getStandaloneServer } from './testConfig';
+import { isTestSetupCompleted } from '../testContext';
+import { getStandaloneServer } from '../testConfig';
import * as assert from 'assert';
import { promisify } from 'util';
diff --git a/extensions/integration-tests/src/index.ts b/extensions/integration-tests/src/tests/index.ts
similarity index 100%
rename from extensions/integration-tests/src/index.ts
rename to extensions/integration-tests/src/tests/index.ts
diff --git a/extensions/integration-tests/src/notebook.test.ts b/extensions/integration-tests/src/tests/notebook.test.ts
similarity index 99%
rename from extensions/integration-tests/src/notebook.test.ts
rename to extensions/integration-tests/src/tests/notebook.test.ts
index c62aa9b1f3..3fe406aa27 100644
--- a/extensions/integration-tests/src/notebook.test.ts
+++ b/extensions/integration-tests/src/tests/notebook.test.ts
@@ -7,10 +7,10 @@ import 'mocha';
import * as assert from 'assert';
import * as azdata from 'azdata';
import * as vscode from 'vscode';
-import { isTestSetupCompleted } from './testContext';
-import { sqlNotebookContent, writeNotebookToFile, sqlKernelMetadata, getFileName, pySparkNotebookContent, pySparkKernelMetadata, pythonKernelMetadata, sqlNotebookMultipleCellsContent, notebookContentForCellLanguageTest, sqlKernelSpec, pythonKernelSpec, pySparkKernelSpec, CellTypes } from './notebook.util';
-import { getConfigValue, EnvironmentVariable_PYTHON_PATH, TestServerProfile, getStandaloneServer } from './testConfig';
-import { connectToServer, sleep, testServerProfileToIConnectionProfile } from './utils';
+import { isTestSetupCompleted } from '../testContext';
+import { sqlNotebookContent, writeNotebookToFile, sqlKernelMetadata, getFileName, pySparkNotebookContent, pySparkKernelMetadata, pythonKernelMetadata, sqlNotebookMultipleCellsContent, notebookContentForCellLanguageTest, sqlKernelSpec, pythonKernelSpec, pySparkKernelSpec, CellTypes } from '../notebook.util';
+import { getConfigValue, EnvironmentVariable_PYTHON_PATH, TestServerProfile, getStandaloneServer } from '../testConfig';
+import { connectToServer, sleep, testServerProfileToIConnectionProfile } from '../utils';
import * as fs from 'fs';
import { stressify } from 'adstest';
import { isNullOrUndefined, promisify } from 'util';
diff --git a/extensions/integration-tests/src/objectExplorer.test.ts b/extensions/integration-tests/src/tests/objectExplorer.test.ts
similarity index 98%
rename from extensions/integration-tests/src/objectExplorer.test.ts
rename to extensions/integration-tests/src/tests/objectExplorer.test.ts
index db3a2fcad2..309e5a1d80 100644
--- a/extensions/integration-tests/src/objectExplorer.test.ts
+++ b/extensions/integration-tests/src/tests/objectExplorer.test.ts
@@ -5,9 +5,9 @@
import 'mocha';
import * as azdata from 'azdata';
-import { isTestSetupCompleted } from './testContext';
-import { getBdcServer, TestServerProfile, getAzureServer, getStandaloneServer } from './testConfig';
-import { connectToServer, createDB, deleteDB, DefaultConnectTimeoutInMs, asyncTimeout } from './utils';
+import { isTestSetupCompleted } from '../testContext';
+import { getBdcServer, TestServerProfile, getAzureServer, getStandaloneServer } from '../testConfig';
+import { connectToServer, createDB, deleteDB, DefaultConnectTimeoutInMs, asyncTimeout } from '../utils';
import * as assert from 'assert';
import { stressify } from 'adstest';
diff --git a/extensions/integration-tests/src/schemaCompare.test.ts b/extensions/integration-tests/src/tests/schemaCompare.test.ts
similarity index 99%
rename from extensions/integration-tests/src/schemaCompare.test.ts
rename to extensions/integration-tests/src/tests/schemaCompare.test.ts
index 59aef7cfb8..79ca3788ae 100644
--- a/extensions/integration-tests/src/schemaCompare.test.ts
+++ b/extensions/integration-tests/src/tests/schemaCompare.test.ts
@@ -6,14 +6,14 @@
import 'mocha';
import * as azdata from 'azdata';
import * as vscode from 'vscode';
-import * as utils from './utils';
-import * as mssql from '../../mssql';
+import * as utils from '../utils';
+import * as mssql from '../../../mssql';
import * as os from 'os';
import * as fs from 'fs';
const path = require('path');
-import { isTestSetupCompleted } from './testContext';
+import { isTestSetupCompleted } from '../testContext';
import * as assert from 'assert';
-import { getStandaloneServer } from './testConfig';
+import { getStandaloneServer } from '../testConfig';
import { stressify } from 'adstest';
import { promisify } from 'util';
diff --git a/extensions/integration-tests/test-workspace/.vscode/settings.json b/extensions/integration-tests/test-workspace/.vscode/settings.json
new file mode 100644
index 0000000000..e59a76cf46
--- /dev/null
+++ b/extensions/integration-tests/test-workspace/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "workbench.enablePreviewFeatures": true,
+ "workbench.showConnectDialogOnStartup": false
+}
diff --git a/extensions/machine-learning-services/config.json b/extensions/machine-learning-services/config.json
index 1ba95ab82e..ab363773d5 100644
--- a/extensions/machine-learning-services/config.json
+++ b/extensions/machine-learning-services/config.json
@@ -1,13 +1,9 @@
{
"sqlPackageManagement": {
"requiredPythonPackages": [
- {
- "name": "pymssql",
- "version": "2.1.4"
- },
{
"name": "sqlmlutils",
- "version": ""
+ "version": "1.0.0"
}
],
"requiredRPackages": [
diff --git a/extensions/machine-learning-services/package.json b/extensions/machine-learning-services/package.json
index 0ac1dc3946..abe3a7f0a8 100644
--- a/extensions/machine-learning-services/package.json
+++ b/extensions/machine-learning-services/package.json
@@ -37,7 +37,7 @@
},
"machineLearningServices.enableR": {
"type": "boolean",
- "default": "true",
+ "default": "false",
"description": "%mls.enableR.description%"
},
"machineLearningServices.pythonPath": {
diff --git a/extensions/machine-learning-services/src/common/constants.ts b/extensions/machine-learning-services/src/common/constants.ts
index 6711463a77..565a111475 100644
--- a/extensions/machine-learning-services/src/common/constants.ts
+++ b/extensions/machine-learning-services/src/common/constants.ts
@@ -47,7 +47,8 @@ export const msgYes = localize('msgYes', "Yes");
export const msgNo = localize('msgNo', "No");
export const managePackageCommandError = localize('mls.managePackages.error', "Either no connection is available or the server does not have external script enabled.");
export function taskFailedError(taskName: string, err: string): string { return localize('mls.taskFailedError.error', "Failed to complete task '{0}'. Error: {1}", taskName, err); }
-export const installDependenciesMsgTaskName = localize('mls.installDependencies.msgTaskName', "Installing Machine Learning extension dependencies");
+export const installPackageMngDependenciesMsgTaskName = localize('mls.installPackageMngDependencies.msgTaskName', "Installing package management dependencies");
+export const installModelMngDependenciesMsgTaskName = localize('mls.installModelMngDependencies.msgTaskName', "Installing model management dependencies");
export const noResultError = localize('mls.noResultError', "No Result returned");
export const requiredPackagesNotInstalled = localize('mls.requiredPackagesNotInstalled', "The required dependencies are not installed");
export const confirmEnableExternalScripts = localize('mls.confirmEnableExternalScripts', "External script is required for package management. Are you sure you want to enable that.");
diff --git a/extensions/machine-learning-services/src/common/queryRunner.ts b/extensions/machine-learning-services/src/common/queryRunner.ts
index 90f92a8739..ec46cee7d7 100644
--- a/extensions/machine-learning-services/src/common/queryRunner.ts
+++ b/extensions/machine-learning-services/src/common/queryRunner.ts
@@ -7,22 +7,32 @@ import * as azdata from 'azdata';
import * as nbExtensionApis from '../typings/notebookServices';
import { ApiWrapper } from './apiWrapper';
import * as constants from '../common/constants';
+import * as utils from '../common/utils';
-const maxNumberOfRetries = 3;
+const maxNumberOfRetries = 2;
const listPythonPackagesQuery = `
+Declare @tablevar table(name NVARCHAR(MAX), version NVARCHAR(MAX))
+insert into @tablevar(name, version)
EXEC sp_execute_external_script
@language=N'Python',
@script=N'import pkg_resources
import pandas
OutputDataSet = pandas.DataFrame([(d.project_name, d.version) for d in pkg_resources.working_set])'
+select e.name, version from sys.external_libraries e join @tablevar t on e.name = t.name
+where [language] = 'PYTHON'
`;
const listRPackagesQuery = `
+Declare @tablevar table(name NVARCHAR(MAX), version NVARCHAR(MAX))
+insert into @tablevar(name, version)
EXEC sp_execute_external_script
@language=N'R',
@script=N'
OutputDataSet <- as.data.frame(installed.packages()[,c(1,3)])'
+
+select e.name, version from sys.external_libraries e join @tablevar t on e.name = t.name
+where [language] = 'R'
`;
const checkMlInstalledQuery = `
@@ -63,24 +73,24 @@ export class QueryRunner {
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
- public async getPythonPackages(connection: azdata.connection.ConnectionProfile): Promise {
- return this.getPackages(connection, listPythonPackagesQuery);
+ public async getPythonPackages(connection: azdata.connection.ConnectionProfile, databaseName: string): Promise {
+ return this.getPackages(connection, databaseName, listPythonPackagesQuery);
}
/**
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
- public async getRPackages(connection: azdata.connection.ConnectionProfile): Promise {
- return this.getPackages(connection, listRPackagesQuery);
+ public async getRPackages(connection: azdata.connection.ConnectionProfile, databaseName: string): Promise {
+ return this.getPackages(connection, databaseName, listRPackagesQuery);
}
- private async getPackages(connection: azdata.connection.ConnectionProfile, script: string): Promise {
+ private async getPackages(connection: azdata.connection.ConnectionProfile, databaseName: string, script: string): Promise {
let packages: nbExtensionApis.IPackageDetails[] = [];
let result: azdata.SimpleExecuteResult | undefined = undefined;
for (let index = 0; index < maxNumberOfRetries; index++) {
- result = await this.runQuery(connection, script);
+ result = await this.runQuery(connection, utils.getScriptWithDBChange(connection.databaseName, databaseName, script));
if (result && result.rowCount > 0) {
break;
}
diff --git a/extensions/machine-learning-services/src/externalLanguage/languageService.ts b/extensions/machine-learning-services/src/externalLanguage/languageService.ts
index c8a8e289a9..12c452cd59 100644
--- a/extensions/machine-learning-services/src/externalLanguage/languageService.ts
+++ b/extensions/machine-learning-services/src/externalLanguage/languageService.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
-import * as mssql from '../../../mssql/src/mssql';
+import * as mssql from '../../../mssql';
import { ApiWrapper } from '../common/apiWrapper';
/**
diff --git a/extensions/machine-learning-services/src/modelManagement/modelPythonClient.ts b/extensions/machine-learning-services/src/modelManagement/modelPythonClient.ts
index 1b5022b554..1efe1780cb 100644
--- a/extensions/machine-learning-services/src/modelManagement/modelPythonClient.ts
+++ b/extensions/machine-learning-services/src/modelManagement/modelPythonClient.ts
@@ -40,7 +40,7 @@ export class ModelPythonClient {
* Installs dependencies for python client
*/
private async installDependencies(): Promise {
- await utils.executeTasks(this._apiWrapper, constants.installDependenciesMsgTaskName, [
+ await utils.executeTasks(this._apiWrapper, constants.installModelMngDependenciesMsgTaskName, [
this._packageManager.installRequiredPythonPackages(this._config.modelsRequiredPythonPackages)], true);
}
diff --git a/extensions/machine-learning-services/src/packageManagement/SqlPackageManageProviderBase.ts b/extensions/machine-learning-services/src/packageManagement/packageManageProviderBase.ts
similarity index 76%
rename from extensions/machine-learning-services/src/packageManagement/SqlPackageManageProviderBase.ts
rename to extensions/machine-learning-services/src/packageManagement/packageManageProviderBase.ts
index 90870aa84a..36549d9523 100644
--- a/extensions/machine-learning-services/src/packageManagement/SqlPackageManageProviderBase.ts
+++ b/extensions/machine-learning-services/src/packageManagement/packageManageProviderBase.ts
@@ -5,7 +5,6 @@
import * as azdata from 'azdata';
import { ApiWrapper } from '../common/apiWrapper';
-import * as constants from '../common/constants';
import * as nbExtensionApis from '../typings/notebookServices';
import * as utils from '../common/utils';
@@ -23,14 +22,17 @@ export abstract class SqlPackageManageProviderBase {
}
/**
- * Returns location title
+ * Returns database names
*/
- public async getLocationTitle(): Promise {
+ public async getLocations(): Promise {
let connection = await this.getCurrentConnection();
if (connection) {
- return `${connection.serverName} ${connection.databaseName ? connection.databaseName : ''}`;
+ let databases = await this._apiWrapper.listDatabases(connection.connectionId);
+ return databases.map(x => {
+ return { displayName: x, name: x };
+ });
}
- return constants.noConnectionError;
+ return [];
}
protected async getCurrentConnection(): Promise {
@@ -42,16 +44,16 @@ export abstract class SqlPackageManageProviderBase {
* @param packages Packages to install
* @param useMinVersion minimum version
*/
- public async installPackages(packages: nbExtensionApis.IPackageDetails[], useMinVersion: boolean): Promise {
+ public async installPackages(packages: nbExtensionApis.IPackageDetails[], useMinVersion: boolean, databaseName: string): Promise {
if (packages) {
- await Promise.all(packages.map(x => this.installPackage(x, useMinVersion)));
+ await Promise.all(packages.map(x => this.installPackage(x, useMinVersion, databaseName)));
}
//TODO: use useMinVersion
console.log(useMinVersion);
}
- private async installPackage(packageDetail: nbExtensionApis.IPackageDetails, useMinVersion: boolean): Promise {
+ private async installPackage(packageDetail: nbExtensionApis.IPackageDetails, useMinVersion: boolean, databaseName: string): Promise {
if (useMinVersion) {
let packageOverview = await this.getPackageOverview(packageDetail.name);
if (packageOverview && packageOverview.versions) {
@@ -60,16 +62,16 @@ export abstract class SqlPackageManageProviderBase {
}
}
- await this.executeScripts(ScriptMode.Install, packageDetail);
+ await this.executeScripts(ScriptMode.Install, packageDetail, databaseName);
}
/**
* Uninstalls given packages
* @param packages Packages to uninstall
*/
- public async uninstallPackages(packages: nbExtensionApis.IPackageDetails[]): Promise {
+ public async uninstallPackages(packages: nbExtensionApis.IPackageDetails[], databaseName: string): Promise {
if (packages) {
- await Promise.all(packages.map(x => this.executeScripts(ScriptMode.Uninstall, x)));
+ await Promise.all(packages.map(x => this.executeScripts(ScriptMode.Uninstall, x, databaseName)));
}
}
@@ -88,8 +90,8 @@ export abstract class SqlPackageManageProviderBase {
/**
* Returns list of packages
*/
- public async listPackages(): Promise {
- let packages = await this.fetchPackages();
+ public async listPackages(databaseName: string): Promise {
+ let packages = await this.fetchPackages(databaseName);
if (packages) {
packages = packages.sort((a, b) => this.comparePackages(a, b));
} else {
@@ -110,6 +112,6 @@ export abstract class SqlPackageManageProviderBase {
}
protected abstract fetchPackage(packageName: string): Promise;
- protected abstract fetchPackages(): Promise;
- protected abstract executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails): Promise;
+ protected abstract fetchPackages(databaseName: string): Promise;
+ protected abstract executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails, databaseName: string): Promise;
}
diff --git a/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts b/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
index b2cf31381d..fcb73a6555 100644
--- a/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
+++ b/extensions/machine-learning-services/src/packageManagement/packageManagementService.ts
@@ -103,15 +103,15 @@ export class PackageManagementService {
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
- public async getPythonPackages(connection: azdata.connection.ConnectionProfile): Promise {
- return this._queryRunner.getPythonPackages(connection);
+ public async getPythonPackages(connection: azdata.connection.ConnectionProfile, databaseName: string): Promise {
+ return this._queryRunner.getPythonPackages(connection, databaseName);
}
/**
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
- public async getRPackages(connection: azdata.connection.ConnectionProfile): Promise {
- return this._queryRunner.getRPackages(connection);
+ public async getRPackages(connection: azdata.connection.ConnectionProfile, databaseName: string): Promise {
+ return this._queryRunner.getRPackages(connection, databaseName);
}
}
diff --git a/extensions/machine-learning-services/src/packageManagement/packageManager.ts b/extensions/machine-learning-services/src/packageManagement/packageManager.ts
index 72e2bdb2f6..ec45279b29 100644
--- a/extensions/machine-learning-services/src/packageManagement/packageManager.ts
+++ b/extensions/machine-learning-services/src/packageManagement/packageManager.ts
@@ -93,7 +93,6 @@ export class PackageManager {
// Execute the command
//
this._apiWrapper.executeCommand(constants.managePackagesCommand, {
- multiLocations: false,
defaultLocation: defaultProvider.packageTarget.location,
defaultProviderId: defaultProvider.providerId
});
@@ -116,7 +115,7 @@ export class PackageManager {
* Installs dependencies for the extension
*/
public async installDependencies(): Promise {
- await utils.executeTasks(this._apiWrapper, constants.installDependenciesMsgTaskName, [
+ await utils.executeTasks(this._apiWrapper, constants.installPackageMngDependenciesMsgTaskName, [
this.installRequiredPythonPackages(this._config.requiredSqlPythonPackages),
this.installRequiredRPackages()], true);
}
@@ -130,7 +129,7 @@ export class PackageManager {
}
await utils.createFolder(utils.getRPackagesFolderPath(this._rootFolder));
- await Promise.all(this._config.requiredSqlPythonPackages.map(x => this.installRPackage(x)));
+ await Promise.all(this._config.requiredSqlRPackages.map(x => this.installRPackage(x)));
}
/**
@@ -151,7 +150,8 @@ export class PackageManager {
let fileContent = '';
requiredPackages.forEach(packageDetails => {
let hasVersion = ('version' in packageDetails) && !isNullOrUndefined(packageDetails['version']) && packageDetails['version'].length > 0;
- if (!installedPackages.find(x => x.name === packageDetails['name'] && (!hasVersion || packageDetails['version'] === x.version))) {
+ if (!installedPackages.find(x => x.name === packageDetails['name']
+ && (!hasVersion || utils.comparePackageVersions(packageDetails['version'] || '', x.version) <= 0))) {
let packageNameDetail = hasVersion ? `${packageDetails.name}==${packageDetails.version}` : `${packageDetails.name}`;
fileContent = `${fileContent}${packageNameDetail}\n`;
}
@@ -177,7 +177,7 @@ export class PackageManager {
private async getInstalledPipPackages(): Promise {
try {
let cmd = `"${this.pythonExecutable}" -m pip list --format=json`;
- let packagesInfo = await this._processService.executeBufferedCommand(cmd, this._outputChannel);
+ let packagesInfo = await this._processService.executeBufferedCommand(cmd, undefined);
let packagesResult: nbExtensionApis.IPackageDetails[] = [];
if (packagesInfo) {
packagesResult = JSON.parse(packagesInfo);
diff --git a/extensions/machine-learning-services/src/packageManagement/sqlPythonPackageManageProvider.ts b/extensions/machine-learning-services/src/packageManagement/sqlPythonPackageManageProvider.ts
index 8ebf25e472..8639d701ab 100644
--- a/extensions/machine-learning-services/src/packageManagement/sqlPythonPackageManageProvider.ts
+++ b/extensions/machine-learning-services/src/packageManagement/sqlPythonPackageManageProvider.ts
@@ -9,7 +9,7 @@ import * as nbExtensionApis from '../typings/notebookServices';
import { ApiWrapper } from '../common/apiWrapper';
import { ProcessService } from '../common/processService';
import { Config } from '../configurations/config';
-import { SqlPackageManageProviderBase, ScriptMode } from './SqlPackageManageProviderBase';
+import { SqlPackageManageProviderBase, ScriptMode } from './packageManageProviderBase';
import { HttpClient } from '../common/httpClient';
import * as utils from '../common/utils';
import { PackageManagementService } from './packageManagementService';
@@ -50,8 +50,8 @@ export class SqlPythonPackageManageProvider extends SqlPackageManageProviderBase
/**
* Returns list of packages
*/
- protected async fetchPackages(): Promise {
- return await this._service.getPythonPackages(await this.getCurrentConnection());
+ protected async fetchPackages(databaseName: string): Promise {
+ return await this._service.getPythonPackages(await this.getCurrentConnection(), databaseName);
}
/**
@@ -59,14 +59,14 @@ export class SqlPythonPackageManageProvider extends SqlPackageManageProviderBase
* @param packageDetails Packages to install or uninstall
* @param scriptMode can be 'install' or 'uninstall'
*/
- protected async executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails): Promise {
+ protected async executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails, databaseName: string): Promise {
let connection = await this.getCurrentConnection();
let credentials = await this._apiWrapper.getCredentials(connection.connectionId);
if (connection) {
let port = '1433';
let server = connection.serverName;
- let database = connection.databaseName ? `, database="${connection.databaseName}"` : '';
+ let database = databaseName ? `, database="${databaseName}"` : '';
let index = connection.serverName.indexOf(',');
if (index > 0) {
port = connection.serverName.substring(index + 1);
diff --git a/extensions/machine-learning-services/src/packageManagement/sqlRPackageManageProvider.ts b/extensions/machine-learning-services/src/packageManagement/sqlRPackageManageProvider.ts
index a947ce1e66..c40560e4b8 100644
--- a/extensions/machine-learning-services/src/packageManagement/sqlRPackageManageProvider.ts
+++ b/extensions/machine-learning-services/src/packageManagement/sqlRPackageManageProvider.ts
@@ -10,7 +10,7 @@ import * as nbExtensionApis from '../typings/notebookServices';
import { ApiWrapper } from '../common/apiWrapper';
import { ProcessService } from '../common/processService';
import { Config } from '../configurations/config';
-import { SqlPackageManageProviderBase, ScriptMode } from './SqlPackageManageProviderBase';
+import { SqlPackageManageProviderBase, ScriptMode } from './packageManageProviderBase';
import { HttpClient } from '../common/httpClient';
import * as constants from '../common/constants';
import { PackageManagementService } from './packageManagementService';
@@ -54,8 +54,8 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
/**
* Returns list of packages
*/
- protected async fetchPackages(): Promise {
- return await this._service.getRPackages(await this.getCurrentConnection());
+ protected async fetchPackages(databaseName: string): Promise {
+ return await this._service.getRPackages(await this.getCurrentConnection(), databaseName);
}
/**
@@ -63,12 +63,12 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
* @param packageDetails Packages to install or uninstall
* @param scriptMode can be 'install' or 'uninstall'
*/
- protected async executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails): Promise {
+ protected async executeScripts(scriptMode: ScriptMode, packageDetails: nbExtensionApis.IPackageDetails, databaseName: string): Promise {
let connection = await this.getCurrentConnection();
let credentials = await this._apiWrapper.getCredentials(connection.connectionId);
if (connection) {
- let database = connection.databaseName ? `, database="${connection.databaseName}"` : '';
+ let database = databaseName ? `, database="${databaseName}"` : '';
let connectionParts = `server="${connection.serverName}", uid="${connection.userName}", pwd="${credentials[azdata.ConnectionOptionSpecialType.password]}"${database}`;
let rCommandScript = scriptMode === ScriptMode.Install ? 'sql_install.packages' : 'sql_remove.packages';
diff --git a/extensions/machine-learning-services/src/test/packageManagement/packageManager.test.ts b/extensions/machine-learning-services/src/test/packageManagement/packageManager.test.ts
index 950a2f60be..ca85657c92 100644
--- a/extensions/machine-learning-services/src/test/packageManagement/packageManager.test.ts
+++ b/extensions/machine-learning-services/src/test/packageManagement/packageManager.test.ts
@@ -116,7 +116,7 @@ describe('Package Manager', () => {
it('installDependencies Should install packages that are not already installed', async function (): Promise {
let testContext = createContext();
- //let packagesInstalled = false;
+ let packagesInstalled = false;
let installedPackages = `[
{"name":"pymssql","version":"2.1.4"}
]`;
@@ -128,15 +128,67 @@ describe('Package Manager', () => {
});
testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
if (command.indexOf('pip install') > 0) {
- //packagesInstalled = true;
+ packagesInstalled = true;
}
return Promise.resolve(installedPackages);
});
let packageManager = createPackageManager(testContext);
await packageManager.installDependencies();
- //should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
- //should.equal(packagesInstalled, true);
+ should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
+ should.equal(packagesInstalled, true);
+ });
+
+ it('installDependencies Should not install packages if runtime is disabled in setting', async function (): Promise {
+ let testContext = createContext();
+ testContext.config.setup(x => x.rEnabled).returns(() => false);
+ testContext.config.setup(x => x.pythonEnabled).returns(() => false);
+ let packagesInstalled = false;
+ let installedPackages = `[
+ {"name":"pymssql","version":"2.1.4"}
+ ]`;
+ testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
+ label: 'Yes'
+ }));
+ testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
+ operationInfo.operation(testContext.op);
+ });
+ testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
+ if (command.indexOf('pip install') > 0 || command.indexOf('install.packages') > 0) {
+ packagesInstalled = true;
+ }
+ return Promise.resolve(installedPackages);
+ });
+
+ let packageManager = createPackageManager(testContext);
+ await packageManager.installDependencies();
+ should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
+ should.equal(packagesInstalled, false);
+ });
+
+ it('installDependencies Should install packages that have older version installed', async function (): Promise {
+ let testContext = createContext();
+ let packagesInstalled = false;
+ let installedPackages = `[
+ {"name":"sqlmlutils","version":"0.1.1"}
+ ]`;
+ testContext.apiWrapper.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
+ label: 'Yes'
+ }));
+ testContext.apiWrapper.setup(x => x.startBackgroundOperation(TypeMoq.It.isAny())).returns((operationInfo: azdata.BackgroundOperationInfo) => {
+ operationInfo.operation(testContext.op);
+ });
+ testContext.processService.setup(x => x.executeBufferedCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((command) => {
+ if (command.indexOf('pip install') > 0) {
+ packagesInstalled = true;
+ }
+ return Promise.resolve(installedPackages);
+ });
+
+ let packageManager = createPackageManager(testContext);
+ await packageManager.installDependencies();
+ should.equal(testContext.getOpStatus(), azdata.TaskStatus.Succeeded);
+ should.equal(packagesInstalled, true);
});
it('installDependencies Should install packages if list packages fails', async function (): Promise {
@@ -197,7 +249,7 @@ describe('Package Manager', () => {
{ name: 'pymssql', version: '2.1.4' },
{ name: 'sqlmlutils', version: '' }
]);
- testContext.config.setup(x => x.requiredSqlPythonPackages).returns( () => [
+ testContext.config.setup(x => x.requiredSqlRPackages).returns( () => [
{ name: 'RODBCext', repository: 'https://cran.microsoft.com' },
{ name: 'sqlmlutils', fileName: 'sqlmlutils_0.7.1.zip', downloadUrl: 'https://github.com/microsoft/sqlmlutils/blob/master/R/dist/sqlmlutils_0.7.1.zip?raw=true'}
]);
diff --git a/extensions/machine-learning-services/src/test/packageManagement/sqlPythonPackageManageProvider.test.ts b/extensions/machine-learning-services/src/test/packageManagement/sqlPythonPackageManageProvider.test.ts
index 0ee3592e29..ceb2f50202 100644
--- a/extensions/machine-learning-services/src/test/packageManagement/sqlPythonPackageManageProvider.test.ts
+++ b/extensions/machine-learning-services/src/test/packageManagement/sqlPythonPackageManageProvider.test.ts
@@ -7,7 +7,6 @@ import * as azdata from 'azdata';
import * as should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
-import * as constants from '../../common/constants';
import { SqlPythonPackageManageProvider } from '../../packageManagement/sqlPythonPackageManageProvider';
import { createContext, TestContext } from './utils';
import * as nbExtensionApis from '../../typings/notebookServices';
@@ -40,10 +39,10 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
+ testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'a-name',
@@ -72,10 +71,10 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
+ testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'b-name',
@@ -95,10 +94,10 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
let packages: nbExtensionApis.IPackageDetails[];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
+ testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
@@ -108,10 +107,10 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
+ testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
@@ -152,7 +151,7 @@ describe('SQL Python Package Manager', () => {
});
let provider = createProvider(testContext);
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
@@ -192,7 +191,7 @@ describe('SQL Python Package Manager', () => {
});
let provider = createProvider(testContext);
- await provider.uninstallPackages(packages);
+ await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
@@ -233,7 +232,7 @@ describe('SQL Python Package Manager', () => {
});
let provider = createProvider(testContext);
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
@@ -255,7 +254,7 @@ describe('SQL Python Package Manager', () => {
let provider = createProvider(testContext);
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
@@ -277,7 +276,7 @@ describe('SQL Python Package Manager', () => {
let provider = createProvider(testContext);
- await provider.uninstallPackages(packages);
+ await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
@@ -346,42 +345,44 @@ describe('SQL Python Package Manager', () => {
should.deepEqual(actual, packagePreview);
});
- it('getLocationTitle Should default string for no connection', async function (): Promise {
+ it('getLocations Should return empty array for no connection', async function (): Promise {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
+ let actual = await provider.getLocations();
- should.deepEqual(actual, constants.noConnectionError);
+ should.deepEqual(actual, []);
});
- it('getLocationTitle Should return connection title string for valid connection', async function (): Promise {
+ it('getLocations Should return database names for valid connection', async function (): Promise {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
+ const databaseNames = [
+ 'db1',
+ 'db2'
+ ];
+ const expected = [
+ {
+ displayName: 'db1',
+ name: 'db1'
+ },
+ {
+ displayName: 'db2',
+ name: 'db2'
+ }
+ ];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
+ testContext.apiWrapper.setup(x => x.listDatabases(connection.connectionId)).returns(() => { return Promise.resolve(databaseNames); });
let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
+ let actual = await provider.getLocations();
- should.deepEqual(actual, `${connection.serverName} ${connection.databaseName}`);
- });
-
- it('getLocationTitle Should return server name as connection title if there is not database name', async function (): Promise {
- let testContext = createContext();
-
- let connection = new azdata.connection.ConnectionProfile();
- connection.serverName = 'serverName';
- testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
-
- let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
-
- should.deepEqual(actual, `${connection.serverName} `);
+ should.deepEqual(actual, expected);
});
function createProvider(testContext: TestContext): SqlPythonPackageManageProvider {
diff --git a/extensions/machine-learning-services/src/test/packageManagement/sqlRPackageManageProvider.test.ts b/extensions/machine-learning-services/src/test/packageManagement/sqlRPackageManageProvider.test.ts
index 6752a00dc1..4bf4146785 100644
--- a/extensions/machine-learning-services/src/test/packageManagement/sqlRPackageManageProvider.test.ts
+++ b/extensions/machine-learning-services/src/test/packageManagement/sqlRPackageManageProvider.test.ts
@@ -7,7 +7,6 @@ import * as azdata from 'azdata';
import * as should from 'should';
import 'mocha';
import * as TypeMoq from 'typemoq';
-import * as constants from '../../common/constants';
import { SqlRPackageManageProvider } from '../../packageManagement/sqlRPackageManageProvider';
import { createContext, TestContext } from './utils';
import * as nbExtensionApis from '../../typings/notebookServices';
@@ -40,10 +39,10 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
+ testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected = [
{
'name': 'a-name',
@@ -63,10 +62,10 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
let packages: nbExtensionApis.IPackageDetails[];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
+ testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
@@ -76,10 +75,10 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
- testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
+ testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
- let actual = await provider.listPackages();
+ let actual = await provider.listPackages(connection.databaseName);
let expected: nbExtensionApis.IPackageDetails[] = [];
should.deepEqual(actual, expected);
});
@@ -118,7 +117,7 @@ describe('SQL R Package Manager', () => {
});
let provider = createProvider(testContext);
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
@@ -157,7 +156,7 @@ describe('SQL R Package Manager', () => {
});
let provider = createProvider(testContext);
- await provider.uninstallPackages(packages);
+ await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, true);
});
@@ -179,7 +178,7 @@ describe('SQL R Package Manager', () => {
let provider = createProvider(testContext);
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
@@ -201,7 +200,7 @@ describe('SQL R Package Manager', () => {
let provider = createProvider(testContext);
- await provider.uninstallPackages(packages);
+ await provider.uninstallPackages(packages, connection.databaseName);
should.deepEqual(packagesUpdated, false);
});
@@ -271,42 +270,44 @@ describe('SQL R Package Manager', () => {
should.deepEqual(actual, packagePreview);
});
- it('getLocationTitle Should default string for no connection', async function (): Promise {
+ it('getLocations Should return empty array for no connection', async function (): Promise {
let testContext = createContext();
let connection: azdata.connection.ConnectionProfile;
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
+ let actual = await provider.getLocations();
- should.deepEqual(actual, constants.noConnectionError);
+ should.deepEqual(actual, []);
});
- it('getLocationTitle Should return connection title string for valid connection', async function (): Promise {
+ it('getLocations Should return database names for valid connection', async function (): Promise {
let testContext = createContext();
let connection = new azdata.connection.ConnectionProfile();
connection.serverName = 'serverName';
connection.databaseName = 'databaseName';
+ const databaseNames = [
+ 'db1',
+ 'db2'
+ ];
+ const expected = [
+ {
+ displayName: 'db1',
+ name: 'db1'
+ },
+ {
+ displayName: 'db2',
+ name: 'db2'
+ }
+ ];
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
+ testContext.apiWrapper.setup(x => x.listDatabases(connection.connectionId)).returns(() => { return Promise.resolve(databaseNames); });
let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
+ let actual = await provider.getLocations();
- should.deepEqual(actual, `${connection.serverName} ${connection.databaseName}`);
- });
-
- it('getLocationTitle Should return server name as connection title if there is not database name', async function (): Promise {
- let testContext = createContext();
-
- let connection = new azdata.connection.ConnectionProfile();
- connection.serverName = 'serverName';
- testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
-
- let provider = createProvider(testContext);
- let actual = await provider.getLocationTitle();
-
- should.deepEqual(actual, `${connection.serverName} `);
+ should.deepEqual(actual, expected);
});
function createProvider(testContext: TestContext): SqlRPackageManageProvider {
diff --git a/extensions/machine-learning-services/src/test/queryRunner.test.ts b/extensions/machine-learning-services/src/test/queryRunner.test.ts
index 6183ae593e..7786da774f 100644
--- a/extensions/machine-learning-services/src/test/queryRunner.test.ts
+++ b/extensions/machine-learning-services/src/test/queryRunner.test.ts
@@ -59,7 +59,7 @@ describe('Query Runner', () => {
let queryProvider: azdata.QueryProvider;
testContext.apiWrapper.setup(x => x.getProvider(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => queryProvider);
- let actual = await queryRunner.getPythonPackages(connection);
+ let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, []);
});
@@ -70,7 +70,7 @@ describe('Query Runner', () => {
testContext.queryProvider.runQueryAndReturn = () => { return Promise.reject(); };
testContext.apiWrapper.setup(x => x.getProvider(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
- let actual = await queryRunner.getPythonPackages(connection);
+ let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, []);
});
@@ -117,7 +117,7 @@ describe('Query Runner', () => {
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
- let actual = await queryRunner.getPythonPackages(connection);
+ let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, expected);
});
@@ -138,7 +138,7 @@ describe('Query Runner', () => {
testContext.queryProvider.runQueryAndReturn = () => { return Promise.resolve(result); };
testContext.apiWrapper.setup(x => x.getProvider(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => testContext.queryProvider);
- let actual = await queryRunner.getPythonPackages(connection);
+ let actual = await queryRunner.getPythonPackages(connection, connection.databaseName);
should.deepEqual(actual, expected);
});
diff --git a/extensions/machine-learning-services/src/test/views/externalLanguages/languageController.test.ts b/extensions/machine-learning-services/src/test/views/externalLanguages/languageController.test.ts
index 61c998b942..c0d0e3e898 100644
--- a/extensions/machine-learning-services/src/test/views/externalLanguages/languageController.test.ts
+++ b/extensions/machine-learning-services/src/test/views/externalLanguages/languageController.test.ts
@@ -8,7 +8,7 @@ import 'mocha';
import * as TypeMoq from 'typemoq';
import { createContext } from './utils';
import { LanguageController } from '../../../views/externalLanguages/languageController';
-import * as mssql from '../../../../../mssql/src/mssql';
+import * as mssql from '../../../../../mssql';
describe('External Languages Controller', () => {
it('Should open dialog for manage languages successfully ', async function (): Promise {
diff --git a/extensions/machine-learning-services/src/test/views/externalLanguages/languagesDialogModel.test.ts b/extensions/machine-learning-services/src/test/views/externalLanguages/languagesDialogModel.test.ts
index b41b44f898..b6c8d78249 100644
--- a/extensions/machine-learning-services/src/test/views/externalLanguages/languagesDialogModel.test.ts
+++ b/extensions/machine-learning-services/src/test/views/externalLanguages/languagesDialogModel.test.ts
@@ -6,7 +6,7 @@
import * as should from 'should';
import 'mocha';
import { createContext } from './utils';
-import * as mssql from '../../../../../mssql/src/mssql';
+import * as mssql from '../../../../../mssql';
import { LanguageService } from '../../../externalLanguage/languageService';
describe('External Languages Dialog Model', () => {
diff --git a/extensions/machine-learning-services/src/test/views/externalLanguages/utils.ts b/extensions/machine-learning-services/src/test/views/externalLanguages/utils.ts
index 95420783b7..c3aa267494 100644
--- a/extensions/machine-learning-services/src/test/views/externalLanguages/utils.ts
+++ b/extensions/machine-learning-services/src/test/views/externalLanguages/utils.ts
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
import * as TypeMoq from 'typemoq';
import { ApiWrapper } from '../../../common/apiWrapper';
import { LanguageViewBase } from '../../../views/externalLanguages/languageViewBase';
-import * as mssql from '../../../../../mssql/src/mssql';
+import * as mssql from '../../../../../mssql';
import { LanguageService } from '../../../externalLanguage/languageService';
import { createViewContext } from '../utils';
diff --git a/extensions/machine-learning-services/src/typings/notebookServices.d.ts b/extensions/machine-learning-services/src/typings/notebookServices.d.ts
index 74ad82a49d..eb643b105e 100644
--- a/extensions/machine-learning-services/src/typings/notebookServices.d.ts
+++ b/extensions/machine-learning-services/src/typings/notebookServices.d.ts
@@ -51,13 +51,56 @@ export interface IPackageOverview {
summary: string;
}
-export interface IPackageManageProvider {
- providerId: string;
- packageTarget: IPackageTarget;
- listPackages(): Promise
- installPackages(package: IPackageDetails[], useMinVersion: boolean): Promise;
- uninstallPackages(package: IPackageDetails[]): Promise;
- canUseProvider(): Promise;
- getLocationTitle(): Promise;
- getPackageOverview(packageName: string): Promise
+export interface IPackageLocation {
+ name: string;
+ displayName: string;
+}
+
+/**
+ * Package manage provider interface
+ */
+export interface IPackageManageProvider {
+ /**
+ * Provider id
+ */
+ providerId: string;
+
+ /**
+ * package target
+ */
+ packageTarget: IPackageTarget;
+
+ /**
+ * Returns list of installed packages
+ */
+ listPackages(location?: string): Promise;
+
+ /**
+ * Installs give packages
+ * @param package Packages to install
+ * @param useMinVersion if true, minimal version will be used
+ */
+ installPackages(package: IPackageDetails[], useMinVersion: boolean, location?: string): Promise;
+
+ /**
+ * Uninstalls given packages
+ * @param package package to uninstall
+ */
+ uninstallPackages(package: IPackageDetails[], location?: string): Promise;
+
+ /**
+ * Returns true if the provider can be used in current context
+ */
+ canUseProvider(): Promise;
+
+ /**
+ * Returns location title
+ */
+ getLocations(): Promise;
+
+ /**
+ * Returns Package Overview
+ * @param packageName package name
+ */
+ getPackageOverview(packageName: string): Promise;
}
diff --git a/extensions/machine-learning-services/src/views/externalLanguages/languageContentView.ts b/extensions/machine-learning-services/src/views/externalLanguages/languageContentView.ts
index 3be3d04c04..1df7404d49 100644
--- a/extensions/machine-learning-services/src/views/externalLanguages/languageContentView.ts
+++ b/extensions/machine-learning-services/src/views/externalLanguages/languageContentView.ts
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
-import * as mssql from '../../../../mssql/src/mssql';
+import * as mssql from '../../../../mssql';
import { LanguageViewBase } from './languageViewBase';
import * as constants from '../../common/constants';
import { ApiWrapper } from '../../common/apiWrapper';
diff --git a/extensions/machine-learning-services/src/views/externalLanguages/languageController.ts b/extensions/machine-learning-services/src/views/externalLanguages/languageController.ts
index 63ad405bd4..7d18ee14b7 100644
--- a/extensions/machine-learning-services/src/views/externalLanguages/languageController.ts
+++ b/extensions/machine-learning-services/src/views/externalLanguages/languageController.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import * as mssql from '../../../../mssql/src/mssql';
+import * as mssql from '../../../../mssql';
import { ApiWrapper } from '../../common/apiWrapper';
import { LanguageService } from '../../externalLanguage/languageService';
import { LanguagesDialog } from './languagesDialog';
diff --git a/extensions/machine-learning-services/src/views/externalLanguages/languageViewBase.ts b/extensions/machine-learning-services/src/views/externalLanguages/languageViewBase.ts
index 9e3cc04550..df432451a5 100644
--- a/extensions/machine-learning-services/src/views/externalLanguages/languageViewBase.ts
+++ b/extensions/machine-learning-services/src/views/externalLanguages/languageViewBase.ts
@@ -7,7 +7,7 @@ import * as azdata from 'azdata';
import * as vscode from 'vscode';
import * as constants from '../../common/constants';
import { ApiWrapper } from '../../common/apiWrapper';
-import * as mssql from '../../../../mssql/src/mssql';
+import * as mssql from '../../../../mssql';
import * as path from 'path';
export interface LanguageUpdateModel {
diff --git a/extensions/machine-learning-services/src/views/externalLanguages/languagesTable.ts b/extensions/machine-learning-services/src/views/externalLanguages/languagesTable.ts
index 838afc5542..3fe1dda8a1 100644
--- a/extensions/machine-learning-services/src/views/externalLanguages/languagesTable.ts
+++ b/extensions/machine-learning-services/src/views/externalLanguages/languagesTable.ts
@@ -5,7 +5,7 @@
import * as azdata from 'azdata';
import * as constants from '../../common/constants';
-import * as mssql from '../../../../mssql/src/mssql';
+import * as mssql from '../../../../mssql';
import { LanguageViewBase } from './languageViewBase';
import { ApiWrapper } from '../../common/apiWrapper';
diff --git a/extensions/markdown-language-features/src/features/previewManager.ts b/extensions/markdown-language-features/src/features/previewManager.ts
index af7051f357..6970c98ec0 100644
--- a/extensions/markdown-language-features/src/features/previewManager.ts
+++ b/extensions/markdown-language-features/src/features/previewManager.ts
@@ -151,7 +151,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
}
public async openCustomDocument(uri: vscode.Uri) {
- return new vscode.CustomDocument(this.customEditorViewType, uri);
+ return new vscode.CustomDocument(uri);
}
public async resolveCustomTextEditor(
diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json
index 3217d48408..1dbef5f18b 100644
--- a/extensions/mssql/package.json
+++ b/extensions/mssql/package.json
@@ -485,6 +485,32 @@
}
]
}
+ },
+ {
+ "id": "mssql-databases",
+ "description": "%mssql.tabs.databases%",
+ "provider": "*",
+ "title": "%mssql.tabs.databases%",
+ "when": "dashboardContext == 'server'",
+ "group": "home",
+ "icon": {
+ "light": "resources/light/database.svg",
+ "dark": "resources/dark/database_inverse.svg"
+ },
+ "container": {
+ "widgets-container": [
+ {
+ "name": "%explorer-widget-title%",
+ "gridItemConfig": {
+ "sizex": 3,
+ "sizey": 3
+ },
+ "widget": {
+ "explorer-widget": {}
+ }
+ }
+ ]
+ }
}
],
"connectionProvider": {
diff --git a/extensions/mssql/package.nls.json b/extensions/mssql/package.nls.json
index 98c9c9a209..8d4cafee8f 100644
--- a/extensions/mssql/package.nls.json
+++ b/extensions/mssql/package.nls.json
@@ -140,5 +140,8 @@
"mssql.connectionOptions.packetSize.displayName": "Packet size",
"mssql.connectionOptions.packetSize.description": "Size in bytes of the network packets used to communicate with an instance of SQL Server",
"mssql.connectionOptions.typeSystemVersion.displayName": "Type system version",
- "mssql.connectionOptions.typeSystemVersion.description": "Indicates which server type system then provider will expose through the DataReader"
+ "mssql.connectionOptions.typeSystemVersion.description": "Indicates which server type system the provider will expose through the DataReader",
+
+ "mssql.tabs.databases": "Databases",
+ "explorer-widget-title": "Search"
}
diff --git a/extensions/mssql/resources/dark/database_inverse.svg b/extensions/mssql/resources/dark/database_inverse.svg
index fbb38a2836..5fb31021fb 100644
--- a/extensions/mssql/resources/dark/database_inverse.svg
+++ b/extensions/mssql/resources/dark/database_inverse.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/extensions/mssql/resources/light/database.svg b/extensions/mssql/resources/light/database.svg
index c11f929ce4..bc00d48e2b 100644
--- a/extensions/mssql/resources/light/database.svg
+++ b/extensions/mssql/resources/light/database.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/extensions/notebook/src/dialog/managePackages/addNewPackageTab.ts b/extensions/notebook/src/dialog/managePackages/addNewPackageTab.ts
index d0223f4f6f..c111e9b27e 100644
--- a/extensions/notebook/src/dialog/managePackages/addNewPackageTab.ts
+++ b/extensions/notebook/src/dialog/managePackages/addNewPackageTab.ts
@@ -147,7 +147,7 @@ export class AddNewPackageTab {
let pipPackage: PipPackageOverview;
pipPackage = await this.dialog.model.getPackageOverview(packageName);
- if (!pipPackage.versions || pipPackage.versions.length === 0) {
+ if (!pipPackage?.versions || pipPackage.versions.length === 0) {
this.dialog.showErrorMessage(
localize('managePackages.noVersionsFound',
"Could not find any valid versions for the specified package"));
diff --git a/extensions/notebook/src/dialog/managePackages/installedPackagesTab.ts b/extensions/notebook/src/dialog/managePackages/installedPackagesTab.ts
index 60302554d8..c9962e625f 100644
--- a/extensions/notebook/src/dialog/managePackages/installedPackagesTab.ts
+++ b/extensions/notebook/src/dialog/managePackages/installedPackagesTab.ts
@@ -20,11 +20,13 @@ export class InstalledPackagesTab {
private installedPkgTab: azdata.window.DialogTab;
private packageTypeDropdown: azdata.DropDownComponent;
- private locationComponent: azdata.TextComponent;
+ private locationComponent: azdata.Component;
private installedPackageCount: azdata.TextComponent;
private installedPackagesTable: azdata.TableComponent;
private installedPackagesLoader: azdata.LoadingComponent;
private uninstallPackageButton: azdata.ButtonComponent;
+ private view: azdata.ModelView | undefined;
+ private formBuilder: azdata.FormBuilder;
constructor(private dialog: ManagePackagesDialog, private jupyterInstallation: JupyterServerInstallation) {
this.prompter = new CodeAdapter();
@@ -32,14 +34,7 @@ export class InstalledPackagesTab {
this.installedPkgTab = azdata.window.createTab(localize('managePackages.installedTabTitle', "Installed"));
this.installedPkgTab.registerContent(async view => {
-
- // TODO: only supporting single location for now. We should add a drop down for multi locations mode
- //
- let locationTitle = await this.dialog.model.getLocationTitle();
- this.locationComponent = view.modelBuilder.text().withProperties({
- value: locationTitle
- }).component();
-
+ this.view = view;
let dropdownValues = this.dialog.model.getPackageTypes().map(x => {
return {
name: x.providerId,
@@ -52,11 +47,17 @@ export class InstalledPackagesTab {
value: defaultPackageType
}).component();
this.dialog.changeProvider(defaultPackageType.providerId);
- this.packageTypeDropdown.onValueChanged(() => {
- this.dialog.resetPages((this.packageTypeDropdown.value).name)
- .catch(err => {
- this.dialog.showErrorMessage(utils.getErrorMessage(err));
- });
+ this.packageTypeDropdown.onValueChanged(async () => {
+ this.dialog.changeProvider((this.packageTypeDropdown.value).name);
+ try {
+ await this.resetLocations();
+ await this.dialog.resetPages();
+ }
+ catch (err) {
+ this.dialog.showErrorMessage(utils.getErrorMessage(err));
+
+ }
+
});
this.installedPackageCount = view.modelBuilder.text().withProperties({
@@ -81,11 +82,8 @@ export class InstalledPackagesTab {
}).component();
this.uninstallPackageButton.onDidClick(() => this.doUninstallPackage());
- let formModel = view.modelBuilder.formContainer()
+ this.formBuilder = view.modelBuilder.formContainer()
.withFormItems([{
- component: this.locationComponent,
- title: localize('managePackages.location', "Location")
- }, {
component: this.packageTypeDropdown,
title: localize('managePackages.packageType', "Package Type")
}, {
@@ -97,10 +95,11 @@ export class InstalledPackagesTab {
}, {
component: this.uninstallPackageButton,
title: ''
- }]).component();
+ }]);
+ await this.resetLocations();
this.installedPackagesLoader = view.modelBuilder.loadingComponent()
- .withItem(formModel)
+ .withItem(this.formBuilder.component())
.withProperties({
loading: true
}).component();
@@ -112,6 +111,68 @@ export class InstalledPackagesTab {
});
}
+ private async resetLocations(): Promise {
+ if (this.view) {
+ if (this.locationComponent) {
+ this.formBuilder.removeFormItem({
+ component: this.locationComponent,
+ title: localize('managePackages.location', "Location")
+ });
+ }
+
+ this.locationComponent = await InstalledPackagesTab.getLocationComponent(this.view, this.dialog);
+
+ this.formBuilder.insertFormItem({
+ component: this.locationComponent,
+ title: localize('managePackages.location', "Location")
+ }, 1);
+ }
+ }
+
+ /**
+ * Creates a component for package locations
+ * @param view Model view
+ * @param dialog Manage package dialog
+ */
+ public static async getLocationComponent(view: azdata.ModelView, dialog: ManagePackagesDialog): Promise {
+ const locations = await dialog.model.getLocations();
+ let component: azdata.Component;
+ if (locations && locations.length === 1) {
+ component = view.modelBuilder.text().withProperties({
+ value: locations[0].displayName
+ }).component();
+ } else if (locations) {
+ let dropdownValues = locations.map(x => {
+ return {
+ name: x.name,
+ displayName: x.displayName
+ };
+ });
+ let locationDropDown = view.modelBuilder.dropDown().withProperties({
+ values: dropdownValues,
+ value: dropdownValues[0]
+ }).component();
+
+ locationDropDown.onValueChanged(async () => {
+ dialog.changeLocation((locationDropDown.value).name);
+ try {
+ await dialog.resetPages();
+ }
+ catch (err) {
+ dialog.showErrorMessage(utils.getErrorMessage(err));
+ }
+ });
+ component = locationDropDown;
+ } else {
+ component = view.modelBuilder.text().withProperties({
+ }).component();
+ }
+ if (locations && locations.length > 0) {
+ dialog.changeLocation(locations[0].name);
+ }
+ return component;
+ }
+
public get tab(): azdata.window.DialogTab {
return this.installedPkgTab;
}
diff --git a/extensions/notebook/src/dialog/managePackages/managePackagesDialog.ts b/extensions/notebook/src/dialog/managePackages/managePackagesDialog.ts
index 644888c61d..e3bcdcc69a 100644
--- a/extensions/notebook/src/dialog/managePackages/managePackagesDialog.ts
+++ b/extensions/notebook/src/dialog/managePackages/managePackagesDialog.ts
@@ -67,14 +67,17 @@ export class ManagePackagesDialog {
}
/**
- * Resets the tabs for given provider Id
- * @param providerId Package Management Provider Id
+ * Changes the current location
+ * @param location location name
*/
- public async resetPages(providerId: string): Promise {
+ public changeLocation(location: string): void {
+ this.model.changeLocation(location);
+ }
- // Change the provider in the model
- //
- this.changeProvider(providerId);
+ /**
+ * Resets the tabs for given provider Id
+ */
+ public async resetPages(): Promise {
// Load packages for given provider
//
diff --git a/extensions/notebook/src/dialog/managePackages/managePackagesDialogModel.ts b/extensions/notebook/src/dialog/managePackages/managePackagesDialogModel.ts
index 66bee24037..4300f457d1 100644
--- a/extensions/notebook/src/dialog/managePackages/managePackagesDialogModel.ts
+++ b/extensions/notebook/src/dialog/managePackages/managePackagesDialogModel.ts
@@ -4,10 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { JupyterServerInstallation } from '../../jupyter/jupyterServerInstallation';
-import { IPackageManageProvider, IPackageDetails, IPackageOverview } from '../../types';
+import { IPackageManageProvider, IPackageDetails, IPackageOverview, IPackageLocation } from '../../types';
export interface ManagePackageDialogOptions {
- multiLocations: boolean;
defaultLocation?: string;
defaultProviderId?: string;
}
@@ -23,11 +22,12 @@ export interface ProviderPackageType {
export class ManagePackagesDialogModel {
private _currentProvider: string;
+ private _currentLocation: string;
/**
* A set for locations
*/
- private _locations: Set = new Set();
+ private _locationTypes: Set = new Set();
/**
* Map of locations to providers
@@ -77,15 +77,10 @@ export class ManagePackagesDialogModel {
if (this._options.defaultProviderId && !this._packageManageProviders.has(this._options.defaultProviderId)) {
throw new Error(`Invalid default provider id '${this._options.defaultProviderId}`);
}
-
- if (!this._options.multiLocations && !this.defaultLocation) {
- throw new Error('Default location not specified for single location mode');
- }
}
private get defaultOptions(): ManagePackageDialogOptions {
return {
- multiLocations: true,
defaultLocation: undefined,
defaultProviderId: undefined
};
@@ -120,13 +115,6 @@ export class ManagePackagesDialogModel {
return undefined;
}
- /**
- * Returns true if multi locations mode is enabled
- */
- public get multiLocationMode(): boolean {
- return this.options.multiLocations;
- }
-
/**
* Returns options
*/
@@ -135,17 +123,17 @@ export class ManagePackagesDialogModel {
}
/**
- * returns the array of target locations
+ * returns the array of target location types
*/
- public get targetLocations(): string[] {
- return Array.from(this._locations.keys());
+ public get targetLocationTypes(): string[] {
+ return Array.from(this._locationTypes.keys());
}
/**
* Returns the default location
*/
public get defaultLocation(): string {
- return this.options.defaultLocation || this.targetLocations[0];
+ return this.options.defaultLocation || this.targetLocationTypes[0];
}
/**
@@ -164,8 +152,8 @@ export class ManagePackagesDialogModel {
for (let index = 0; index < keyArray.length; index++) {
const element = this.packageManageProviders.get(keyArray[index]);
if (await element.canUseProvider()) {
- if (!this._locations.has(element.packageTarget.location)) {
- this._locations.add(element.packageTarget.location);
+ if (!this._locationTypes.has(element.packageTarget.location)) {
+ this._locationTypes.add(element.packageTarget.location);
}
if (!this._packageTypes.has(element.packageTarget.location)) {
this._packageTypes.set(element.packageTarget.location, []);
@@ -205,7 +193,7 @@ export class ManagePackagesDialogModel {
public async listPackages(): Promise {
let provider = this.currentPackageManageProvider;
if (provider) {
- return await provider.listPackages();
+ return await provider.listPackages(this._currentLocation);
} else {
throw new Error('Current Provider is not set');
}
@@ -222,6 +210,13 @@ export class ManagePackagesDialogModel {
}
}
+ /**
+ * Changes the current location
+ */
+ public changeLocation(location: string): void {
+ this._currentLocation = location;
+ }
+
/**
* Installs given packages using current provider
* @param packages Packages to install
@@ -229,7 +224,7 @@ export class ManagePackagesDialogModel {
public async installPackages(packages: IPackageDetails[]): Promise {
let provider = this.currentPackageManageProvider;
if (provider) {
- await provider.installPackages(packages, false);
+ await provider.installPackages(packages, false, this._currentLocation);
} else {
throw new Error('Current Provider is not set');
}
@@ -238,10 +233,10 @@ export class ManagePackagesDialogModel {
/**
* Returns the location title for current provider
*/
- public async getLocationTitle(): Promise {
+ public async getLocations(): Promise {
let provider = this.currentPackageManageProvider;
if (provider) {
- return await provider.getLocationTitle();
+ return await provider.getLocations();
}
return Promise.resolve(undefined);
}
@@ -253,7 +248,7 @@ export class ManagePackagesDialogModel {
public async uninstallPackages(packages: IPackageDetails[]): Promise {
let provider = this.currentPackageManageProvider;
if (provider) {
- await provider.uninstallPackages(packages);
+ await provider.uninstallPackages(packages, this._currentLocation);
} else {
throw new Error('Current Provider is not set');
}
diff --git a/extensions/notebook/src/jupyter/jupyterController.ts b/extensions/notebook/src/jupyter/jupyterController.ts
index 3921185430..7873066c33 100644
--- a/extensions/notebook/src/jupyter/jupyterController.ts
+++ b/extensions/notebook/src/jupyter/jupyterController.ts
@@ -207,7 +207,6 @@ export class JupyterController implements vscode.Disposable {
try {
if (!options) {
options = {
- multiLocations: false,
defaultLocation: constants.localhostName,
defaultProviderId: LocalPipPackageManageProvider.ProviderId
};
diff --git a/extensions/notebook/src/jupyter/localCondaPackageManageProvider.ts b/extensions/notebook/src/jupyter/localCondaPackageManageProvider.ts
index c5f4b60f44..a0746218cd 100644
--- a/extensions/notebook/src/jupyter/localCondaPackageManageProvider.ts
+++ b/extensions/notebook/src/jupyter/localCondaPackageManageProvider.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IPackageManageProvider, IPackageDetails, IPackageTarget, IPackageOverview } from '../types';
+import { IPackageManageProvider, IPackageDetails, IPackageTarget, IPackageOverview, IPackageLocation } from '../types';
import { IJupyterServerInstallation } from './jupyterServerInstallation';
import * as constants from '../common/constants';
import * as utils from '../common/utils';
@@ -35,7 +35,7 @@ export class LocalCondaPackageManageProvider implements IPackageManageProvider {
/**
* Returns list of packages
*/
- public async listPackages(): Promise {
+ public async listPackages(location?: string): Promise {
return await this.jupyterInstallation.getInstalledCondaPackages();
}
@@ -44,7 +44,7 @@ export class LocalCondaPackageManageProvider implements IPackageManageProvider {
* @param packages Packages to install
* @param useMinVersion minimum version
*/
- installPackages(packages: IPackageDetails[], useMinVersion: boolean): Promise {
+ installPackages(packages: IPackageDetails[], useMinVersion: boolean, location?: string): Promise {
return this.jupyterInstallation.installCondaPackages(packages, useMinVersion);
}
@@ -52,7 +52,7 @@ export class LocalCondaPackageManageProvider implements IPackageManageProvider {
* Uninstalls given packages
* @param packages Packages to uninstall
*/
- uninstallPackages(packages: IPackageDetails[]): Promise {
+ uninstallPackages(packages: IPackageDetails[], location?: string): Promise {
return this.jupyterInstallation.uninstallCondaPackages(packages);
}
@@ -66,8 +66,8 @@ export class LocalCondaPackageManageProvider implements IPackageManageProvider {
/**
* Returns location title
*/
- getLocationTitle(): Promise {
- return Promise.resolve(constants.localhostTitle);
+ getLocations(): Promise {
+ return Promise.resolve([{ displayName: constants.localhostTitle, name: constants.localhostName }]);
}
/**
diff --git a/extensions/notebook/src/jupyter/localPipPackageManageProvider.ts b/extensions/notebook/src/jupyter/localPipPackageManageProvider.ts
index 1be49dcc78..4d3372e93f 100644
--- a/extensions/notebook/src/jupyter/localPipPackageManageProvider.ts
+++ b/extensions/notebook/src/jupyter/localPipPackageManageProvider.ts
@@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { IPackageManageProvider, IPackageDetails, IPackageTarget, IPackageOverview } from '../types';
+import { IPackageManageProvider, IPackageDetails, IPackageTarget, IPackageOverview, IPackageLocation } from '../types';
import { IJupyterServerInstallation } from './jupyterServerInstallation';
import * as constants from '../common/constants';
import * as utils from '../common/utils';
@@ -38,7 +38,7 @@ export class LocalPipPackageManageProvider implements IPackageManageProvider {
/**
* Returns list of packages
*/
- public async listPackages(): Promise {
+ public async listPackages(location?: string): Promise {
return await this.jupyterInstallation.getInstalledPipPackages();
}
@@ -47,7 +47,7 @@ export class LocalPipPackageManageProvider implements IPackageManageProvider {
* @param packages Packages to install
* @param useMinVersion minimum version
*/
- installPackages(packages: IPackageDetails[], useMinVersion: boolean): Promise {
+ installPackages(packages: IPackageDetails[], useMinVersion: boolean, location?: string): Promise {
return this.jupyterInstallation.installPipPackages(packages, useMinVersion);
}
@@ -55,7 +55,7 @@ export class LocalPipPackageManageProvider implements IPackageManageProvider {
* Uninstalls given packages
* @param packages Packages to uninstall
*/
- uninstallPackages(packages: IPackageDetails[]): Promise {
+ uninstallPackages(packages: IPackageDetails[], location?: string): Promise {
return this.jupyterInstallation.uninstallPipPackages(packages);
}
@@ -69,8 +69,8 @@ export class LocalPipPackageManageProvider implements IPackageManageProvider {
/**
* Returns location title
*/
- getLocationTitle(): Promise {
- return Promise.resolve(constants.localhostTitle);
+ getLocations(): Promise {
+ return Promise.resolve([{ displayName: constants.localhostTitle, name: constants.localhostName }]);
}
/**
diff --git a/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts
new file mode 100644
index 0000000000..46e5df1798
--- /dev/null
+++ b/extensions/notebook/src/test/managePackages/managePackagesDialog.test.ts
@@ -0,0 +1,295 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+import * as azdata from 'azdata';
+import * as vscode from 'vscode';
+import * as TypeMoq from 'typemoq';
+import { ManagePackagesDialog } from '../../dialog/managePackages/managePackagesDialog';
+import { ManagePackagesDialogModel } from '../../dialog/managePackages/managePackagesDialogModel';
+import { IPackageManageProvider, IPackageLocation } from '../../types';
+import { LocalCondaPackageManageProvider } from '../../jupyter/localCondaPackageManageProvider';
+import { InstalledPackagesTab } from '../../dialog/managePackages/installedPackagesTab';
+import should = require('should');
+
+interface TestContext {
+ view: azdata.ModelView;
+ onClick: vscode.EventEmitter;
+ dialog: TypeMoq.IMock;
+ model: TypeMoq.IMock;
+}
+
+describe('Manage Package Dialog', () => {
+
+ it('getLocationComponent should create text component for one location', async function (): Promise {
+ let testContext = createViewContext();
+ let locations = [
+ {
+ displayName: 'dl1',
+ name: 'nl1'
+ }
+ ];
+ testContext.model.setup(x => x.getLocations()).returns(() => Promise.resolve(locations));
+ testContext.model.setup(x => x.changeLocation('nl1'));
+ testContext.dialog.setup(x => x.changeLocation('nl1'));
+
+ let actual = await InstalledPackagesTab.getLocationComponent(testContext.view, testContext.dialog.object);
+ should.equal('onTextChanged' in actual, true);
+ testContext.dialog.verify(x => x.changeLocation('nl1'), TypeMoq.Times.once());
+ });
+
+ it('getLocationComponent should create text component for undefined location', async function (): Promise {
+ let testContext = createViewContext();
+ let locations: IPackageLocation[] | undefined = undefined;
+ testContext.model.setup(x => x.getLocations()).returns(() => Promise.resolve(locations));
+
+ let actual = await InstalledPackagesTab.getLocationComponent(testContext.view, testContext.dialog.object);
+ should.equal('onTextChanged' in actual, true);
+ });
+
+ it('getLocationComponent should create drop down component for more than one location', async function (): Promise {
+ let testContext = createViewContext();
+ let locations = [
+ {
+ displayName: 'dl1',
+ name: 'nl1'
+ },
+ {
+ displayName: 'dl2',
+ name: 'nl2'
+ }
+ ];
+ testContext.model.setup(x => x.getLocations()).returns(() => Promise.resolve(locations));
+ testContext.dialog.setup(x => x.changeLocation('nl1'));
+ testContext.dialog.setup(x => x.resetPages()).returns(() => Promise.resolve());
+
+ let actual = await InstalledPackagesTab.getLocationComponent(testContext.view, testContext.dialog.object);
+ should.equal('onValueChanged' in actual, true);
+ testContext.dialog.verify(x => x.changeLocation('nl1'), TypeMoq.Times.once());
+ (actual).value = {
+ displayName: 'dl2',
+ name: 'nl2'
+ };
+ testContext.onClick.fire();
+ testContext.dialog.verify(x => x.changeLocation('nl2'), TypeMoq.Times.once());
+ testContext.dialog.verify(x => x.resetPages(), TypeMoq.Times.once());
+
+ });
+
+ it('getLocationComponent should show error if reset pages fails', async function (): Promise {
+ let testContext = createViewContext();
+ let locations = [
+ {
+ displayName: 'dl1',
+ name: 'nl1'
+ },
+ {
+ displayName: 'dl2',
+ name: 'nl2'
+ }
+ ];
+ testContext.model.setup(x => x.getLocations()).returns(() => Promise.resolve(locations));
+ testContext.dialog.setup(x => x.changeLocation('nl1'));
+ testContext.dialog.setup(x => x.resetPages()).throws(new Error('failed'));
+ testContext.dialog.setup(x => x.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve());
+
+ let actual = await InstalledPackagesTab.getLocationComponent(testContext.view, testContext.dialog.object);
+ should.equal('onValueChanged' in actual, true);
+ testContext.dialog.verify(x => x.changeLocation('nl1'), TypeMoq.Times.once());
+ (actual).value = {
+ displayName: 'dl2',
+ name: 'nl2'
+ };
+ testContext.onClick.fire();
+ testContext.dialog.verify(x => x.changeLocation('nl2'), TypeMoq.Times.once());
+ testContext.dialog.verify(x => x.showErrorMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
+
+ });
+
+ function createViewContext(): TestContext {
+ let packageManageProviders = new Map();
+ packageManageProviders.set(LocalCondaPackageManageProvider.ProviderId, new LocalCondaPackageManageProvider(undefined));
+ let model = TypeMoq.Mock.ofInstance(new ManagePackagesDialogModel(undefined, packageManageProviders));
+ let dialog = TypeMoq.Mock.ofInstance(new ManagePackagesDialog(model.object));
+ dialog.setup(x => x.model).returns(() => model.object);
+
+ let onClick: vscode.EventEmitter = new vscode.EventEmitter();
+
+ let componentBase: azdata.Component = {
+ id: '',
+ updateProperties: () => Promise.resolve(),
+ updateProperty: () => Promise.resolve(),
+ updateCssStyles: undefined!,
+ onValidityChanged: undefined!,
+ valid: true,
+ validate: undefined!,
+ focus: undefined!
+ };
+ let button: azdata.ButtonComponent = Object.assign({}, componentBase, {
+ onDidClick: onClick.event
+ });
+ let radioButton: azdata.RadioButtonComponent = Object.assign({}, componentBase, {
+ onDidClick: onClick.event
+ });
+ const components: azdata.Component[] = [];
+ let container = {
+ clearItems: () => { },
+ addItems: () => { },
+ addItem: () => { },
+ removeItem: () => true,
+ insertItem: () => { },
+ items: components,
+ setLayout: () => { }
+ };
+ let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
+ });
+ let flex: azdata.FlexContainer = Object.assign({}, componentBase, container, {
+ });
+
+ let buttonBuilder: azdata.ComponentBuilder = {
+ component: () => button,
+ withProperties: () => buttonBuilder,
+ withValidation: () => buttonBuilder
+ };
+ let radioButtonBuilder: azdata.ComponentBuilder = {
+ component: () => radioButton,
+ withProperties: () => radioButtonBuilder,
+ withValidation: () => radioButtonBuilder
+ };
+ let inputBox: () => azdata.InputBoxComponent = () => Object.assign({}, componentBase, {
+ onTextChanged: undefined!,
+ onEnterKeyPressed: undefined!,
+ value: ''
+ });
+ let image: () => azdata.ImageComponent = () => Object.assign({}, componentBase, {
+
+ });
+ let dropdown: () => azdata.DropDownComponent = () => Object.assign({}, componentBase, {
+ onValueChanged: onClick.event,
+ value: {
+ name: '',
+ displayName: ''
+ },
+ values: []
+ });
+ let declarativeTable: () => azdata.DeclarativeTableComponent = () => Object.assign({}, componentBase, {
+ onDataChanged: undefined!,
+ data: [],
+ columns: []
+ });
+
+ let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, {
+ loading: false,
+ component: undefined!
+ });
+
+ let declarativeTableBuilder: azdata.ComponentBuilder = {
+ component: () => declarativeTable(),
+ withProperties: () => declarativeTableBuilder,
+ withValidation: () => declarativeTableBuilder
+ };
+
+ let loadingBuilder: azdata.LoadingComponentBuilder = {
+ component: () => loadingComponent(),
+ withProperties: () => loadingBuilder,
+ withValidation: () => loadingBuilder,
+ withItem: () => loadingBuilder
+ };
+
+ let formBuilder: azdata.FormBuilder = Object.assign({}, {
+ component: () => form,
+ addFormItem: () => { },
+ insertFormItem: () => { },
+ removeFormItem: () => true,
+ addFormItems: () => { },
+ withFormItems: () => formBuilder,
+ withProperties: () => formBuilder,
+ withValidation: () => formBuilder,
+ withItems: () => formBuilder,
+ withLayout: () => formBuilder
+ });
+
+ let flexBuilder: azdata.FlexBuilder = Object.assign({}, {
+ component: () => flex,
+ withProperties: () => flexBuilder,
+ withValidation: () => flexBuilder,
+ withItems: () => flexBuilder,
+ withLayout: () => flexBuilder
+ });
+
+ let inputBoxBuilder: azdata.ComponentBuilder = {
+ component: () => {
+ let r = inputBox();
+ return r;
+ },
+ withProperties: () => inputBoxBuilder,
+ withValidation: () => inputBoxBuilder
+ };
+ let imageBuilder: azdata.ComponentBuilder = {
+ component: () => {
+ let r = image();
+ return r;
+ },
+ withProperties: () => imageBuilder,
+ withValidation: () => imageBuilder
+ };
+ let dropdownBuilder: azdata.ComponentBuilder = {
+ component: () => {
+ let r = dropdown();
+ return r;
+ },
+ withProperties: () => dropdownBuilder,
+ withValidation: () => dropdownBuilder
+ };
+
+ let view: azdata.ModelView = {
+ onClosed: undefined!,
+ connection: undefined!,
+ serverInfo: undefined!,
+ valid: true,
+ onValidityChanged: undefined!,
+ validate: undefined!,
+ initializeModel: () => { return Promise.resolve(); },
+ modelBuilder: {
+ radioCardGroup: undefined!,
+ navContainer: undefined!,
+ divContainer: undefined!,
+ flexContainer: () => flexBuilder,
+ splitViewContainer: undefined!,
+ dom: undefined!,
+ card: undefined!,
+ inputBox: () => inputBoxBuilder,
+ checkBox: undefined!,
+ radioButton: () => radioButtonBuilder,
+ webView: undefined!,
+ editor: undefined!,
+ diffeditor: undefined!,
+ text: () => inputBoxBuilder,
+ image: () => imageBuilder,
+ button: () => buttonBuilder,
+ dropDown: () => dropdownBuilder,
+ tree: undefined!,
+ listBox: undefined!,
+ table: undefined!,
+ declarativeTable: () => declarativeTableBuilder,
+ dashboardWidget: undefined!,
+ dashboardWebview: undefined!,
+ formContainer: () => formBuilder,
+ groupContainer: undefined!,
+ toolbarContainer: undefined!,
+ loadingComponent: () => loadingBuilder,
+ fileBrowserTree: undefined!,
+ hyperlink: undefined!,
+ tabbedPanel: undefined!,
+ separator: undefined!
+ }
+ };
+
+ return {
+ dialog: dialog,
+ model: model,
+ view: view,
+ onClick: onClick,
+ };
+ }
+});
diff --git a/extensions/notebook/src/test/managePackages/managePackagesDialogModel.test.ts b/extensions/notebook/src/test/managePackages/managePackagesDialogModel.test.ts
index 8fe0deb61e..1c2b226457 100644
--- a/extensions/notebook/src/test/managePackages/managePackagesDialogModel.test.ts
+++ b/extensions/notebook/src/test/managePackages/managePackagesDialogModel.test.ts
@@ -50,7 +50,6 @@ describe('Manage Packages', () => {
providers.set(provider.providerId, provider);
let options = {
- multiLocations: true,
defaultLocation: 'invalid location'
};
let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, options);
@@ -64,29 +63,12 @@ describe('Manage Packages', () => {
providers.set(provider.providerId, provider);
let options = {
- multiLocations: true,
defaultProviderId: 'invalid provider'
};
let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, options);
await should(model.init()).rejectedWith(`Invalid default provider id '${options.defaultProviderId}`);
});
- /* Test disabled. Tracking issue: https://github.com/microsoft/azuredatastudio/issues/8877
- it('Init should throw exception not given valid default location for single location mode', async function (): Promise {
- let testContext = createContext();
- let provider = createProvider(testContext);
- let providers = new Map();
- providers.set(provider.providerId, provider);
-
- let options = {
- multiLocations: false
- };
- let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, options);
- await should(model.init()).rejectedWith(`Default location not specified for single location mode`);
- });
- */
-
-
it('Init should set default options given undefined', async function (): Promise {
let testContext = createContext();
let provider = createProvider(testContext);
@@ -96,7 +78,6 @@ describe('Manage Packages', () => {
let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, undefined);
await model.init();
- should.equal(model.multiLocationMode, true);
should.equal(model.defaultLocation, provider.packageTarget.location);
should.equal(model.defaultProviderId, provider.providerId);
});
@@ -119,14 +100,12 @@ describe('Manage Packages', () => {
providers.set(testContext1.provider.providerId, createProvider(testContext1));
providers.set(testContext2.provider.providerId, createProvider(testContext2));
let options = {
- multiLocations: false,
defaultLocation: testContext2.provider.packageTarget.location,
defaultProviderId: testContext2.provider.providerId
};
let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, options);
await model.init();
- should.equal(model.multiLocationMode, false);
should.equal(model.defaultLocation, testContext2.provider.packageTarget.location);
should.equal(model.defaultProviderId, testContext2.provider.providerId);
});
@@ -195,7 +174,7 @@ describe('Manage Packages', () => {
it('changeProvider should change current provider successfully', async function (): Promise {
let testContext1 = createContext();
testContext1.provider.providerId = 'providerId1';
- testContext1.provider.getLocationTitle = () => Promise.resolve('location title 1');
+ testContext1.provider.getLocations = () => Promise.resolve([{displayName: 'location title 1', name: 'location1'}]);
testContext1.provider.packageTarget = {
location: 'location1',
packageType: 'package-type1'
@@ -203,7 +182,7 @@ describe('Manage Packages', () => {
let testContext2 = createContext();
testContext2.provider.providerId = 'providerId2';
- testContext2.provider.getLocationTitle = () => Promise.resolve('location title 2');
+ testContext2.provider.getLocations = () => Promise.resolve([{displayName: 'location title 2', name: 'location2'}]);
testContext2.provider.packageTarget = {
location: 'location2',
packageType: 'package-type2'
@@ -217,7 +196,7 @@ describe('Manage Packages', () => {
await model.init();
model.changeProvider('providerId2');
- should.deepEqual(await model.getLocationTitle(), 'location title 2');
+ should.deepEqual(await model.getLocations(), [{displayName: 'location title 2', name: 'location2'}]);
});
it('changeProvider should throw exception given invalid provider', async function (): Promise {
@@ -283,7 +262,7 @@ describe('Manage Packages', () => {
let testContext2 = createContext();
testContext2.provider.providerId = 'providerId2';
- testContext2.provider.getLocationTitle = () => Promise.resolve('location title 2');
+ testContext2.provider.getLocations = () => Promise.resolve([{displayName: 'location title 2', name: 'location2'}]);
testContext2.provider.packageTarget = {
location: 'location2',
packageType: 'package-type2'
@@ -301,6 +280,12 @@ describe('Manage Packages', () => {
testContext2.provider.listPackages = () => {
return Promise.resolve(packages);
};
+ testContext1.provider.listPackages = () => {
+ return Promise.resolve([{
+ name: 'p3',
+ version: '1.1.1.3'
+ }]);
+ };
let providers = new Map();
providers.set(testContext1.provider.providerId, createProvider(testContext1));
@@ -315,7 +300,50 @@ describe('Manage Packages', () => {
await should(model.installPackages(packages)).resolved();
await should(model.uninstallPackages(packages)).resolved();
await should(model.getPackageOverview('p1')).resolved();
- await should(model.getLocationTitle()).resolvedWith('location title 2');
+ await should(model.getLocations()).resolvedWith([{displayName: 'location title 2', name: 'location2'}]);
+ });
+
+ it('listPackages should return packages for current location', async function (): Promise {
+ let testContext = createContext();
+ testContext.provider.providerId = 'providerId1';
+ testContext.provider.packageTarget = {
+ location: 'location1',
+ packageType: 'package-type1'
+ };
+
+ let packages1 = [
+ {
+ name: 'p1',
+ version: '1.1.1.1'
+ },
+ {
+ name: 'p2',
+ version: '1.1.1.2'
+ }
+ ];
+ let packages2 = [{
+ name: 'p3',
+ version: '1.1.1.3'
+ }];
+ testContext.provider.listPackages = (location) => {
+ if (location === 'location1') {
+ return Promise.resolve(packages1);
+ } else {
+ return Promise.resolve(packages2);
+ }
+
+ };
+
+ let providers = new Map();
+ providers.set(testContext.provider.providerId, createProvider(testContext));
+
+ let model = new ManagePackagesDialogModel(jupyterServerInstallation, providers, undefined);
+
+ await model.init();
+ model.changeProvider('providerId1');
+ model.changeLocation('location2');
+
+ await should(model.listPackages()).resolvedWith(packages2);
});
function createContext(): TestContext {
@@ -327,7 +355,7 @@ describe('Manage Packages', () => {
packageType: 'package-type'
},
canUseProvider: () => { return Promise.resolve(true); },
- getLocationTitle: () => { return Promise.resolve('location-title'); },
+ getLocations: () => { return Promise.resolve([{displayName: 'location-title', name: 'location'}]); },
installPackages:() => { return Promise.resolve(); },
uninstallPackages: (packages: IPackageDetails[]) => { return Promise.resolve(); },
listPackages: () => { return Promise.resolve([]); },
@@ -339,10 +367,10 @@ describe('Manage Packages', () => {
function createProvider(testContext: TestContext): IPackageManageProvider {
let mockProvider = TypeMoq.Mock.ofType(LocalPipPackageManageProvider);
mockProvider.setup(x => x.canUseProvider()).returns(() => testContext.provider.canUseProvider());
- mockProvider.setup(x => x.getLocationTitle()).returns(() => testContext.provider.getLocationTitle());
- mockProvider.setup(x => x.installPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((packages, useMinVersion) => testContext.provider.installPackages(packages, useMinVersion));
- mockProvider.setup(x => x.uninstallPackages(TypeMoq.It.isAny())).returns((packages) => testContext.provider.uninstallPackages(packages));
- mockProvider.setup(x => x.listPackages()).returns(() => testContext.provider.listPackages());
+ mockProvider.setup(x => x.getLocations()).returns(() => testContext.provider.getLocations());
+ mockProvider.setup(x => x.installPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((packages, useMinVersion) => testContext.provider.installPackages(packages, useMinVersion));
+ mockProvider.setup(x => x.uninstallPackages(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((packages) => testContext.provider.uninstallPackages(packages));
+ mockProvider.setup(x => x.listPackages(TypeMoq.It.isAny())).returns(() => testContext.provider.listPackages());
mockProvider.setup(x => x.getPackageOverview(TypeMoq.It.isAny())).returns((name) => testContext.provider.getPackageOverview(name));
mockProvider.setup(x => x.packageTarget).returns(() => testContext.provider.packageTarget);
mockProvider.setup(x => x.providerId).returns(() => testContext.provider.providerId);
diff --git a/extensions/notebook/src/types.d.ts b/extensions/notebook/src/types.d.ts
index 368d221a1b..984a47d7a5 100644
--- a/extensions/notebook/src/types.d.ts
+++ b/extensions/notebook/src/types.d.ts
@@ -65,6 +65,14 @@ export interface IPackageDetails {
version: string;
}
+/**
+ * Package location
+ */
+export interface IPackageLocation {
+ name: string;
+ displayName: string;
+}
+
/**
* Package target interface
*/
@@ -99,20 +107,22 @@ export interface IPackageManageProvider {
/**
* Returns list of installed packages
*/
- listPackages(): Promise;
+ listPackages(location?: string): Promise;
/**
* Installs give packages
* @param package Packages to install
* @param useMinVersion if true, minimal version will be used
+ * @param location package location
*/
- installPackages(package: IPackageDetails[], useMinVersion: boolean): Promise;
+ installPackages(package: IPackageDetails[], useMinVersion: boolean, location?: string): Promise;
/**
* Uninstalls given packages
* @param package package to uninstall
+ * @param location package location
*/
- uninstallPackages(package: IPackageDetails[]): Promise;
+ uninstallPackages(package: IPackageDetails[], location?: string): Promise;
/**
* Returns true if the provider can be used in current context
@@ -122,7 +132,7 @@ export interface IPackageManageProvider {
/**
* Returns location title
*/
- getLocationTitle(): Promise;
+ getLocations(): Promise;
/**
* Returns Package Overview
diff --git a/extensions/schema-compare/src/dialogs/schemaCompareDialog.ts b/extensions/schema-compare/src/dialogs/schemaCompareDialog.ts
index da4034a0c2..dd04183f04 100644
--- a/extensions/schema-compare/src/dialogs/schemaCompareDialog.ts
+++ b/extensions/schema-compare/src/dialogs/schemaCompareDialog.ts
@@ -103,7 +103,7 @@ export class SchemaCompareDialog {
endpointType: mssql.SchemaCompareEndpointType.Database,
serverDisplayName: (this.sourceServerDropdown.value as ConnectionDropdownValue).displayName,
serverName: (this.sourceServerDropdown.value as ConnectionDropdownValue).name,
- databaseName: (this.sourceDatabaseDropdown.value).name,
+ databaseName: this.sourceDatabaseDropdown.value.toString(),
ownerUri: ownerUri,
packageFilePath: '',
connectionDetails: undefined
@@ -127,7 +127,7 @@ export class SchemaCompareDialog {
endpointType: mssql.SchemaCompareEndpointType.Database,
serverDisplayName: (this.targetServerDropdown.value as ConnectionDropdownValue).displayName,
serverName: (this.targetServerDropdown.value as ConnectionDropdownValue).name,
- databaseName: (this.targetDatabaseDropdown.value).name,
+ databaseName: this.targetDatabaseDropdown.value.toString(),
ownerUri: ownerUri,
packageFilePath: '',
connectionDetails: undefined
@@ -204,7 +204,7 @@ export class SchemaCompareDialog {
this.sourceDatabaseComponent = await this.createSourceDatabaseDropdown(view);
if ((this.sourceServerDropdown.value as ConnectionDropdownValue)) {
- await this.populateDatabaseDropdown((this.sourceServerDropdown.value as ConnectionDropdownValue).connection.connectionId, false);
+ await this.populateDatabaseDropdown((this.sourceServerDropdown.value as ConnectionDropdownValue).connection, false);
}
this.targetServerComponent = await this.createTargetServerDropdown(view);
@@ -212,7 +212,7 @@ export class SchemaCompareDialog {
this.targetDatabaseComponent = await this.createTargetDatabaseDropdown(view);
if ((this.targetServerDropdown.value as ConnectionDropdownValue)) {
- await this.populateDatabaseDropdown((this.targetServerDropdown.value as ConnectionDropdownValue).connection.connectionId, true);
+ await this.populateDatabaseDropdown((this.targetServerDropdown.value as ConnectionDropdownValue).connection, true);
}
this.sourceDacpacComponent = await this.createFileBrowser(view, false, this.schemaCompareResult.sourceEndpointInfo);
@@ -477,7 +477,7 @@ export class SchemaCompareDialog {
});
}
else {
- await this.populateDatabaseDropdown((this.sourceServerDropdown.value as ConnectionDropdownValue).connection.connectionId, false);
+ await this.populateDatabaseDropdown((this.sourceServerDropdown.value as ConnectionDropdownValue).connection, false);
}
});
@@ -503,7 +503,7 @@ export class SchemaCompareDialog {
});
}
else {
- await this.populateDatabaseDropdown((this.targetServerDropdown.value as ConnectionDropdownValue).connection.connectionId, true);
+ await this.populateDatabaseDropdown((this.targetServerDropdown.value as ConnectionDropdownValue).connection, true);
}
});
@@ -627,11 +627,18 @@ export class SchemaCompareDialog {
return listValue.displayName === value || listValue === value;
}
- protected async populateDatabaseDropdown(connectionId: string, isTarget: boolean): Promise {
+ protected async populateDatabaseDropdown(connectionProfile: azdata.connection.ConnectionProfile, isTarget: boolean): Promise {
let currentDropdown = isTarget ? this.targetDatabaseDropdown : this.sourceDatabaseDropdown;
currentDropdown.updateProperties({ values: [], value: null });
- let values = await this.getDatabaseValues(connectionId, isTarget);
+ let values = [];
+ try {
+ values = await this.getDatabaseValues(connectionProfile.connectionId, isTarget);
+ } catch (e) {
+ // if the user doesn't have access to master, just set the database of the connection profile
+ values = [connectionProfile.databaseName];
+ console.warn(e);
+ }
if (values && values.length > 0) {
currentDropdown.updateProperties({
values: values,
@@ -640,7 +647,7 @@ export class SchemaCompareDialog {
}
}
- protected async getDatabaseValues(connectionId: string, isTarget: boolean): Promise<{ displayName, name }[]> {
+ protected async getDatabaseValues(connectionId: string, isTarget: boolean): Promise {
let endpointInfo = isTarget ? this.schemaCompareResult.targetEndpointInfo : this.schemaCompareResult.sourceEndpointInfo;
let idx = -1;
@@ -654,10 +661,7 @@ export class SchemaCompareDialog {
idx = count;
}
- return {
- displayName: db,
- name: db
- };
+ return db;
});
if (idx >= 0) {
diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js
index 8f55c823ed..f1546f9e15 100644
--- a/extensions/shared.webpack.config.js
+++ b/extensions/shared.webpack.config.js
@@ -69,7 +69,7 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) {
// yes, really source maps
devtool: 'source-map',
plugins: [
- // @ts-ignore
+ // @ts-expect-error
new CopyWebpackPlugin([
{ from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] }
]),
diff --git a/extensions/vscode-account/package.json b/extensions/vscode-account/package.json
index cd579aef85..3a5407738c 100644
--- a/extensions/vscode-account/package.json
+++ b/extensions/vscode-account/package.json
@@ -27,7 +27,20 @@
"title": "%signOut%",
"category": "%displayName%"
}
- ]
+ ],
+ "configuration": {
+ "title": "Microsoft Account",
+ "properties": {
+ "microsoftAccount.logLevel": {
+ "type": "string",
+ "enum": [
+ "info",
+ "trace"
+ ],
+ "default": "info"
+ }
+ }
+ }
},
"scripts": {
"vscode:prepublish": "npm run compile",
diff --git a/extensions/vscode-account/src/keychain.ts b/extensions/vscode-account/src/keychain.ts
index b5314e5de3..73ab288f5f 100644
--- a/extensions/vscode-account/src/keychain.ts
+++ b/extensions/vscode-account/src/keychain.ts
@@ -45,6 +45,7 @@ export class Keychain {
async setToken(token: string): Promise {
try {
+ Logger.trace('Writing to keychain', token);
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
} catch (e) {
// Ignore
@@ -59,7 +60,9 @@ export class Keychain {
async getToken(): Promise {
try {
- return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
+ const result = await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
+ Logger.trace('Reading from keychain', result);
+ return result;
} catch (e) {
// Ignore
Logger.error(`Getting token failed: ${e}`);
diff --git a/extensions/vscode-account/src/logger.ts b/extensions/vscode-account/src/logger.ts
index ec0699eab3..c5dd1235bd 100644
--- a/extensions/vscode-account/src/logger.ts
+++ b/extensions/vscode-account/src/logger.ts
@@ -7,11 +7,23 @@ import * as vscode from 'vscode';
type LogLevel = 'Trace' | 'Info' | 'Error';
+enum Level {
+ Trace = 'trace',
+ Info = 'Info'
+}
+
class Log {
private output: vscode.OutputChannel;
+ private level: Level;
constructor() {
this.output = vscode.window.createOutputChannel('Account');
+ this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
+ vscode.workspace.onDidChangeConfiguration(e => {
+ if (e.affectsConfiguration('microsoftAccount.logLevel')) {
+ this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
+ }
+ });
}
private data2String(data: any): string {
@@ -32,6 +44,12 @@ class Log {
this.logLevel('Error', message, data);
}
+ public trace(message: string, data?: any): void {
+ if (this.level === Level.Trace) {
+ this.logLevel('Trace', message, data);
+ }
+ }
+
public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json
index 0f3815b2b6..c976dbc8d0 100644
--- a/extensions/vscode-colorize-tests/package.json
+++ b/extensions/vscode-colorize-tests/package.json
@@ -1,68 +1,59 @@
{
- "name": "vscode-colorize-tests",
- "description": "Colorize tests for VS Code",
- "version": "0.0.1",
- "publisher": "vscode",
- "license": "MIT",
- "private": true,
- "activationEvents": [
- "onLanguage:json"
- ],
- "main": "./out/colorizerTestMain",
- "enableProposedApi": true,
- "engines": {
- "vscode": "*"
- },
- "scripts": {
- "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json"
- },
- "dependencies": {
- "jsonc-parser": "2.2.1"
- },
- "devDependencies": {
- "@types/node": "^12.11.7",
- "mocha-junit-reporter": "^1.17.0",
- "mocha-multi-reporters": "^1.1.7",
- "vscode": "1.1.5"
- },
- "contributes": {
- "semanticTokenTypes": [
- {
- "id": "testToken",
- "description": "A test token"
- }
- ],
- "semanticTokenModifiers": [
- {
- "id": "testModifier",
- "description": "A test modifier"
- }
- ],
- "semanticTokenStyleDefaults": [
- {
- "selector": "testToken",
- "scope": [ "entity.name.function.special" ]
- },
- {
- "selector": "*.testModifier",
- "light": {
- "fontStyle": "bold"
- },
- "dark": {
- "fontStyle": "bold"
- },
- "highContrast": {
- "fontStyle": "bold"
- }
- }
- ],
- "productIconThemes": [
- {
- "id": "Test Product Icons",
- "label": "The Test Product Icon Theme",
- "path": "./producticons/test-product-icon-theme.json",
- "_watch": true
- }
- ]
- }
+ "name": "vscode-colorize-tests",
+ "description": "Colorize tests for VS Code",
+ "version": "0.0.1",
+ "publisher": "vscode",
+ "license": "MIT",
+ "private": true,
+ "activationEvents": [
+ "onLanguage:json"
+ ],
+ "main": "./out/colorizerTestMain",
+ "enableProposedApi": true,
+ "engines": {
+ "vscode": "*"
+ },
+ "scripts": {
+ "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-colorize-tests ./tsconfig.json"
+ },
+ "dependencies": {
+ "jsonc-parser": "2.2.1"
+ },
+ "devDependencies": {
+ "@types/node": "^12.11.7",
+ "mocha-junit-reporter": "^1.17.0",
+ "mocha-multi-reporters": "^1.1.7",
+ "vscode": "1.1.5"
+ },
+ "contributes": {
+ "semanticTokenTypes": [
+ {
+ "id": "testToken",
+ "description": "A test token"
+ }
+ ],
+ "semanticTokenModifiers": [
+ {
+ "id": "testModifier",
+ "description": "A test modifier"
+ }
+ ],
+ "semanticTokenScopes": [
+ {
+ "scopes": {
+ "testToken": [
+ "entity.name.function.special"
+ ]
+ }
+ }
+ ],
+ "productIconThemes": [
+ {
+ "id": "Test Product Icons",
+ "label": "The Test Product Icon Theme",
+ "path": "./producticons/test-product-icon-theme.json",
+ "_watch": true
+ }
+ ]
+ }
}
diff --git a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts
index 079c77e8e8..c12e4af29e 100644
--- a/extensions/vscode-colorize-tests/src/colorizerTestMain.ts
+++ b/extensions/vscode-colorize-tests/src/colorizerTestMain.ts
@@ -56,7 +56,7 @@ export function activate(context: vscode.ExtensionContext): any {
};
jsoncParser.visit(document.getText(), visitor);
- return new vscode.SemanticTokens(builder.build());
+ return builder.build();
}
};
diff --git a/package.json b/package.json
index 79df1d81f0..1008d08c25 100644
--- a/package.json
+++ b/package.json
@@ -57,6 +57,7 @@
"jquery": "3.4.0",
"jschardet": "2.1.1",
"keytar": "^4.11.0",
+ "minimist": "^1.2.5",
"native-is-elevated": "0.4.1",
"native-keymap": "2.1.1",
"native-watchdog": "1.3.0",
@@ -72,7 +73,6 @@
"spdlog": "^0.11.1",
"sudo-prompt": "9.1.1",
"v8-inspect-profiler": "^0.0.20",
- "vscode-minimist": "^1.2.2",
"vscode-nsfw": "1.2.8",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.8",
@@ -98,6 +98,7 @@
"@types/http-proxy-agent": "^2.0.1",
"@types/iconv-lite": "0.0.1",
"@types/keytar": "^4.4.0",
+ "@types/minimist": "^1.2.0",
"@types/mocha": "2.2.39",
"@types/node": "^12.11.7",
"@types/plotly.js": "^1.44.9",
diff --git a/remote/package.json b/remote/package.json
index f1976985a7..25220035ee 100644
--- a/remote/package.json
+++ b/remote/package.json
@@ -23,6 +23,7 @@
"iconv-lite": "0.5.0",
"jquery": "3.4.0",
"jschardet": "2.1.1",
+ "minimist": "^1.2.5",
"native-watchdog": "1.3.0",
"ng2-charts": "^1.6.0",
"node-pty": "^0.10.0-beta2",
@@ -33,7 +34,6 @@
"semver-umd": "^5.5.5",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.32",
"spdlog": "^0.11.1",
- "vscode-minimist": "^1.2.2",
"vscode-nsfw": "1.2.8",
"vscode-proxy-agent": "^0.5.2",
"vscode-ripgrep": "^1.5.8",
diff --git a/remote/yarn.lock b/remote/yarn.lock
index faf4f46b9f..761cdd74b3 100644
--- a/remote/yarn.lock
+++ b/remote/yarn.lock
@@ -454,6 +454,11 @@ minimist@0.0.8:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
+minimist@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
+ integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
+
mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -695,11 +700,6 @@ util-deprecate@^1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-vscode-minimist@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.2.tgz#65403f44f0c6010d259b2271d36eb5c6f4ad8aab"
- integrity sha512-DXMNG2QgrXn1jOP12LzjVfvxVkzxv/0Qa27JrMBj/XP2esj+fJ/wP2T4YUH5derj73Lc96dC8F25WyfDUbTpxQ==
-
vscode-nsfw@1.2.8:
version "1.2.8"
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.2.8.tgz#1bf452e72ff1304934de63692870d039a2d972af"
diff --git a/scripts/code-web.js b/scripts/code-web.js
index 23bd232903..b7441ea015 100755
--- a/scripts/code-web.js
+++ b/scripts/code-web.js
@@ -13,7 +13,7 @@ const fs = require('fs');
const path = require('path');
const util = require('util');
const opn = require('opn');
-const minimist = require('vscode-minimist');
+const minimist = require('minimist');
const APP_ROOT = path.dirname(__dirname);
const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
diff --git a/scripts/code.sh b/scripts/code.sh
index 0afe96bfcb..3bc09f54f4 100755
--- a/scripts/code.sh
+++ b/scripts/code.sh
@@ -55,6 +55,9 @@ function code() {
function code-wsl()
{
+ HOST_IP=$(powershell.exe -Command "& {(Get-NetIPAddress | Where-Object {\$_.InterfaceAlias -like '*WSL*' -and \$_.AddressFamily -eq 'IPv4'}).IPAddress | Write-Host -NoNewline}")
+ export DISPLAY="$HOST_IP:0"
+
# in a wsl shell
ELECTRON="$ROOT/.build/electron/Code - OSS.exe"
if [ -f "$ELECTRON" ]; then
diff --git a/scripts/sql-test-integration.bat b/scripts/sql-test-integration.bat
index b96bce3804..0bc551b0a8 100644
--- a/scripts/sql-test-integration.bat
+++ b/scripts/sql-test-integration.bat
@@ -47,7 +47,7 @@ if "%SKIP_PYTHON_INSTALL_TEST%" == "1" (
)
call %INTEGRATION_TEST_ELECTRON_PATH% --user-data-dir=%VSCODEUSERDATADIR% --extensions-dir=%VSCODEEXTENSIONSDIR% --remote-debugging-port=9222 ^
---extensionDevelopmentPath=%~dp0\..\extensions\integration-tests --extensionTestsPath=%~dp0\..\extensions\integration-tests\out --disable-telemetry --disable-crash-reporter --disable-updates -nogpu
+--extensionDevelopmentPath=%~dp0\..\extensions\integration-tests --extensionTestsPath=%~dp0\..\extensions\integration-tests\out\tests --disable-telemetry --disable-crash-reporter --disable-updates -nogpu
rmdir /s /q %VSCODEUSERDATADIR%
rmdir /s /q %VSCODEEXTENSIONSDIR%
diff --git a/scripts/sql-test-integration.sh b/scripts/sql-test-integration.sh
index 43e1a4a18e..fafaebe858 100755
--- a/scripts/sql-test-integration.sh
+++ b/scripts/sql-test-integration.sh
@@ -60,7 +60,7 @@ fi
--extensionDevelopmentPath=$ROOT/extensions/profiler \
--extensionDevelopmentPath=$ROOT/extensions/resource-deployment \
--extensionDevelopmentPath=$ROOT/extensions/schema-compare \
---extensionTestsPath=$ROOT/extensions/integration-tests/out \
+--extensionTestsPath=$ROOT/extensions/integration-tests/out/tests \
--user-data-dir=$VSCODEUSERDATADIR --extensions-dir=$VSCODEEXTDIR \
--disable-telemetry --disable-crash-reporter --disable-updates --skip-getting-started --disable-inspect
diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js
index 422168128a..c92f1c79ce 100644
--- a/src/bootstrap-fork.js
+++ b/src/bootstrap-fork.js
@@ -142,13 +142,11 @@ function pipeLoggingToParent() {
function handleExceptions() {
// Handle uncaught exceptions
- // @ts-ignore
process.on('uncaughtException', function (err) {
console.error('Uncaught Exception: ', err);
});
// Handle unhandled promise rejections
- // @ts-ignore
process.on('unhandledRejection', function (reason) {
console.error('Unhandled Promise Rejection: ', reason);
});
diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js
index bcd6c4a5c9..4c0f153f69 100644
--- a/src/bootstrap-window.js
+++ b/src/bootstrap-window.js
@@ -25,7 +25,6 @@ exports.assign = function assign(destination, source) {
*/
exports.load = function (modulePaths, resultCallback, options) {
- // @ts-ignore
const webFrame = require('electron').webFrame;
const path = require('path');
@@ -49,7 +48,6 @@ exports.load = function (modulePaths, resultCallback, options) {
}
// Error handler
- // @ts-ignore
process.on('uncaughtException', function (error) {
onUnexpectedError(error, enableDeveloperTools);
});
@@ -184,7 +182,6 @@ function parseURLQueryArgs() {
*/
function registerDeveloperKeybindings(disallowReloadKeybinding) {
- // @ts-ignore
const ipc = require('electron').ipcRenderer;
const extractKey = function (e) {
@@ -223,7 +220,6 @@ function registerDeveloperKeybindings(disallowReloadKeybinding) {
function onUnexpectedError(error, enableDeveloperTools) {
- // @ts-ignore
const ipc = require('electron').ipcRenderer;
if (enableDeveloperTools) {
diff --git a/src/main.js b/src/main.js
index e572053219..287e693b36 100644
--- a/src/main.js
+++ b/src/main.js
@@ -323,7 +323,7 @@ function getUserDataPath(cliArgs) {
* @returns {ParsedArgs}
*/
function parseCLIArgs() {
- const minimist = require('vscode-minimist');
+ const minimist = require('minimist');
return minimist(process.argv, {
string: [
diff --git a/src/paths.js b/src/paths.js
index b49f0db52e..7453a528c6 100644
--- a/src/paths.js
+++ b/src/paths.js
@@ -6,7 +6,7 @@
//@ts-check
'use strict';
-// @ts-ignore
+// @ts-expect-error
// const pkg = require('../package.json');
const path = require('path');
const os = require('os');
@@ -34,4 +34,4 @@ function getDefaultUserDataPath(platform) {
}
exports.getAppDataPath = getAppDataPath;
-exports.getDefaultUserDataPath = getDefaultUserDataPath;
\ No newline at end of file
+exports.getDefaultUserDataPath = getDefaultUserDataPath;
diff --git a/src/sql/azdata.d.ts b/src/sql/azdata.d.ts
index 2e35d1dc27..e7841d69ff 100644
--- a/src/sql/azdata.d.ts
+++ b/src/sql/azdata.d.ts
@@ -2212,7 +2212,8 @@ declare module 'azdata' {
Sql = 1,
OssRdbms = 2,
AzureKeyVault = 3,
- Graph = 4
+ Graph = 4,
+ MicrosoftResourceManagement = 5
}
export interface DidChangeAccountsParams {
diff --git a/src/sql/azdata.proposed.d.ts b/src/sql/azdata.proposed.d.ts
index aeafcb309d..eb9ab370d2 100644
--- a/src/sql/azdata.proposed.d.ts
+++ b/src/sql/azdata.proposed.d.ts
@@ -225,6 +225,7 @@ declare module 'azdata' {
*/
export interface TabbedPanelLayout {
orientation: TabOrientation;
+ showIcon: boolean;
}
/**
@@ -245,6 +246,11 @@ declare module 'azdata' {
* Id of the tab
*/
id: string;
+
+ /**
+ * Icon of the tab
+ */
+ icon?: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
}
/**
@@ -287,5 +293,33 @@ declare module 'azdata' {
*/
export const onDidChangeActiveNotebookEditor: vscode.Event;
}
+
+ export namespace window {
+ export interface ModelViewDashboard {
+ registerTabs(handler: (view: ModelView) => Thenable<(DashboardTab | DashboardTabGroup)[]>): void;
+ open(): Thenable;
+ }
+
+ export function createModelViewDashboard(title: string): ModelViewDashboard;
+ }
+
+ export interface DashboardTab extends Tab {
+ /**
+ * Toolbar of the tab, optional.
+ */
+ toolbar?: ToolbarContainer;
+ }
+
+ export interface DashboardTabGroup {
+ /**
+ * * Title of the tab group
+ */
+ title: string;
+
+ /**
+ * children of the tab group
+ */
+ tabs: DashboardTab[];
+ }
}
diff --git a/src/sql/base/browser/ui/panel/media/panel.css b/src/sql/base/browser/ui/panel/media/panel.css
index b68539e8b1..fe2215f198 100644
--- a/src/sql/base/browser/ui/panel/media/panel.css
+++ b/src/sql/base/browser/ui/panel/media/panel.css
@@ -24,9 +24,16 @@ panel {
position: relative;
}
+.tabbedPanel.vertical>.title {
+ flex: 0 0 auto;
+ flex-direction: column;
+ height: 100%;
+}
+
.tabbedPanel .tabContainer {
flex: 1 1 auto;
overflow: hidden;
+ height: 100%;
}
.tabbedPanel .tabList {
@@ -45,31 +52,34 @@ panel {
margin: auto;
}
+.tabbedPanel.horizontal .tabList .tab .tabLabel {
+ font-size: 12px;
+ font-weight: normal;
+}
+
+.tabbedPanel.vertical .tabList .tab .tabLabel {
+ font-size: 12px;
+ padding-bottom: 0px;
+ font-weight: normal;
+}
+
.tabbedPanel .tabList .tab .tabLabel {
font-size: 13px;
padding-bottom: 4px;
font-weight: 600;
}
-.tabbedPanel.vertical .tabList .tab .tabLabel {
- font-size: 11px;
-}
-
.tabbedPanel .tabList .tab-header {
display: flex;
padding-left: 5px;
padding-right: 5px;
- cursor: pointer;
+ min-width: 65px;
}
-.tabbedPanel.vertical .tabList .tab-header {
+.tabbedPanel.vertical > .title .tabList .tab-header {
display: block;
- text-transform: none;
- text-overflow: ellipsis;
- overflow: hidden;
- width: auto;
- height: 50px;
- line-height: 45px;
+ min-width: 150px;
+ line-height: 35px;
}
.tabbedPanel .tabList .tab .tabIcon.codicon {
@@ -85,8 +95,6 @@ panel {
.tabbedPanel .composite.title .title-actions .action-label {
display: block;
- height: 35px;
- line-height: 35px;
min-width: 28px;
background-size: 16px;
background-position: center center;
@@ -110,22 +118,22 @@ panel {
flex-direction: row;
}
-.tabbedPanel.vertical > .title {
+.tabbedPanel.vertical>.title {
flex: 0 0 auto;
flex-direction: column;
height: 100%;
}
-.tabbedPanel > .tab-content {
- flex: 1;
+.tabbedPanel>.tab-content {
+ flex: 1 1 auto;
position: relative;
}
-.tabbedPanel.vertical > .title > .tabContainer > .monaco-scrollable-element > .tabList {
+.tabbedPanel.vertical>.title > .tabContainer .tabList {
flex-flow: column;
}
-.tabbedPanel.horizontal > .title > .tabContainer > .monaco-scrollable-element > .tabList {
+.tabbedPanel.horizontal > .title > .tabContainer .tabList {
flex-flow: row;
}
@@ -153,9 +161,11 @@ panel {
border-color: rgb(214, 214, 214);
}
-.tabbedPanel .action-container {
+.tabbedPanel .vertical-tab-action-container {
display: flex;
flex-flow: row-reverse;
+ height: 35px;
+ padding: 0px 5px;
}
.tabbedPanel .tab-action {
@@ -164,12 +174,13 @@ panel {
padding: 0px;
border: 0px;
background-color: transparent;
- background-position: 2px center;
+ background-position: center;
background-repeat: no-repeat;
- background-size: 11px 11px;
+ background-size: 9px 9px;
+ align-self: center;
}
-.vs .tabbedPanel .tab-action.collapse{
+.vs .tabbedPanel .tab-action.collapse {
background-image: url("collapse.svg");
}
diff --git a/src/sql/base/browser/ui/panel/panel.component.ts b/src/sql/base/browser/ui/panel/panel.component.ts
index fd658c22e7..1dd8dd6543 100644
--- a/src/sql/base/browser/ui/panel/panel.component.ts
+++ b/src/sql/base/browser/ui/panel/panel.component.ts
@@ -52,16 +52,16 @@ let idPool = 0;