mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Machine Learning Services Dashboard (#8796)
* Added a dashboard for Machine Learning Services Extension
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
"azdata": ">=1.13.0"
|
||||
},
|
||||
"activationEvents": [
|
||||
"onCommand:ml.command.managePackages"
|
||||
"*"
|
||||
],
|
||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||
"icon": "images/ML_ExtensionIcon.png",
|
||||
@@ -27,28 +27,66 @@
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "ml.command.managePackages",
|
||||
"title": "%ml.command.managePackages%"
|
||||
"command": "mls.command.managePackages",
|
||||
"title": "%mls.command.managePackages%"
|
||||
},
|
||||
{
|
||||
"command": "mls.command.odbcdriver",
|
||||
"title": "%mls.command.odbcdriver%"
|
||||
},
|
||||
{
|
||||
"command": "mls.command.mlsdocs",
|
||||
"title": "%mls.command.mlsdocs%"
|
||||
}
|
||||
],
|
||||
"dashboard.tabs": [
|
||||
{
|
||||
"id": "mls-dashboard",
|
||||
"description": "%description%",
|
||||
"provider": "MSSQL",
|
||||
"title": "%displayName%",
|
||||
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud",
|
||||
"container": {
|
||||
"grid-container": [
|
||||
{
|
||||
"name": "%title.configurations%",
|
||||
"row": 0,
|
||||
"col": 0,
|
||||
"widget": {
|
||||
"modelview": {
|
||||
"id": "ml.tasks"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "%title.tasks%",
|
||||
"row": 0,
|
||||
"col": 1,
|
||||
"widget": {
|
||||
"tasks-widget": [
|
||||
"mls.command.managePackages",
|
||||
"mls.command.odbcdriver",
|
||||
"mls.command.mlsdocs"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-nls": "^4.0.0"
|
||||
"vscode-nls": "^4.0.0",
|
||||
"vscode-languageclient": "^5.3.0-next.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.5",
|
||||
"@types/node": "^10.14.8",
|
||||
"@types/uuid": "^3.4.5",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-junit-reporter": "^1.17.0",
|
||||
"mocha-multi-reporters": "^1.1.7",
|
||||
"should": "^13.2.1",
|
||||
"typemoq": "^2.1.0",
|
||||
"vscode": "1.1.26"
|
||||
},
|
||||
"__metadata": {
|
||||
"id": "56",
|
||||
"publisherDisplayName": "Microsoft",
|
||||
"publisherId": "Microsoft"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"displayName": "SQL Server Machine Learning Services",
|
||||
"description": "SQL Server Machine Learning Services",
|
||||
"mlServices.enable": "Enable Machine Learning Services",
|
||||
"mlServices.disable": "Disable Machine Learning Services",
|
||||
"title.tasks": "Getting Started",
|
||||
"title.tasks": "Tasks",
|
||||
"title.configurations": "Configurations",
|
||||
"title.endpoints": "Endpoints",
|
||||
"title.books": "Machine Learning Services Books",
|
||||
"ml.command.managePackages": "Manage Packages in SQL Server"
|
||||
"mls.command.managePackages": "Manage Packages in SQL Server",
|
||||
"mls.command.odbcdriver": "Install ODBC Driver for SQL Server",
|
||||
"mls.command.mlsdocs": "Machine Learning Services Documentation"
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ export class ApiWrapper {
|
||||
public executeCommand<T>(command: string, ...rest: any[]): Thenable<T | undefined> {
|
||||
return vscode.commands.executeCommand(command, ...rest);
|
||||
}
|
||||
public registerTaskHandler(taskId: string, handler: (profile: azdata.IConnectionProfile) => void): void {
|
||||
azdata.tasks.registerTask(taskId, handler);
|
||||
}
|
||||
|
||||
public getUriForConnection(connectionId: string): Thenable<string> {
|
||||
return azdata.connection.getUriForConnection(connectionId);
|
||||
@@ -59,6 +62,10 @@ export class ApiWrapper {
|
||||
azdata.tasks.startBackgroundOperation(operationInfo);
|
||||
}
|
||||
|
||||
public openExternal(target: vscode.Uri): Thenable<boolean> {
|
||||
return vscode.env.openExternal(target);
|
||||
}
|
||||
|
||||
public getExtension(extensionId: string): vscode.Extension<any> | undefined {
|
||||
return vscode.extensions.getExtension(extensionId);
|
||||
}
|
||||
|
||||
@@ -12,17 +12,65 @@ const localize = nls.loadMessageBundle();
|
||||
export const winPlatform = 'win32';
|
||||
export const pythonBundleVersion = '0.0.1';
|
||||
export const managePackagesCommand = 'jupyter.cmd.managePackages';
|
||||
export const mlManagePackagesCommand = 'ml.command.managePackages';
|
||||
export const pythonLanguageName = 'Python';
|
||||
export const rLanguageName = 'R';
|
||||
|
||||
export const mlEnableMlsCommand = 'mls.command.enableMls';
|
||||
export const mlDisableMlsCommand = 'mls.command.disableMls';
|
||||
export const extensionOutputChannel = 'Machine Learning Services';
|
||||
export const notebookExtensionName = 'Microsoft.notebook';
|
||||
|
||||
// Tasks, commands
|
||||
//
|
||||
export const mlManagePackagesCommand = 'mls.command.managePackages';
|
||||
export const mlOdbcDriverCommand = 'mls.command.odbcdriver';
|
||||
export const mlsDocumentsCommand = 'mls.command.mlsdocs';
|
||||
|
||||
// Localized texts
|
||||
//
|
||||
export const managePackageCommandError = localize('ml.managePackages.error', "Either no connection is available or the server does not have external script enabled.");
|
||||
export function installDependenciesError(err: string): string { return localize('ml.installDependencies.error', "Failed to install dependencies. Error: {0}", err); }
|
||||
export const installDependenciesMsgTaskName = localize('ml.installDependencies.msgTaskName', "Installing Machine Learning extension dependencies");
|
||||
export const installDependenciesPackages = localize('ml.installDependencies.packages', "Installing required packages ...");
|
||||
export const installDependenciesPackagesAlreadyInstalled = localize('ml.installDependencies.packagesAlreadyInstalled', "Required packages are already installed.");
|
||||
export function installDependenciesGetPackagesError(err: string): string { return localize('ml.installDependencies.getPackagesError', "Failed to get installed python packages. Error: {0}", err); }
|
||||
export const packageManagerNoConnection = localize('ml.packageManager.NoConnection', "No connection selected");
|
||||
export const notebookExtensionNotLoaded = localize('ml.notebookExtensionNotLoaded', "Notebook extension is not loaded");
|
||||
export const managePackageCommandError = localize('mls.managePackages.error', "Either no connection is available or the server does not have external script enabled.");
|
||||
export function installDependenciesError(err: string): string { return localize('mls.installDependencies.error', "Failed to install dependencies. Error: {0}", err); }
|
||||
export const installDependenciesMsgTaskName = localize('mls.installDependencies.msgTaskName', "Installing Machine Learning extension dependencies");
|
||||
export const installDependenciesPackages = localize('mls.installDependencies.packages', "Installing required packages ...");
|
||||
export const installDependenciesPackagesAlreadyInstalled = localize('mls.installDependencies.packagesAlreadyInstalled', "Required packages are already installed.");
|
||||
export function installDependenciesGetPackagesError(err: string): string { return localize('mls.installDependencies.getPackagesError', "Failed to get installed python packages. Error: {0}", err); }
|
||||
export const packageManagerNoConnection = localize('mls.packageManager.NoConnection', "No connection selected");
|
||||
export const notebookExtensionNotLoaded = localize('mls.notebookExtensionNotLoaded', "Notebook extension is not loaded");
|
||||
export const mlsEnabledMessage = localize('mls.enabledMessage', "Machine Learning Services Enabled");
|
||||
export const mlsDisabledMessage = localize('mls.disabledMessage', "Machine Learning Services Disabled");
|
||||
export const mlsConfigUpdateFailed = localize('mls.configUpdateFailed', "Failed to modify Machine Learning Services configurations");
|
||||
export const mlsEnableButtonTitle = localize('mls.enableButtonTitle', "Enable");
|
||||
export const mlsDisableButtonTitle = localize('mls.disableButtonTitle', "Disable");
|
||||
export const mlsConfigTitle = localize('mls.configTitle', "Config");
|
||||
export const mlsConfigStatus = localize('mls.configStatus', "Enabled");
|
||||
export const mlsConfigAction = localize('mls.configAction', "Action");
|
||||
export const mlsExternalExecuteScriptTitle = localize('mls.externalExecuteScriptTitle', "External Execute Script");
|
||||
export const mlsPythonLanguageTitle = localize('mls.pythonLanguageTitle', "Python");
|
||||
export const mlsRLanguageTitle = localize('mls.rLanguageTitle', "R");
|
||||
|
||||
// Links
|
||||
//
|
||||
export const mlsDocuments = 'https://docs.microsoft.com/sql/advanced-analytics/?view=sql-server-ver15';
|
||||
export const odbcDriverWindowsDocuments = 'https://docs.microsoft.com/sql/connect/odbc/windows/microsoft-odbc-driver-for-sql-server-on-windows?view=sql-server-ver15';
|
||||
export const odbcDriverLinuxDocuments = 'https://docs.microsoft.com/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver15';
|
||||
export const installMlsLinuxDocs = 'https://docs.microsoft.com/sql/linux/sql-server-linux-setup-machine-learning?toc=%2fsql%2fadvanced-analytics%2ftoc.json&view=sql-server-ver15';
|
||||
export const installMlsWindowsDocs = 'https://docs.microsoft.com/sql/advanced-analytics/install/sql-machine-learning-services-windows-install?view=sql-server-ver15';
|
||||
|
||||
// CSS Styles
|
||||
//
|
||||
export namespace cssStyles {
|
||||
export const title = { 'font-size': '14px', 'font-weight': '600' };
|
||||
export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text', 'border': 'none', 'background-color': '#FFFFFF' };
|
||||
export const tableRow = { 'border-top': 'solid 1px #ccc', 'border-bottom': 'solid 1px #ccc', 'border-left': 'none', 'border-right': 'none' };
|
||||
export const hyperlink = { 'user-select': 'text', 'color': '#0078d4', 'text-decoration': 'underline', 'cursor': 'pointer' };
|
||||
export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' };
|
||||
export const overflowEllipsisText = { ...text, 'overflow': 'hidden', 'text-overflow': 'ellipsis' };
|
||||
export const nonSelectableText = { ...cssStyles.text, 'user-select': 'none' };
|
||||
export const tabHeaderText = { 'margin-block-start': '2px', 'margin-block-end': '0px', 'user-select': 'none' };
|
||||
export const selectedResourceHeaderTab = { 'font-weight': 'bold', 'color': '' };
|
||||
export const unselectedResourceHeaderTab = { 'font-weight': '', 'color': '#0078d4' };
|
||||
export const selectedTabDiv = { 'border-bottom': '2px solid #000' };
|
||||
export const unselectedTabDiv = { 'border-bottom': '1px solid #ccc' };
|
||||
export const lastUpdatedText = { ...text, 'color': '#595959' };
|
||||
export const errorText = { ...text, 'color': 'red' };
|
||||
}
|
||||
|
||||
@@ -11,10 +11,13 @@ import * as childProcess from 'child_process';
|
||||
const ExecScriptsTimeoutInSeconds = 600000;
|
||||
export class ProcessService {
|
||||
|
||||
public timeout = ExecScriptsTimeoutInSeconds;
|
||||
|
||||
public async execScripts(exeFilePath: string, scripts: string[], outputChannel?: vscode.OutputChannel): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
|
||||
const scriptExecution = childProcess.spawn(exeFilePath);
|
||||
let timer: NodeJS.Timeout;
|
||||
let output: string;
|
||||
scripts.forEach(script => {
|
||||
scriptExecution.stdin.write(`${script}\n`);
|
||||
@@ -34,19 +37,23 @@ export class ProcessService {
|
||||
}
|
||||
|
||||
scriptExecution.on('exit', (code) => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
if (code === 0) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(`Process exited with code: ${code}. output: ${output}`);
|
||||
}
|
||||
|
||||
});
|
||||
setTimeout(() => {
|
||||
timer = setTimeout(() => {
|
||||
try {
|
||||
scriptExecution.kill();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}, ExecScriptsTimeoutInSeconds);
|
||||
}, this.timeout);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,7 +63,9 @@ export class ProcessService {
|
||||
outputChannel.appendLine(` > ${cmd}`);
|
||||
}
|
||||
|
||||
let child = childProcess.exec(cmd, (err, stdout) => {
|
||||
let child = childProcess.exec(cmd, {
|
||||
timeout: this.timeout
|
||||
}, (err, stdout) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as nbExtensionApis from '../typings/notebookServices';
|
||||
import { ApiWrapper } from './apiWrapper';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
const listPythonPackagesQuery = `
|
||||
EXEC sp_execute_external_script
|
||||
@@ -25,11 +26,11 @@ Declare @external_script_enabled bit
|
||||
SELECT @external_script_enabled=config_value FROM @tablevar WHERE name = 'external scripts enabled'
|
||||
SELECT @external_script_enabled`;
|
||||
|
||||
const checkPythonInstalledQuery = `
|
||||
const checkLanguageInstalledQuery = `
|
||||
|
||||
SELECT is_installed
|
||||
FROM sys.dm_db_external_language_stats s, sys.external_languages l
|
||||
WHERE s.external_language_id = l.external_language_id AND language = 'Python'`;
|
||||
WHERE s.external_language_id = l.external_language_id AND language = '#LANGUAGE#'`;
|
||||
|
||||
const modifyExternalScriptConfigQuery = `
|
||||
|
||||
@@ -86,7 +87,21 @@ export class QueryRunner {
|
||||
* Returns true if python installed in the give SQL server instance
|
||||
*/
|
||||
public async isPythonInstalled(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
|
||||
let result = await this.runQuery(connection, checkPythonInstalledQuery);
|
||||
return this.isLanguageInstalled(connection, constants.pythonLanguageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if R installed in the give SQL server instance
|
||||
*/
|
||||
public async isRInstalled(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
|
||||
return this.isLanguageInstalled(connection, constants.rLanguageName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if language installed in the give SQL server instance
|
||||
*/
|
||||
private async isLanguageInstalled(connection: azdata.connection.ConnectionProfile, language: string): Promise<boolean> {
|
||||
let result = await this.runQuery(connection, checkLanguageInstalledQuery.replace('#LANGUAGE#', language));
|
||||
let isInstalled = false;
|
||||
if (result && result.rows && result.rows.length > 0) {
|
||||
isInstalled = result.rows[0][0].displayValue === '1';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as uuid from 'uuid';
|
||||
import * as UUID from 'vscode-languageclient/lib/utils/uuid';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
@@ -15,7 +15,7 @@ import { promisify } from 'util';
|
||||
export async function execCommandOnTempFile<T>(content: string, command: (filePath: string) => Promise<T>): Promise<T> {
|
||||
let tempFilePath: string = '';
|
||||
try {
|
||||
tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${uuid.v4()}`);
|
||||
tempFilePath = path.join(os.tmpdir(), `ads_ml_temp_${UUID.generateUuid()}`);
|
||||
await fs.promises.writeFile(tempFilePath, content);
|
||||
let result = await command(tempFilePath);
|
||||
return result;
|
||||
@@ -46,3 +46,7 @@ export function getPythonExePath(rootFolder: string): string {
|
||||
constants.pythonBundleVersion,
|
||||
process.platform === constants.winPlatform ? 'python.exe' : 'bin/python3');
|
||||
}
|
||||
|
||||
export function isWindows(): boolean {
|
||||
return process.platform === 'win32';
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as nbExtensionApis from '../typings/notebookServices';
|
||||
import { PackageManager } from '../packageManagement/packageManager';
|
||||
import * as constants from '../common/constants';
|
||||
@@ -13,12 +14,13 @@ import { ApiWrapper } from '../common/apiWrapper';
|
||||
import { QueryRunner } from '../common/queryRunner';
|
||||
import { ProcessService } from '../common/processService';
|
||||
import { Config } from '../common/config';
|
||||
import { ServerConfigWidget } from '../widgets/serverConfigWidgets';
|
||||
import { ServerConfigManager } from '../serverConfig/serverConfigManager';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
*/
|
||||
export default class MainController implements vscode.Disposable {
|
||||
|
||||
private _outputChannel: vscode.OutputChannel;
|
||||
private _rootPath = this._context.extensionPath;
|
||||
private _config: Config;
|
||||
@@ -28,7 +30,8 @@ export default class MainController implements vscode.Disposable {
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _queryRunner: QueryRunner,
|
||||
private _processService: ProcessService,
|
||||
private _packageManager?: PackageManager
|
||||
private _packageManager?: PackageManager,
|
||||
private _serverConfigManager?: ServerConfigManager
|
||||
) {
|
||||
this._outputChannel = this._apiWrapper.createOutputChannel(constants.extensionOutputChannel);
|
||||
this._rootPath = this._context.extensionPath;
|
||||
@@ -63,14 +66,28 @@ export default class MainController implements vscode.Disposable {
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
|
||||
this._outputChannel.show(true);
|
||||
let nbApis = await this.getNotebookExtensionApis();
|
||||
await this._config.load();
|
||||
|
||||
let tasks = new ServerConfigWidget(this._apiWrapper, this.serverConfigManager);
|
||||
tasks.register();
|
||||
|
||||
let packageManager = this.getPackageManager(nbApis);
|
||||
this._apiWrapper.registerCommand(constants.mlManagePackagesCommand, (async () => {
|
||||
await packageManager.managePackages();
|
||||
}));
|
||||
this._apiWrapper.registerTaskHandler(constants.mlManagePackagesCommand, async () => {
|
||||
await packageManager.managePackages();
|
||||
});
|
||||
this._apiWrapper.registerTaskHandler(constants.mlOdbcDriverCommand, async () => {
|
||||
await this.serverConfigManager.openOdbcDriverDocuments();
|
||||
});
|
||||
this._apiWrapper.registerTaskHandler(constants.mlsDocumentsCommand, async () => {
|
||||
await this.serverConfigManager.openDocuments();
|
||||
});
|
||||
|
||||
try {
|
||||
await packageManager.installDependencies();
|
||||
} catch (err) {
|
||||
@@ -90,10 +107,13 @@ export default class MainController implements vscode.Disposable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Package manager instance
|
||||
* Returns the server config manager instance
|
||||
*/
|
||||
public set packageManager(value: PackageManager) {
|
||||
this._packageManager = value;
|
||||
public get serverConfigManager(): ServerConfigManager {
|
||||
if (!this._serverConfigManager) {
|
||||
this._serverConfigManager = new ServerConfigManager(this._apiWrapper, this._queryRunner);
|
||||
}
|
||||
return this._serverConfigManager;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 azdata from 'azdata';
|
||||
import { QueryRunner } from '../common/queryRunner';
|
||||
import * as constants from '../common/constants';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import * as utils from '../common/utils';
|
||||
|
||||
export class ServerConfigManager {
|
||||
|
||||
/**
|
||||
* Creates a new instance of ServerConfigManager
|
||||
*/
|
||||
constructor(
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _queryRunner: QueryRunner,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens server config documents
|
||||
*/
|
||||
public async openDocuments(): Promise<boolean> {
|
||||
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.mlsDocuments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens ODBC driver documents
|
||||
*/
|
||||
public async openOdbcDriverDocuments(): Promise<boolean> {
|
||||
if (utils.isWindows()) {
|
||||
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.odbcDriverWindowsDocuments));
|
||||
} else {
|
||||
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.odbcDriverLinuxDocuments));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens install MLS documents
|
||||
*/
|
||||
public async openInstallDocuments(): Promise<boolean> {
|
||||
if (utils.isWindows()) {
|
||||
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.installMlsWindowsDocs));
|
||||
} else {
|
||||
return await this._apiWrapper.openExternal(vscode.Uri.parse(constants.installMlsLinuxDocs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if mls is installed in the give SQL server instance
|
||||
*/
|
||||
public async isMachineLearningServiceEnabled(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
|
||||
return this._queryRunner.isMachineLearningServiceEnabled(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if R installed in the give SQL server instance
|
||||
*/
|
||||
public async isRInstalled(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
|
||||
return this._queryRunner.isRInstalled(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if python installed in the give SQL server instance
|
||||
*/
|
||||
public async isPythonInstalled(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
|
||||
return this._queryRunner.isPythonInstalled(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates external script config
|
||||
* @param connection SQL Connection
|
||||
* @param enable if true external script will be enabled
|
||||
*/
|
||||
public async updateExternalScriptConfig(connection: azdata.connection.ConnectionProfile, enable: boolean): Promise<boolean> {
|
||||
await this._queryRunner.updateExternalScriptConfig(connection, enable);
|
||||
let current = await this._queryRunner.isMachineLearningServiceEnabled(connection);
|
||||
if (current === enable) {
|
||||
this._apiWrapper.showInfoMessage(enable ? constants.mlsEnabledMessage : constants.mlsDisabledMessage);
|
||||
} else {
|
||||
this._apiWrapper.showErrorMessage(constants.mlsConfigUpdateFailed);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { ProcessService } from '../../common/processService';
|
||||
import * as utils from '../../common/utils';
|
||||
import should = require('should');
|
||||
|
||||
interface TestContext {
|
||||
|
||||
outputChannel: vscode.OutputChannel;
|
||||
}
|
||||
|
||||
function createContext(): TestContext {
|
||||
return {
|
||||
outputChannel: {
|
||||
name: '',
|
||||
append: () => { },
|
||||
appendLine: () => { },
|
||||
clear: () => { },
|
||||
show: () => { },
|
||||
hide: () => { },
|
||||
dispose: () => { }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function execFolderListCommand(context: TestContext, service : ProcessService): Promise<void> {
|
||||
if (utils.isWindows()) {
|
||||
return service.execScripts('cmd', ['dir', '.'], context.outputChannel);
|
||||
} else {
|
||||
return service.execScripts('/bin/sh', ['-c', 'ls'], context.outputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
function execFolderListBufferedCommand(context: TestContext, service : ProcessService): Promise<string> {
|
||||
if (utils.isWindows()) {
|
||||
return service.executeBufferedCommand('dir', context.outputChannel);
|
||||
} else {
|
||||
return service.executeBufferedCommand('ls', context.outputChannel);
|
||||
}
|
||||
}
|
||||
|
||||
describe('Process Service', () => {
|
||||
it('Executing a valid script should return successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
let service = new ProcessService();
|
||||
await should(execFolderListCommand(context, service)).resolved();
|
||||
});
|
||||
|
||||
it('execFolderListCommand should reject if command time out @UNSTABLE@', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
let service = new ProcessService();
|
||||
service.timeout = 10;
|
||||
await should(execFolderListCommand(context, service)).rejected();
|
||||
});
|
||||
|
||||
it('executeBufferedCommand should resolve give valid script', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
let service = new ProcessService();
|
||||
service.timeout = 2000;
|
||||
await should(execFolderListBufferedCommand(context, service)).resolved();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -72,8 +72,7 @@ function createContext(): TestContext {
|
||||
}
|
||||
|
||||
function createController(testContext: TestContext): MainController {
|
||||
let controller = new MainController(testContext.context, testContext.apiWrapper.object, testContext.queryRunner.object, testContext.processService.object);
|
||||
controller.packageManager = testContext.packageManager.object;
|
||||
let controller = new MainController(testContext.context, testContext.apiWrapper.object, testContext.queryRunner.object, testContext.processService.object, testContext.packageManager.object);
|
||||
return controller;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { QueryRunner } from '../../common/queryRunner';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as should from 'should';
|
||||
import { ServerConfigManager } from '../../serverConfig/serverConfigManager';
|
||||
|
||||
interface TestContext {
|
||||
|
||||
apiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
queryRunner: TypeMoq.IMock<QueryRunner>;
|
||||
}
|
||||
|
||||
function createContext(): TestContext {
|
||||
return {
|
||||
apiWrapper: TypeMoq.Mock.ofType(ApiWrapper),
|
||||
queryRunner: TypeMoq.Mock.ofType(QueryRunner)
|
||||
};
|
||||
}
|
||||
|
||||
describe('Server Config Manager', () => {
|
||||
it('openDocuments should open document in browser successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
should.equal(await serverConfigManager.openDocuments(), true);
|
||||
});
|
||||
|
||||
it('openOdbcDriverDocuments should open document in browser successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
should.equal(await serverConfigManager.openOdbcDriverDocuments(), true);
|
||||
});
|
||||
|
||||
it('openInstallDocuments should open document in browser successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.apiWrapper.setup(x => x.openExternal(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
should.equal(await serverConfigManager.openInstallDocuments(), true);
|
||||
});
|
||||
|
||||
it('isMachineLearningServiceEnabled should return true if external script is enabled', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
should.equal(await serverConfigManager.isMachineLearningServiceEnabled(connection), true);
|
||||
});
|
||||
|
||||
it('isRInstalled should return true if R is installed', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.queryRunner.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
should.equal(await serverConfigManager.isRInstalled(connection), true);
|
||||
});
|
||||
|
||||
it('isPythonInstalled should return true if Python is installed', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.queryRunner.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
should.equal(await serverConfigManager.isPythonInstalled(connection), true);
|
||||
});
|
||||
|
||||
it('updateExternalScriptConfig should show info message if updated successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.queryRunner.setup(x => x.updateExternalScriptConfig(TypeMoq.It.isAny(), true)).returns(() => Promise.resolve());
|
||||
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
|
||||
context.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
|
||||
context.apiWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
await serverConfigManager.updateExternalScriptConfig(connection, true);
|
||||
|
||||
context.apiWrapper.verify(x => x.showInfoMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
it('updateExternalScriptConfig should show error message if did not updated successfully', async function (): Promise<void> {
|
||||
const context = createContext();
|
||||
context.queryRunner.setup(x => x.updateExternalScriptConfig(TypeMoq.It.isAny(), true)).returns(() => Promise.resolve());
|
||||
context.queryRunner.setup(x => x.isMachineLearningServiceEnabled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
|
||||
context.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
|
||||
context.apiWrapper.setup(x => x.showErrorMessage(TypeMoq.It.isAny())).returns(() => Promise.resolve(''));
|
||||
let serverConfigManager = new ServerConfigManager(context.apiWrapper.object, context.queryRunner.object);
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
await serverConfigManager.updateExternalScriptConfig(connection, true);
|
||||
|
||||
context.apiWrapper.verify(x => x.showErrorMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
});
|
||||
160
extensions/machine-learning-services/src/widgets/configTable.ts
Normal file
160
extensions/machine-learning-services/src/widgets/configTable.ts
Normal file
@@ -0,0 +1,160 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ApiWrapper } from '../common/apiWrapper';
|
||||
import { ServerConfigManager } from '../serverConfig/serverConfigManager';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
export class ConfigTable {
|
||||
private _statusTable: azdata.DeclarativeTableComponent;
|
||||
|
||||
constructor(private _apiWrapper: ApiWrapper, private _serverConfigManager: ServerConfigManager, private _modelBuilder: azdata.ModelBuilder, private _loadingComponent: azdata.LoadingComponent) {
|
||||
this._statusTable = this._modelBuilder.declarativeTable()
|
||||
.withProperties<azdata.DeclarativeTableProperties>(
|
||||
{
|
||||
columns: [
|
||||
{ // Config
|
||||
displayName: constants.mlsConfigTitle,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: 175,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Status icon
|
||||
displayName: constants.mlsConfigStatus,
|
||||
ariaLabel: constants.mlsConfigStatus,
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: 25,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Action
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: 150,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
}
|
||||
],
|
||||
data: [],
|
||||
ariaLabel: constants.mlsConfigTitle
|
||||
})
|
||||
.component();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config table component
|
||||
*/
|
||||
public get component(): azdata.DeclarativeTableComponent {
|
||||
return this._statusTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes the config table
|
||||
*/
|
||||
public async refresh(): Promise<void> {
|
||||
this._loadingComponent.updateProperties({ loading: true });
|
||||
let connection = await this.getCurrentConnection();
|
||||
const externalScriptsConfig = await this.createTableRowComponents(constants.mlsExternalExecuteScriptTitle,
|
||||
async () => {
|
||||
return await this._serverConfigManager.isMachineLearningServiceEnabled(connection);
|
||||
}, async (enable) => {
|
||||
this._loadingComponent.updateProperties({ loading: true });
|
||||
await this._serverConfigManager.updateExternalScriptConfig(connection, enable);
|
||||
await this.refresh();
|
||||
}
|
||||
);
|
||||
const pythonConfig = await this.createTableRowComponents(constants.mlsPythonLanguageTitle,
|
||||
async () => {
|
||||
return await this._serverConfigManager.isPythonInstalled(connection);
|
||||
}, async () => {
|
||||
await this._serverConfigManager.openInstallDocuments();
|
||||
}
|
||||
);
|
||||
const rConfig = await this.createTableRowComponents(constants.mlsRLanguageTitle,
|
||||
async () => {
|
||||
return await this._serverConfigManager.isRInstalled(connection);
|
||||
}, async () => {
|
||||
await this._serverConfigManager.openInstallDocuments();
|
||||
}
|
||||
);
|
||||
this._statusTable.data = [externalScriptsConfig, pythonConfig, rConfig];
|
||||
this._loadingComponent.updateProperties({ loading: false });
|
||||
}
|
||||
|
||||
private async getCurrentConnection(): Promise<azdata.connection.ConnectionProfile> {
|
||||
return await this._apiWrapper.getCurrentConnection();
|
||||
}
|
||||
|
||||
private async createTableRowComponents(configName: string, checkEnabledFunction: () => Promise<boolean>, updateFunction: (enable: boolean) => Promise<void>): Promise<azdata.Component[]> {
|
||||
const isEnabled = await checkEnabledFunction();
|
||||
|
||||
const nameCell = this._modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({
|
||||
value: configName,
|
||||
CSSStyles: { 'user-select': 'none', ...constants.cssStyles.text }
|
||||
}).component();
|
||||
const statusIconCell = this._modelBuilder.text()
|
||||
.withProperties<azdata.TextComponentProperties>({
|
||||
value: this.getConfigStatusIcon(isEnabled),
|
||||
ariaRole: 'img',
|
||||
title: this.getConfigStatusTest(isEnabled),
|
||||
CSSStyles: { 'user-select': 'none', ...constants.cssStyles.text }
|
||||
}).component();
|
||||
|
||||
const button = this._modelBuilder.button().withProperties<azdata.ButtonProperties>({
|
||||
label: '',
|
||||
title: ''
|
||||
}).component();
|
||||
|
||||
button.label = this.getLabel(isEnabled);
|
||||
button.onDidClick(async () => {
|
||||
await updateFunction(!isEnabled);
|
||||
const isEnabledNewValue = await checkEnabledFunction();
|
||||
button.label = this.getLabel(isEnabledNewValue);
|
||||
});
|
||||
return [
|
||||
nameCell,
|
||||
statusIconCell,
|
||||
button
|
||||
];
|
||||
}
|
||||
|
||||
private getConfigStatusIcon(enabled: boolean): string {
|
||||
if (enabled) {
|
||||
return '✔️';
|
||||
} else {
|
||||
return '❌';
|
||||
}
|
||||
}
|
||||
|
||||
private getConfigStatusTest(enabled: boolean): string {
|
||||
if (enabled) {
|
||||
return constants.mlsEnableButtonTitle;
|
||||
} else {
|
||||
return constants.mlsDisableButtonTitle;
|
||||
}
|
||||
}
|
||||
|
||||
private getLabel(isEnabled: boolean): string {
|
||||
return isEnabled ? constants.mlsDisableButtonTitle : constants.mlsEnableButtonTitle;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { ApiWrapper } from '../common/apiWrapper';
|
||||
import { ServerConfigManager } from '../serverConfig/serverConfigManager';
|
||||
import { ConfigTable } from './configTable';
|
||||
|
||||
export class ServerConfigWidget {
|
||||
|
||||
constructor(private _apiWrapper: ApiWrapper, private _serverConfigManager: ServerConfigManager) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the widget and initializes the components
|
||||
*/
|
||||
public register(): void {
|
||||
azdata.ui.registerModelViewProvider('ml.tasks', async (view) => {
|
||||
const container = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '100%',
|
||||
height: '100%'
|
||||
}).component();
|
||||
const mainContainer = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'column',
|
||||
width: '270px',
|
||||
height: '100%',
|
||||
position: 'absolute'
|
||||
}).component();
|
||||
mainContainer.addItem(container, {
|
||||
CSSStyles: {
|
||||
'padding-top': '25px',
|
||||
'padding-left': '5px'
|
||||
}
|
||||
});
|
||||
let spinner = view.modelBuilder.loadingComponent()
|
||||
.withItem(mainContainer)
|
||||
.withProperties<azdata.LoadingComponentProperties>({ loading: true })
|
||||
.component();
|
||||
|
||||
const configTable = new ConfigTable(this._apiWrapper, this._serverConfigManager, view.modelBuilder, spinner);
|
||||
|
||||
this.addRow(container, view, configTable.component);
|
||||
|
||||
await view.initializeModel(spinner);
|
||||
await configTable.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private addRow(container: azdata.FlexContainer, view: azdata.ModelView, component: azdata.Component) {
|
||||
const bookRow = view.modelBuilder.flexContainer().withLayout({
|
||||
flexFlow: 'row',
|
||||
justifyContent: 'space-between',
|
||||
height: '100'
|
||||
}).component();
|
||||
bookRow.addItem(component, {
|
||||
CSSStyles: {
|
||||
'width': '100',
|
||||
'hight': '100',
|
||||
'padding-top': '10px',
|
||||
'text-align': 'left'
|
||||
}
|
||||
});
|
||||
container.addItems([bookRow]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,23 +7,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.6.tgz#b8622d50557dd155e9f2f634b7d68fd38de5e94b"
|
||||
integrity sha512-1axi39YdtBI7z957vdqXI4Ac25e7YihYQtJa+Clnxg1zTJEaIRbndt71O3sP4GAMgiAm0pY26/b9BrY4MR/PMw==
|
||||
|
||||
"@types/node@*":
|
||||
version "12.12.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.17.tgz#191b71e7f4c325ee0fb23bc4a996477d92b8c39b"
|
||||
integrity sha512-Is+l3mcHvs47sKy+afn2O1rV4ldZFU7W8101cNlOd+MRbjM4Onida8jSZnJdTe/0Pcf25g9BNIUsuugmE6puHA==
|
||||
|
||||
"@types/node@^10.14.8":
|
||||
version "10.14.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.17.tgz#b96d4dd3e427382482848948041d3754d40fd5ce"
|
||||
integrity sha512-p/sGgiPaathCfOtqu2fx5Mu1bcjuP8ALFg4xpGgNkcin7LwRyzUKniEHBKdcE1RPsenq5JVPIpMTJSygLboygQ==
|
||||
|
||||
"@types/uuid@^3.4.5":
|
||||
version "3.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-3.4.6.tgz#d2c4c48eb85a757bf2927f75f939942d521e3016"
|
||||
integrity sha512-cCdlC/1kGEZdEglzOieLDYBxHsvEOIg7kp/2FYyVR9Pxakq+Qf/inL3RKQ+PA8gOlI/NnL+fXmQH12nwcGzsHw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
ajv@^6.5.5:
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1"
|
||||
@@ -1230,6 +1218,11 @@ semver@^5.4.1:
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b"
|
||||
integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==
|
||||
|
||||
semver@^6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
|
||||
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
|
||||
|
||||
should-equal@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3"
|
||||
@@ -1593,6 +1586,32 @@ vinyl@^2.0.0, vinyl@^2.0.1, vinyl@^2.0.2:
|
||||
remove-trailing-separator "^1.0.1"
|
||||
replace-ext "^1.0.0"
|
||||
|
||||
vscode-jsonrpc@^5.0.0-next.5:
|
||||
version "5.0.0-next.5"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.5.tgz#43284da590b86320e427c3256bbe6849d8c6a6bd"
|
||||
integrity sha512-k9akfglxWgr0dtLNscq2uBq48XJwnhf4EaDxn05KQowRwR0DkNML0zeYqFRLtXZe6x5vpL5ppyu4o6GqL+23YQ==
|
||||
|
||||
vscode-languageclient@^5.3.0-next.1:
|
||||
version "5.3.0-next.9"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-5.3.0-next.9.tgz#34f58017647f15cd86015f7af45935dc750611f7"
|
||||
integrity sha512-BFA3X1y2EI2CfsSBy0KG2Xr5BOYfd/97jTmD+doqL6oj+cY8S7AmRCOwb2f9Hbjq8GWL7YC+OJ0leZEUSPgP0A==
|
||||
dependencies:
|
||||
semver "^6.3.0"
|
||||
vscode-languageserver-protocol "^3.15.0-next.8"
|
||||
|
||||
vscode-languageserver-protocol@^3.15.0-next.8:
|
||||
version "3.15.0-next.14"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.14.tgz#e7eb337f1adb50b4a41c05d436ce03c8df1f4d14"
|
||||
integrity sha512-xUwwno6Q6RFd2Z2EWV9D3dQlsKPnHyiZMNWq+EC7JJdp2WH1gRlD+KPX4UGRCnJK0WI5omqHV313IESPwRY5xA==
|
||||
dependencies:
|
||||
vscode-jsonrpc "^5.0.0-next.5"
|
||||
vscode-languageserver-types "^3.15.0-next.9"
|
||||
|
||||
vscode-languageserver-types@^3.15.0-next.9:
|
||||
version "3.15.0-next.9"
|
||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.9.tgz#957a9d1d5998a02edf62298fb7e37d9efcc6c157"
|
||||
integrity sha512-Rl/8qJ6932nrHCdPn+9y0x08uLVQaSLRG+U4JzhyKpWU4eJbVaDRoAcz1Llj7CErJGbPr6kdBvShPy5fRfR+Uw==
|
||||
|
||||
vscode-nls@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c"
|
||||
|
||||
Reference in New Issue
Block a user