Machine Learning Services - Enabling external script for package management only (#9519)

* Machine learning services extension - removed config table and added config update to package manager
This commit is contained in:
Leila Lali
2020-03-19 16:48:33 -07:00
committed by GitHub
parent 35b27f1304
commit 1520441b84
20 changed files with 145 additions and 340 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 32 32"><defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:url(#linear-gradient-2);}.cls-3{fill:url(#linear-gradient-3);}.cls-4{fill:none;}</style><linearGradient id="linear-gradient" x1="15.985" y1="32" x2="15.985" y2="7.226" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#5ea0ef"/><stop offset="1" stop-color="#83b9f9"/></linearGradient><linearGradient id="linear-gradient-2" x1="4.335" y1="31.971" x2="4.335" y2="14.658" gradientUnits="userSpaceOnUse"><stop offset="0.101" stop-color="#32bedd"/><stop offset="1" stop-color="#50e6ff"/></linearGradient><linearGradient id="linear-gradient-3" x1="27.665" y1="31.971" x2="27.665" gradientUnits="userSpaceOnUse"><stop offset="0.243" stop-color="#0078d7"/><stop offset="1" stop-color="#5ea0ef"/></linearGradient></defs><title>j_</title><rect class="cls-1" x="12.313" y="7.226" width="7.344" height="24.774" rx="0.826"/><rect class="cls-2" x="0.664" y="14.658" width="7.344" height="17.312" rx="0.826"/><rect class="cls-3" x="23.993" width="7.344" height="31.971" rx="0.826"/><rect class="cls-4" width="32" height="32"/></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{font-size:16px;font-family:FullMDL2Assets, Full MDL2 Assets;}</style></defs><title>v_</title><text class="cls-1" transform="translate(0 16)"></text></svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -1,5 +1,5 @@
{
"name": "machineLearningServices",
"name": "machine-learning-services",
"displayName": "%displayName%",
"description": "%description%",
"version": "1.0.0",
@@ -14,7 +14,7 @@
"onDashboardOpen"
],
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
"icon": "images/ML_ExtensionIcon.png",
"icon": "images/extensionIcon.png",
"aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e",
"main": "./out/main",
"repository": {
@@ -59,7 +59,11 @@
},
{
"command": "mls.command.predictModel",
"title": "%mls.command.predictModel%"
"title": "%mls.command.predictModel%",
"icon": {
"light": "./images/makePredictions.svg",
"dark": "./images/makePredictions.svg"
}
},
{
"command": "mls.command.manageModels",
@@ -67,7 +71,11 @@
},
{
"command": "mls.command.registerModel",
"title": "%mls.command.registerModel%"
"title": "%mls.command.registerModel%",
"icon": {
"light": "./images/registerModel.svg",
"dark": "./images/registerModel.svg"
}
},
{
"command": "mls.command.manageLanguages",
@@ -95,20 +103,10 @@
"when": "connectionProvider == 'MSSQL' && !mssql:iscloud && dashboardContext == 'server'",
"container": {
"grid-container": [
{
"name": "%title.configurations%",
"row": 0,
"col": 0,
"widget": {
"modelview": {
"id": "ml.tasks"
}
}
},
{
"name": "%title.tasks%",
"row": 0,
"col": 1,
"col": 0,
"widget": {
"tasks-widget": [
"mls.command.managePackages",
@@ -120,8 +118,8 @@
},
{
"name": "%title.documents%",
"row": 1,
"col": 0,
"row": 0,
"col": 1,
"widget": {
"tasks-widget": [
"mls.command.odbcdriver",
@@ -138,7 +136,7 @@
"request": "^2.88.0",
"vscode-nls": "^4.0.0",
"vscode-languageclient": "^5.3.0-next.1",
"@azure/arm-machinelearningservices" : "^3.0.0",
"@azure/arm-machinelearningservices": "^3.0.0",
"polly-js": "^1.6.3"
},
"devDependencies": {
@@ -153,6 +151,6 @@
"vscodetestcover": "github:corivera/vscodetestcover#1.0.5"
},
"resolutions": {
"esprima": "^4.0.0"
"esprima": "^4.0.0"
}
}

View File

@@ -5,8 +5,8 @@
"title.documents": "Documents",
"title.configurations": "Configurations",
"title.endpoints": "Endpoints",
"mls.command.managePackages": "Manage Packages in SQL Server",
"mls.command.manageLanguages": "Manage External Languages",
"mls.command.managePackages": "Manage packages in database",
"mls.command.manageLanguages": "Manage external languages",
"mls.command.predictModel": "Make prediction",
"mls.command.manageModels": "Manage models",
"mls.command.registerModel": "Register model",

View File

@@ -50,6 +50,9 @@ export function taskFailedError(taskName: string, err: string): string { return
export const installDependenciesMsgTaskName = localize('mls.installDependencies.msgTaskName', "Installing Machine Learning extension 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.");
export const enableExternalScriptsError = localize('mls.enableExternalScriptsError', "Failed to enable External script.");
export const externalScriptsIsRequiredError = localize('mls.externalScriptsIsRequiredError', "External script configuration is required for this action.");
export function confirmInstallPythonPackages(packages: string): string {
return localize('mls.installDependencies.confirmInstallPythonPackages'
, "The following Python packages are required to install: {0}. Are you sure you want to install?", packages);
@@ -61,7 +64,6 @@ export const packageManagerNoConnection = localize('mls.packageManager.NoConnect
export const notebookExtensionNotLoaded = localize('mls.notebookExtensionNotLoaded', "Notebook extension is not loaded");
export const mssqlExtensionNotLoaded = localize('mls.mssqlExtensionNotLoaded', "MSSQL 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");
@@ -92,7 +94,7 @@ export const extLangLanguagePlatform = localize('extLang.languagePlatform', "Pla
export const deleteTitle = localize('extLang.delete', "Delete");
export const extLangInstallButtonText = localize('extLang.installButtonText', "Install");
export const extLangCancelButtonText = localize('extLang.CancelButtonText', "Cancel");
export const extLangDoneButtonText = localize('extLang.DoneButtonText', "Done");
export const extLangDoneButtonText = localize('extLang.DoneButtonText', "Close");
export const extLangOkButtonText = localize('extLang.OkButtonText', "OK");
export const extLangSaveButtonText = localize('extLang.SaveButtonText', "Save");
export const extLangLanguageName = localize('extLang.languageName', "Name");
@@ -131,14 +133,15 @@ export const azureModels = localize('models.azureModels', "Models");
export const azureModelsTitle = localize('models.azureModelsTitle', "Azure models");
export const localModelsTitle = localize('models.localModelsTitle', "Local models");
export const modelSourcesTitle = localize('models.modelSourcesTitle', "Source location");
export const modelSourcePageTitle = localize('models.modelSourcePageTitle', "Ender model source details");
export const modelSourcePageTitle = localize('models.modelSourcePageTitle', "Enter model source details");
export const columnSelectionPageTitle = localize('models.columnSelectionPageTitle', "Select input columns");
export const modelDetailsPageTitle = localize('models.modelDetailsPageTitle', "Provide model details");
export const modelLocalSourceTitle = localize('models.modelLocalSourceTitle', "Source file");
export const currentModelsTitle = localize('models.currentModelsTitle', "Models");
export const azureRegisterModel = localize('models.azureRegisterModel', "Register");
export const azureRegisterModel = localize('models.azureRegisterModel', "Deploy");
export const predictModel = localize('models.predictModel', "Predict");
export const registerModelTitle = localize('models.RegisterWizard', "Register model");
export const registerModelTitle = localize('models.RegisterWizard', "Deployed models");
export const deployModelTitle = localize('models.deployModelTitle', "Deploy models");
export const makePredictionTitle = localize('models.makePredictionTitle', "Make prediction");
export const modelRegisteredSuccessfully = localize('models.modelRegisteredSuccessfully', "Model registered successfully");
export const modelFailedToRegister = localize('models.modelFailedToRegistered', "Model failed to register");

View File

@@ -13,8 +13,7 @@ import { ApiWrapper } from '../common/apiWrapper';
import { QueryRunner } from '../common/queryRunner';
import { ProcessService } from '../common/processService';
import { Config } from '../configurations/config';
import { ServerConfigWidget } from '../widgets/serverConfigWidgets';
import { ServerConfigManager } from '../serverConfig/serverConfigManager';
import { PackageManagementService } from '../packageManagement/packageManagementService';
import { HttpClient } from '../common/httpClient';
import { LanguageController } from '../views/externalLanguages/languageController';
import { LanguageService } from '../externalLanguage/languageService';
@@ -38,7 +37,7 @@ export default class MainController implements vscode.Disposable {
private _queryRunner: QueryRunner,
private _processService: ProcessService,
private _packageManager?: PackageManager,
private _serverConfigManager?: ServerConfigManager,
private _packageManagementService?: PackageManagementService,
private _httpClient?: HttpClient
) {
this._outputChannel = this._apiWrapper.createOutputChannel(constants.extensionOutputChannel);
@@ -91,9 +90,6 @@ export default class MainController implements vscode.Disposable {
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();
@@ -145,10 +141,10 @@ export default class MainController implements vscode.Disposable {
await modelManagementController.predictModel();
});
this._apiWrapper.registerTaskHandler(constants.mlOdbcDriverCommand, async () => {
await this.serverConfigManager.openOdbcDriverDocuments();
await this.packageManagementService.openOdbcDriverDocuments();
});
this._apiWrapper.registerTaskHandler(constants.mlsDocumentsCommand, async () => {
await this.serverConfigManager.openDocuments();
await this.packageManagementService.openDocuments();
});
}
@@ -157,7 +153,7 @@ export default class MainController implements vscode.Disposable {
*/
public getPackageManager(nbApis: nbExtensionApis.IExtensionApi): PackageManager {
if (!this._packageManager) {
this._packageManager = new PackageManager(this._outputChannel, this._rootPath, this._apiWrapper, this._queryRunner, this._processService, this._config, this.httpClient);
this._packageManager = new PackageManager(this._outputChannel, this._rootPath, this._apiWrapper, this.packageManagementService, this._processService, this._config, this.httpClient);
this._packageManager.init();
this._packageManager.packageManageProviders.forEach(provider => {
nbApis.registerPackageManager(provider.providerId, provider);
@@ -169,11 +165,11 @@ export default class MainController implements vscode.Disposable {
/**
* Returns the server config manager instance
*/
public get serverConfigManager(): ServerConfigManager {
if (!this._serverConfigManager) {
this._serverConfigManager = new ServerConfigManager(this._apiWrapper, this._queryRunner);
public get packageManagementService(): PackageManagementService {
if (!this._packageManagementService) {
this._packageManagementService = new PackageManagementService(this._apiWrapper, this._queryRunner);
}
return this._serverConfigManager;
return this._packageManagementService;
}
/**

View File

@@ -9,8 +9,9 @@ import { QueryRunner } from '../common/queryRunner';
import * as constants from '../common/constants';
import { ApiWrapper } from '../common/apiWrapper';
import * as utils from '../common/utils';
import * as nbExtensionApis from '../typings/notebookServices';
export class ServerConfigManager {
export class PackageManagementService {
/**
* Creates a new instance of ServerConfigManager
@@ -76,15 +77,41 @@ export class ServerConfigManager {
* @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);
public async enableExternalScriptConfig(connection: azdata.connection.ConnectionProfile): Promise<boolean> {
let current = await this._queryRunner.isMachineLearningServiceEnabled(connection);
if (current === enable) {
this._apiWrapper.showInfoMessage(enable ? constants.mlsEnabledMessage : constants.mlsDisabledMessage);
if (current) {
return current;
}
let confirmed = await utils.promptConfirm(constants.confirmEnableExternalScripts, this._apiWrapper);
if (confirmed) {
await this._queryRunner.updateExternalScriptConfig(connection, true);
current = await this._queryRunner.isMachineLearningServiceEnabled(connection);
if (current) {
this._apiWrapper.showInfoMessage(constants.mlsEnabledMessage);
} else {
this._apiWrapper.showErrorMessage(constants.mlsConfigUpdateFailed);
}
} else {
this._apiWrapper.showErrorMessage(constants.mlsConfigUpdateFailed);
this._apiWrapper.showErrorMessage(constants.externalScriptsIsRequiredError);
}
return current;
}
/**
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
public async getPythonPackages(connection: azdata.connection.ConnectionProfile): Promise<nbExtensionApis.IPackageDetails[]> {
return this._queryRunner.getPythonPackages(connection);
}
/**
* Returns python packages installed in SQL server instance
* @param connection SQL Connection
*/
public async getRPackages(connection: azdata.connection.ConnectionProfile): Promise<nbExtensionApis.IPackageDetails[]> {
return this._queryRunner.getRPackages(connection);
}
}

View File

@@ -7,7 +7,6 @@ import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as nbExtensionApis from '../typings/notebookServices';
import { SqlPythonPackageManageProvider } from './sqlPythonPackageManageProvider';
import { QueryRunner } from '../common/queryRunner';
import * as utils from '../common/utils';
import * as constants from '../common/constants';
import { ApiWrapper } from '../common/apiWrapper';
@@ -17,6 +16,7 @@ import { isNullOrUndefined } from 'util';
import { SqlRPackageManageProvider } from './sqlRPackageManageProvider';
import { HttpClient } from '../common/httpClient';
import { PackageConfigModel } from '../configurations/packageConfigModel';
import { PackageManagementService } from './packageManagementService';
export class PackageManager {
@@ -31,12 +31,12 @@ export class PackageManager {
private _outputChannel: vscode.OutputChannel,
private _rootFolder: string,
private _apiWrapper: ApiWrapper,
private _queryRunner: QueryRunner,
private _service: PackageManagementService,
private _processService: ProcessService,
private _config: Config,
private _httpClient: HttpClient) {
this._sqlPythonPackagePackageManager = new SqlPythonPackageManageProvider(this._outputChannel, this._apiWrapper, this._queryRunner, this._processService, this._config, this._httpClient);
this._sqlRPackageManager = new SqlRPackageManageProvider(this._outputChannel, this._apiWrapper, this._queryRunner, this._processService, this._config, this._httpClient);
this._sqlPythonPackagePackageManager = new SqlPythonPackageManageProvider(this._outputChannel, this._apiWrapper, this._service, this._processService, this._config, this._httpClient);
this._sqlRPackageManager = new SqlRPackageManageProvider(this._outputChannel, this._apiWrapper, this._service, this._processService, this._config, this._httpClient);
}
/**
@@ -67,11 +67,13 @@ export class PackageManager {
*/
public async managePackages(): Promise<void> {
try {
await this.enableExternalScript();
// Only execute the command if there's a valid connection with ml configuration enabled
//
let connection = await this.getCurrentConnection();
let isPythonInstalled = await this._queryRunner.isPythonInstalled(connection);
let isRInstalled = await this._queryRunner.isRInstalled(connection);
let isPythonInstalled = await this._service.isPythonInstalled(connection);
let isRInstalled = await this._service.isRInstalled(connection);
let defaultProvider: SqlRPackageManageProvider | SqlPythonPackageManageProvider | undefined;
if (connection && isPythonInstalled && this._sqlPythonPackagePackageManager.canUseProvider) {
defaultProvider = this._sqlPythonPackagePackageManager;
@@ -80,10 +82,10 @@ export class PackageManager {
}
if (connection && defaultProvider) {
await this.enableExternalScript();
// Install dependencies
//
if (!this.dependenciesInstalled) {
this._apiWrapper.showInfoMessage(constants.installingDependencies);
await this.installDependencies();
this.dependenciesInstalled = true;
}
@@ -99,7 +101,14 @@ export class PackageManager {
this._apiWrapper.showInfoMessage(constants.managePackageCommandError);
}
} catch (err) {
this._outputChannel.appendLine(err);
this._apiWrapper.showErrorMessage(err);
}
}
public async enableExternalScript(): Promise<void> {
let connection = await this.getCurrentConnection();
if (!await this._service.enableExternalScriptConfig(connection)) {
throw Error(constants.externalScriptsIsRequiredError);
}
}

View File

@@ -6,13 +6,13 @@
import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as nbExtensionApis from '../typings/notebookServices';
import { QueryRunner } from '../common/queryRunner';
import { ApiWrapper } from '../common/apiWrapper';
import { ProcessService } from '../common/processService';
import { Config } from '../configurations/config';
import { SqlPackageManageProviderBase, ScriptMode } from './SqlPackageManageProviderBase';
import { HttpClient } from '../common/httpClient';
import * as utils from '../common/utils';
import { PackageManagementService } from './packageManagementService';
/**
* Manage Package Provider for python packages inside SQL server databases
@@ -26,7 +26,7 @@ export class SqlPythonPackageManageProvider extends SqlPackageManageProviderBase
constructor(
private _outputChannel: vscode.OutputChannel,
apiWrapper: ApiWrapper,
private _queryRunner: QueryRunner,
private _service: PackageManagementService,
private _processService: ProcessService,
private _config: Config,
private _httpClient: HttpClient) {
@@ -51,7 +51,7 @@ export class SqlPythonPackageManageProvider extends SqlPackageManageProviderBase
* Returns list of packages
*/
protected async fetchPackages(): Promise<nbExtensionApis.IPackageDetails[]> {
return await this._queryRunner.getPythonPackages(await this.getCurrentConnection());
return await this._service.getPythonPackages(await this.getCurrentConnection());
}
/**
@@ -97,7 +97,7 @@ export class SqlPythonPackageManageProvider extends SqlPackageManageProviderBase
return false;
}
let connection = await this.getCurrentConnection();
if (connection && await this._queryRunner.isPythonInstalled(connection)) {
if (connection && await this._service.isPythonInstalled(connection)) {
return true;
}
return false;

View File

@@ -7,13 +7,13 @@ import * as vscode from 'vscode';
import * as azdata from 'azdata';
import * as nbExtensionApis from '../typings/notebookServices';
import { QueryRunner } from '../common/queryRunner';
import { ApiWrapper } from '../common/apiWrapper';
import { ProcessService } from '../common/processService';
import { Config } from '../configurations/config';
import { SqlPackageManageProviderBase, ScriptMode } from './SqlPackageManageProviderBase';
import { HttpClient } from '../common/httpClient';
import * as constants from '../common/constants';
import { PackageManagementService } from './packageManagementService';
@@ -30,7 +30,7 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
constructor(
private _outputChannel: vscode.OutputChannel,
apiWrapper: ApiWrapper,
private _queryRunner: QueryRunner,
private _service: PackageManagementService,
private _processService: ProcessService,
private _config: Config,
private _httpClient: HttpClient) {
@@ -55,7 +55,7 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
* Returns list of packages
*/
protected async fetchPackages(): Promise<nbExtensionApis.IPackageDetails[]> {
return await this._queryRunner.getRPackages(await this.getCurrentConnection());
return await this._service.getRPackages(await this.getCurrentConnection());
}
/**
@@ -96,7 +96,7 @@ export class SqlRPackageManageProvider extends SqlPackageManageProviderBase impl
return false;
}
let connection = await this.getCurrentConnection();
if (connection && await this._queryRunner.isRInstalled(connection)) {
if (connection && await this._service.isRInstalled(connection)) {
return true;
}
return false;

View File

@@ -8,7 +8,7 @@ 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';
import { PackageManagementService } from '../../packageManagement/packageManagementService';
interface TestContext {
@@ -23,74 +23,64 @@ function createContext(): TestContext {
};
}
describe('Server Config Manager', () => {
describe('Package Management Service', () => {
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);
let serverConfigManager = new PackageManagementService(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);
let serverConfigManager = new PackageManagementService(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);
let serverConfigManager = new PackageManagementService(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();
let serverConfigManager = new PackageManagementService(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();
let serverConfigManager = new PackageManagementService(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();
let serverConfigManager = new PackageManagementService(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> {
it('enableExternalScriptConfig 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.setup(x => x.showQuickPick(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve({
label: 'Yes'
}));
let serverConfigManager = new PackageManagementService(context.apiWrapper.object, context.queryRunner.object);
let connection = new azdata.connection.ConnectionProfile();
await serverConfigManager.enableExternalScriptConfig(connection);
context.apiWrapper.verify(x => x.showErrorMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
});

View File

@@ -22,7 +22,8 @@ describe('Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.queryRunner.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
@@ -33,8 +34,10 @@ describe('Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.queryRunner.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.queryRunner.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.once());
@@ -46,8 +49,10 @@ describe('Package Manager', () => {
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny()));
testContext.queryRunner.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.queryRunner.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isRInstalled(connection)).returns(() => {return Promise.resolve(false);});
testContext.serverConfigManager.setup(x => x.isPythonInstalled(connection)).returns(() => {return Promise.resolve(true);});
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
testContext.apiWrapper.verify(x => x.showInfoMessage(TypeMoq.It.isAny()), TypeMoq.Times.once());
@@ -59,6 +64,7 @@ describe('Package Manager', () => {
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => {return Promise.resolve(connection);});
testContext.apiWrapper.setup(x => x.executeCommand(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {return Promise.resolve();});
testContext.apiWrapper.setup(x => x.showInfoMessage(TypeMoq.It.isAny()));
testContext.serverConfigManager.setup(x => x.enableExternalScriptConfig(connection)).returns(() => {return Promise.resolve(true);});
let packageManager = createPackageManager(testContext);
await packageManager.managePackages();
@@ -204,7 +210,7 @@ describe('Package Manager', () => {
testContext.outputChannel,
'',
testContext.apiWrapper.object,
testContext.queryRunner.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);

View File

@@ -40,7 +40,7 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -72,7 +72,7 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -95,7 +95,7 @@ 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.queryRunner.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -108,7 +108,7 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
testContext.serverConfigManager.setup(x => x.getPythonPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -298,7 +298,7 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
testContext.serverConfigManager.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
@@ -311,7 +311,7 @@ describe('SQL Python Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
testContext.serverConfigManager.setup(x => x.isPythonInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
@@ -390,7 +390,7 @@ describe('SQL Python Package Manager', () => {
return new SqlPythonPackageManageProvider(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.queryRunner.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);

View File

@@ -40,7 +40,7 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -63,7 +63,7 @@ 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.queryRunner.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve(packages));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -76,7 +76,7 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
testContext.serverConfigManager.setup(x => x.getRPackages(TypeMoq.It.isAny())).returns(() => Promise.resolve([]));
let provider = createProvider(testContext);
let actual = await provider.listPackages();
@@ -222,7 +222,7 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
testContext.serverConfigManager.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(false));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
@@ -235,7 +235,7 @@ describe('SQL R Package Manager', () => {
let connection = new azdata.connection.ConnectionProfile();
testContext.apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
testContext.queryRunner.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
testContext.serverConfigManager.setup(x => x.isRInstalled(TypeMoq.It.isAny())).returns(() => Promise.resolve(true));
let provider = createProvider(testContext);
let actual = await provider.canUseProvider();
@@ -316,7 +316,7 @@ describe('SQL R Package Manager', () => {
return new SqlRPackageManageProvider(
testContext.outputChannel,
testContext.apiWrapper.object,
testContext.queryRunner.object,
testContext.serverConfigManager.object,
testContext.processService.object,
testContext.config.object,
testContext.httpClient.object);

View File

@@ -11,6 +11,7 @@ import { QueryRunner } from '../../common/queryRunner';
import { ProcessService } from '../../common/processService';
import { Config } from '../../configurations/config';
import { HttpClient } from '../../common/httpClient';
import { PackageManagementService } from '../../packageManagement/packageManagementService';
export interface TestContext {
@@ -22,6 +23,7 @@ export interface TestContext {
op: azdata.BackgroundOperation;
getOpStatus: () => azdata.TaskStatus;
httpClient: TypeMoq.IMock<HttpClient>;
serverConfigManager: TypeMoq.IMock<PackageManagementService>;
}
export function createContext(): TestContext {
@@ -49,6 +51,7 @@ export function createContext(): TestContext {
id: '',
onCanceled: new vscode.EventEmitter<void>().event,
},
getOpStatus: () => { return opStatus; }
getOpStatus: () => { return opStatus; },
serverConfigManager: TypeMoq.Mock.ofType(PackageManagementService)
};
}

View File

@@ -31,12 +31,12 @@ export class RegisteredModelsDialog extends ModelViewBase {
this.currentLanguagesTab = new CurrentModelsPage(this._apiWrapper, this);
let registerModelButton = this._apiWrapper.createButton(constants.registerModelTitle);
let registerModelButton = this._apiWrapper.createButton(constants.deployModelTitle);
registerModelButton.onClick(async () => {
await this.sendDataRequest(RegisterModelEventName);
});
let dialog = this.dialogView.createDialog('', [this.currentLanguagesTab]);
let dialog = this.dialogView.createDialog(constants.registerModelTitle, [this.currentLanguagesTab]);
dialog.customButtons = [registerModelButton];
this.mainViewPanel = dialog;
dialog.okButton.hidden = true;

View File

@@ -1,160 +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 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;
}
}

View File

@@ -1,69 +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 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]);
}
}