mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Machine Learning Services Extension - External Languages (#9043)
* Added external language list, edit and delete UIs to Machine Learning extension
This commit is contained in:
@@ -73,4 +73,16 @@ export class ApiWrapper {
|
||||
public getConfiguration(section?: string, resource?: vscode.Uri | null): vscode.WorkspaceConfiguration {
|
||||
return vscode.workspace.getConfiguration(section, resource);
|
||||
}
|
||||
|
||||
public createTab(title: string): azdata.window.DialogTab {
|
||||
return azdata.window.createTab(title);
|
||||
}
|
||||
|
||||
public createModelViewDialog(title: string, dialogName?: string, isWide?: boolean): azdata.window.Dialog {
|
||||
return azdata.window.createModelViewDialog(title, dialogName, isWide);
|
||||
}
|
||||
|
||||
public openDialog(dialog: azdata.window.Dialog): void {
|
||||
return azdata.window.openDialog(dialog);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export const notebookExtensionName = 'Microsoft.notebook';
|
||||
|
||||
// Tasks, commands
|
||||
//
|
||||
export const mlManageLanguagesCommand = 'mls.command.manageLanguages';
|
||||
export const mlManagePackagesCommand = 'mls.command.managePackages';
|
||||
export const mlOdbcDriverCommand = 'mls.command.odbcdriver';
|
||||
export const mlsDocumentsCommand = 'mls.command.mlsdocs';
|
||||
@@ -44,6 +45,7 @@ export const installDependenciesPackagesAlreadyInstalled = localize('mls.install
|
||||
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 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");
|
||||
@@ -62,12 +64,36 @@ export const rConfigError = localize('mls.rConfigError', "R executable is not co
|
||||
export const installingDependencies = localize('mls.installingDependencies', "Installing dependencies ...");
|
||||
export const resourceNotFoundError = localize('mls.resourceNotFound', "Could not find the specified resource");
|
||||
export const latestVersion = localize('mls.latestVersion', "Latest");
|
||||
export const localhost = 'localhost';
|
||||
export function httpGetRequestError(code: number, message: string): string {
|
||||
return localize('mls.httpGetRequestError', "Package info request failed with error: {0} {1}",
|
||||
code,
|
||||
message);
|
||||
}
|
||||
|
||||
export function getErrorMessage(error: Error): string { return localize('azure.resource.error', "Error: {0}", error?.message); }
|
||||
export const extLangInstallTabTitle = localize('extLang.installTabTitle', "Installed");
|
||||
export const extLangLanguageCreatedDate = localize('extLang.languageCreatedDate', "Installed");
|
||||
export const extLangLanguagePlatform = localize('extLang.languagePlatform', "Platform");
|
||||
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 extLangOkButtonText = localize('extLang.OkButtonText', "OK");
|
||||
export const extLangSaveButtonText = localize('extLang.SaveButtonText', "Save");
|
||||
export const extLangLanguageName = localize('extLang.languageName', "Name");
|
||||
export const extLangNewLanguageTabTitle = localize('extLang.newLanguageTabTitle', "Add new");
|
||||
export const extLangFileBrowserTabTitle = localize('extLang.fileBrowserTabTitle', "File Browser");
|
||||
export const extLangDialogTitle = localize('extLang.DialogTitle', "Languages");
|
||||
export const extLangTarget = localize('extLang.Target', "Target");
|
||||
export const extLangLocal = localize('extLang.Local', "localhost");
|
||||
export const extLangExtensionFilePath = localize('extLang.extensionFilePath', "Language extension path");
|
||||
export const extLangExtensionFileLocation = localize('extLang.extensionFileLocation', "Language extension location");
|
||||
export const extLangExtensionFileName = localize('extLang.extensionFileName', "Extension file Name");
|
||||
export const extLangEnvVariables = localize('extLang.envVariables', "Environment variables");
|
||||
export const extLangParameters = localize('extLang.parameters', "Parameters");
|
||||
export const extLangSelectedPath = localize('extLang.selectedPath', "Selected Path");
|
||||
export const extLangInstallFailedError = localize('extLang.installFailedError', "Failed to install language");
|
||||
export const extLangUpdateFailedError = localize('extLang.updateFailedError', "Failed to update language");
|
||||
|
||||
// Links
|
||||
//
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import * as nbExtensionApis from '../typings/notebookServices';
|
||||
import * as mssql from '../../../mssql';
|
||||
import { PackageManager } from '../packageManagement/packageManager';
|
||||
import * as constants from '../common/constants';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
@@ -15,6 +16,8 @@ import { Config } from '../configurations/config';
|
||||
import { ServerConfigWidget } from '../widgets/serverConfigWidgets';
|
||||
import { ServerConfigManager } from '../serverConfig/serverConfigManager';
|
||||
import { HttpClient } from '../common/httpClient';
|
||||
import { LanguageController } from '../externalLanguage/languageController';
|
||||
import { LanguageService } from '../externalLanguage/languageService';
|
||||
|
||||
/**
|
||||
* The main controller class that initializes the extension
|
||||
@@ -65,6 +68,18 @@ export default class MainController implements vscode.Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of Server Installation from notebook extension
|
||||
*/
|
||||
private async getLanguageExtensionService(): Promise<mssql.ILanguageExtensionService> {
|
||||
let mssqlExtension = this._apiWrapper.getExtension(mssql.extension.name)?.exports as mssql.IExtension;
|
||||
if (mssqlExtension) {
|
||||
return (mssqlExtension.languageExtension);
|
||||
} else {
|
||||
throw new Error(constants.mssqlExtensionNotLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
private async initialize(): Promise<void> {
|
||||
|
||||
this._outputChannel.show(true);
|
||||
@@ -78,12 +93,23 @@ export default class MainController implements vscode.Disposable {
|
||||
this._apiWrapper.registerCommand(constants.mlManagePackagesCommand, (async () => {
|
||||
await packageManager.managePackages();
|
||||
}));
|
||||
|
||||
let mssqlService = await this.getLanguageExtensionService();
|
||||
let languagesModel = new LanguageService(this._apiWrapper, mssqlService);
|
||||
let languageController = new LanguageController(this._apiWrapper, this._rootPath, languagesModel);
|
||||
|
||||
this._apiWrapper.registerCommand(constants.mlManageLanguagesCommand, (async () => {
|
||||
await languageController.manageLanguages();
|
||||
}));
|
||||
this._apiWrapper.registerCommand(constants.mlsDependenciesCommand, (async () => {
|
||||
await packageManager.installDependencies();
|
||||
}));
|
||||
this._apiWrapper.registerTaskHandler(constants.mlManagePackagesCommand, async () => {
|
||||
await packageManager.managePackages();
|
||||
});
|
||||
this._apiWrapper.registerTaskHandler(constants.mlManageLanguagesCommand, async () => {
|
||||
await languageController.manageLanguages();
|
||||
});
|
||||
this._apiWrapper.registerTaskHandler(constants.mlOdbcDriverCommand, async () => {
|
||||
await this.serverConfigManager.openOdbcDriverDocuments();
|
||||
});
|
||||
@@ -126,7 +152,6 @@ export default class MainController implements vscode.Disposable {
|
||||
return this._httpClient;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Config instance
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as mssql from '../../../mssql/src/mssql';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
import { LanguageService } from './languageService';
|
||||
import { LanguagesDialog } from '../views/externalLanguages/languagesDialog';
|
||||
import { LanguageEditDialog } from '../views/externalLanguages/languageEditDialog';
|
||||
import { FileBrowserDialog } from '../views/externalLanguages/fileBrowserDialog';
|
||||
import { LanguageViewBase, LanguageUpdateModel } from '../views/externalLanguages/languageViewBase';
|
||||
import * as constants from '../common/constants';
|
||||
|
||||
export class LanguageController {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _root: string,
|
||||
private _service: LanguageService) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the manage language dialog and connects events to the model
|
||||
*/
|
||||
public async manageLanguages(): Promise<LanguagesDialog> {
|
||||
|
||||
let dialog = new LanguagesDialog(this._apiWrapper, this._root);
|
||||
|
||||
// Load current connection
|
||||
//
|
||||
await this._service.load();
|
||||
dialog.connection = this._service.connection;
|
||||
dialog.connectionUrl = this._service.connectionUrl;
|
||||
|
||||
// Handle dialog events and connect to model
|
||||
//
|
||||
dialog.onEdit(model => {
|
||||
this.editLanguage(dialog, model);
|
||||
});
|
||||
dialog.onDelete(async deleteModel => {
|
||||
try {
|
||||
await this.executeAction(dialog, this.deleteLanguage, this._service, deleteModel);
|
||||
dialog.onUpdatedLanguage(deleteModel);
|
||||
} catch (err) {
|
||||
dialog.onActionFailed(err);
|
||||
}
|
||||
});
|
||||
|
||||
dialog.onUpdate(async updateModel => {
|
||||
try {
|
||||
await this.executeAction(dialog, this.updateLanguage, this._service, updateModel);
|
||||
dialog.onUpdatedLanguage(updateModel);
|
||||
} catch (err) {
|
||||
dialog.onActionFailed(err);
|
||||
}
|
||||
});
|
||||
|
||||
dialog.onList(async () => {
|
||||
try {
|
||||
let result = await this.listLanguages(this._service);
|
||||
dialog.onListLanguageLoaded(result);
|
||||
} catch (err) {
|
||||
dialog.onActionFailed(err);
|
||||
}
|
||||
});
|
||||
this.onSelectFile(dialog);
|
||||
|
||||
// Open dialog
|
||||
//
|
||||
dialog.showDialog();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public async executeAction<T>(dialog: LanguageViewBase, func: (...args: any[]) => Promise<T>, ...args: any[]): Promise<T> {
|
||||
let result = await func(...args);
|
||||
await dialog.reset();
|
||||
return result;
|
||||
}
|
||||
|
||||
public editLanguage(parent: LanguageViewBase, languageUpdateModel: LanguageUpdateModel): void {
|
||||
let editDialog = new LanguageEditDialog(this._apiWrapper, parent, languageUpdateModel);
|
||||
editDialog.showDialog();
|
||||
}
|
||||
|
||||
private onSelectFile(dialog: LanguageViewBase): void {
|
||||
dialog.fileBrowser(async (args) => {
|
||||
let filePath = '';
|
||||
if (args.target === constants.localhost) {
|
||||
filePath = await this.getLocalFilePath();
|
||||
|
||||
} else {
|
||||
filePath = await this.getServerFilePath(args.target);
|
||||
}
|
||||
dialog.onFilePathSelected({ filePath: filePath, target: args.target });
|
||||
});
|
||||
}
|
||||
|
||||
public getServerFilePath(connectionUrl: string): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
let dialog = new FileBrowserDialog(this._apiWrapper, connectionUrl);
|
||||
dialog.onPathSelected((selectedPath) => {
|
||||
resolve(selectedPath);
|
||||
});
|
||||
|
||||
dialog.showDialog();
|
||||
});
|
||||
}
|
||||
|
||||
public async getLocalFilePath(): Promise<string> {
|
||||
let result = await this._apiWrapper.showOpenDialog({
|
||||
canSelectFiles: true,
|
||||
canSelectFolders: false,
|
||||
canSelectMany: false
|
||||
});
|
||||
return result && result.length > 0 ? result[0].fsPath : '';
|
||||
}
|
||||
|
||||
public async deleteLanguage(model: LanguageService, deleteModel: LanguageUpdateModel): Promise<void> {
|
||||
await model.deleteLanguage(deleteModel.language.name);
|
||||
}
|
||||
|
||||
public async listLanguages(model: LanguageService): Promise<mssql.ExternalLanguage[]> {
|
||||
return await model.getLanguageList();
|
||||
}
|
||||
|
||||
public async updateLanguage(model: LanguageService, updateModel: LanguageUpdateModel): Promise<void> {
|
||||
if (!updateModel.language) {
|
||||
return;
|
||||
}
|
||||
let contents: mssql.ExternalLanguageContent[] = [];
|
||||
if (updateModel.language.contents && updateModel.language.contents.length >= 0) {
|
||||
contents = updateModel.language.contents.filter(x => x.platform !== updateModel.content.platform);
|
||||
}
|
||||
contents.push(updateModel.content);
|
||||
|
||||
updateModel.language.contents = contents;
|
||||
await model.updateLanguage(updateModel.language);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from '../../../mssql/src/mssql';
|
||||
import { ApiWrapper } from '../common/apiWrapper';
|
||||
|
||||
/**
|
||||
* Manage package dialog model
|
||||
*/
|
||||
export class LanguageService {
|
||||
|
||||
public connection: azdata.connection.ConnectionProfile | undefined;
|
||||
public connectionUrl: string = '';
|
||||
|
||||
constructor(
|
||||
private _apiWrapper: ApiWrapper,
|
||||
private _languageExtensionService: mssql.ILanguageExtensionService) {
|
||||
}
|
||||
|
||||
public async load() {
|
||||
this.connection = await this.getCurrentConnection();
|
||||
this.connectionUrl = await this.getCurrentConnectionUrl();
|
||||
}
|
||||
|
||||
public async getLanguageList(): Promise<mssql.ExternalLanguage[]> {
|
||||
if (this.connectionUrl) {
|
||||
return await this._languageExtensionService.listLanguages(this.connectionUrl);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public async deleteLanguage(languageName: string): Promise<void> {
|
||||
if (this.connectionUrl) {
|
||||
await this._languageExtensionService.deleteLanguage(this.connectionUrl, languageName);
|
||||
}
|
||||
}
|
||||
|
||||
public async updateLanguage(language: mssql.ExternalLanguage): Promise<void> {
|
||||
if (this.connectionUrl) {
|
||||
await this._languageExtensionService.updateLanguage(this.connectionUrl, language);
|
||||
}
|
||||
}
|
||||
|
||||
private async getCurrentConnectionUrl(): Promise<string> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection) {
|
||||
return await this._apiWrapper.getUriForConnection(connection.connectionId);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private async getCurrentConnection(): Promise<azdata.connection.ConnectionProfile> {
|
||||
return await this._apiWrapper.getCurrentConnection();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import { createContext, ParentDialog } from './utils';
|
||||
import { AddEditLanguageTab } from '../../../views/externalLanguages/addEditLanguageTab';
|
||||
import { LanguageUpdateModel } from '../../../views/externalLanguages/languageViewBase';
|
||||
|
||||
describe('Add Edit External Languages Tab', () => {
|
||||
it('Should create AddEditLanguageTab for new language successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: parent.createNewContent(),
|
||||
language: parent.createNewLanguage(),
|
||||
newLang: true
|
||||
};
|
||||
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
should.notEqual(tab.languageView, undefined, 'Failed to create language view for add');
|
||||
});
|
||||
|
||||
it('Should create AddEditLanguageTab for edit successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: {
|
||||
extensionFileName: 'filename',
|
||||
isLocalFile: true,
|
||||
pathToExtension: 'path',
|
||||
},
|
||||
language: {
|
||||
name: 'name',
|
||||
contents: []
|
||||
},
|
||||
newLang: false
|
||||
};
|
||||
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
should.notEqual(tab.languageView, undefined, 'Failed to create language view for edit');
|
||||
should.equal(tab.saveButton, undefined);
|
||||
});
|
||||
|
||||
it('Should reset AddEditLanguageTab successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: {
|
||||
extensionFileName: 'filename',
|
||||
isLocalFile: true,
|
||||
pathToExtension: 'path',
|
||||
},
|
||||
language: {
|
||||
name: 'name',
|
||||
contents: []
|
||||
},
|
||||
newLang: false
|
||||
};
|
||||
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
if (tab.languageName) {
|
||||
tab.languageName.value = 'some value';
|
||||
}
|
||||
await tab.reset();
|
||||
should.equal(tab.languageName?.value, 'name');
|
||||
});
|
||||
|
||||
it('Should load content successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: {
|
||||
extensionFileName: 'filename',
|
||||
isLocalFile: true,
|
||||
pathToExtension: 'path',
|
||||
environmentVariables: 'env vars',
|
||||
parameters: 'params'
|
||||
},
|
||||
language: {
|
||||
name: 'name',
|
||||
contents: []
|
||||
},
|
||||
newLang: false
|
||||
};
|
||||
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
let content = tab.languageView?.updatedContent;
|
||||
should.notEqual(content, undefined);
|
||||
if (content) {
|
||||
should.equal(content.extensionFileName, languageUpdateModel.content.extensionFileName);
|
||||
should.equal(content.pathToExtension, languageUpdateModel.content.pathToExtension);
|
||||
should.equal(content.environmentVariables, languageUpdateModel.content.environmentVariables);
|
||||
should.equal(content.parameters, languageUpdateModel.content.parameters);
|
||||
}
|
||||
});
|
||||
|
||||
it('Should raise save event if save button clicked ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: parent.createNewContent(),
|
||||
language: parent.createNewLanguage(),
|
||||
newLang: true
|
||||
};
|
||||
let tab = new AddEditLanguageTab(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
should.notEqual(tab.saveButton, undefined);
|
||||
let updateCalled = false;
|
||||
let promise = new Promise(resolve => {
|
||||
parent.onUpdate(() => {
|
||||
updateCalled = true;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
testContext.onClick.fire();
|
||||
parent.onUpdatedLanguage(languageUpdateModel);
|
||||
await promise;
|
||||
should.equal(updateCalled, true);
|
||||
should.notEqual(tab.updatedData, undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,104 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { createContext } from './utils';
|
||||
import { LanguageController } from '../../../externalLanguage/languageController';
|
||||
import * as mssql from '../../../../../mssql/src/mssql';
|
||||
|
||||
describe('External Languages Controller', () => {
|
||||
it('Should open dialog for manage languages successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
|
||||
let dialog = await controller.manageLanguages();
|
||||
testContext.apiWrapper.verify(x => x.openDialog(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
should.notEqual(dialog, undefined);
|
||||
});
|
||||
|
||||
it('Should list languages successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let languages: mssql.ExternalLanguage[] = [{
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
}];
|
||||
|
||||
testContext.dialogModel.setup( x=> x.getLanguageList()).returns(() => Promise.resolve(languages));
|
||||
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
|
||||
let dialog = await controller.manageLanguages();
|
||||
let actual = await dialog.listLanguages();
|
||||
should.deepEqual(actual, languages);
|
||||
});
|
||||
|
||||
it('Should update languages successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let language: mssql.ExternalLanguage = {
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
};
|
||||
|
||||
testContext.dialogModel.setup( x=> x.updateLanguage(language)).returns(() => Promise.resolve());
|
||||
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
|
||||
let dialog = await controller.manageLanguages();
|
||||
await dialog.updateLanguage({
|
||||
language: language,
|
||||
content: language.contents[0],
|
||||
newLang: false
|
||||
});
|
||||
testContext.dialogModel.verify(x => x.updateLanguage(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
it('Should delete language successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let language: mssql.ExternalLanguage = {
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
};
|
||||
|
||||
testContext.dialogModel.setup( x=> x.deleteLanguage(language.name)).returns(() => Promise.resolve());
|
||||
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
|
||||
let dialog = await controller.manageLanguages();
|
||||
await dialog.deleteLanguage({
|
||||
language: language,
|
||||
content: language.contents[0],
|
||||
newLang: false
|
||||
});
|
||||
testContext.dialogModel.verify(x => x.deleteLanguage(TypeMoq.It.isAny()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
it('Should open edit dialog for edit language', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let language: mssql.ExternalLanguage = {
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
};
|
||||
let controller = new LanguageController(testContext.apiWrapper.object, '', testContext.dialogModel.object);
|
||||
let dialog = await controller.manageLanguages();
|
||||
dialog.onEditLanguage({
|
||||
language: language,
|
||||
content: language.contents[0],
|
||||
newLang: false
|
||||
});
|
||||
testContext.apiWrapper.verify(x => x.openDialog(TypeMoq.It.isAny()), TypeMoq.Times.exactly(2));
|
||||
should.notEqual(dialog, undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import { createContext, ParentDialog } from './utils';
|
||||
import { LanguageEditDialog } from '../../../views/externalLanguages/languageEditDialog';
|
||||
import { LanguageUpdateModel } from '../../../views/externalLanguages/languageViewBase';
|
||||
|
||||
describe('Edit External Languages Dialog', () => {
|
||||
it('Should open dialog successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: parent.createNewContent(),
|
||||
language: parent.createNewLanguage(),
|
||||
newLang: true
|
||||
};
|
||||
let dialog = new LanguageEditDialog(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
dialog.showDialog();
|
||||
should.notEqual(dialog.addNewLanguageTab, undefined);
|
||||
});
|
||||
|
||||
it('Should raise save event if save button clicked ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let parent = new ParentDialog(testContext.apiWrapper.object);
|
||||
let languageUpdateModel: LanguageUpdateModel = {
|
||||
content: parent.createNewContent(),
|
||||
language: parent.createNewLanguage(),
|
||||
newLang: true
|
||||
};
|
||||
let dialog = new LanguageEditDialog(testContext.apiWrapper.object, parent, languageUpdateModel);
|
||||
dialog.showDialog();
|
||||
|
||||
let updateCalled = false;
|
||||
let promise = new Promise(resolve => {
|
||||
parent.onUpdate(() => {
|
||||
updateCalled = true;
|
||||
parent.onUpdatedLanguage(languageUpdateModel);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
dialog.onSave();
|
||||
await promise;
|
||||
should.equal(updateCalled, true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import { createContext } from './utils';
|
||||
import { LanguagesDialog } from '../../../views/externalLanguages/languagesDialog';
|
||||
|
||||
describe('External Languages Dialog', () => {
|
||||
it('Should open dialog successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let dialog = new LanguagesDialog(testContext.apiWrapper.object, '');
|
||||
dialog.showDialog();
|
||||
should.notEqual(dialog.addNewLanguageTab, undefined);
|
||||
should.notEqual(dialog.currentLanguagesTab, undefined);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as should from 'should';
|
||||
import 'mocha';
|
||||
import { createContext } from './utils';
|
||||
import * as mssql from '../../../../../mssql/src/mssql';
|
||||
import { LanguageService } from '../../../externalLanguage/languageService';
|
||||
|
||||
describe('External Languages Dialog Model', () => {
|
||||
it('Should list languages successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let languages: mssql.ExternalLanguage[] = [{
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
}];
|
||||
testContext.languageExtensionService.listLanguages = () => {return Promise.resolve(languages);};
|
||||
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
|
||||
await model.load();
|
||||
let actual = await model.getLanguageList();
|
||||
should.deepEqual(actual, languages);
|
||||
});
|
||||
|
||||
it('Should update language successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let language: mssql.ExternalLanguage = {
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
};
|
||||
|
||||
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
|
||||
await model.load();
|
||||
await should(model.updateLanguage(language)).resolved();
|
||||
});
|
||||
|
||||
it('Should delete language successfully ', async function (): Promise<void> {
|
||||
let testContext = createContext();
|
||||
let language: mssql.ExternalLanguage = {
|
||||
name: '',
|
||||
contents: [{
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
}]
|
||||
};
|
||||
|
||||
let model = new LanguageService(testContext.apiWrapper.object, testContext.languageExtensionService);
|
||||
await model.load();
|
||||
await should(model.deleteLanguage(language.name)).resolved();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,236 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import { ApiWrapper } from '../../../common/apiWrapper';
|
||||
import { LanguageViewBase } from '../../../views/externalLanguages/languageViewBase';
|
||||
import * as mssql from '../../../../../mssql/src/mssql';
|
||||
import { LanguageService } from '../../../externalLanguage/languageService';
|
||||
|
||||
export interface TestContext {
|
||||
apiWrapper: TypeMoq.IMock<ApiWrapper>;
|
||||
view: azdata.ModelView;
|
||||
languageExtensionService: mssql.ILanguageExtensionService;
|
||||
onClick: vscode.EventEmitter<any>;
|
||||
dialogModel: TypeMoq.IMock<LanguageService>;
|
||||
}
|
||||
|
||||
export class ParentDialog extends LanguageViewBase {
|
||||
public reset(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
constructor(
|
||||
apiWrapper: ApiWrapper) {
|
||||
super(apiWrapper, '');
|
||||
}
|
||||
}
|
||||
|
||||
export function createContext(): TestContext {
|
||||
let onClick: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
|
||||
|
||||
let apiWrapper = TypeMoq.Mock.ofType(ApiWrapper);
|
||||
let componentBase: azdata.Component = {
|
||||
id: '',
|
||||
updateProperties: () => Promise.resolve(),
|
||||
updateProperty: () => Promise.resolve(),
|
||||
updateCssStyles: undefined!,
|
||||
onValidityChanged: undefined!,
|
||||
valid: true,
|
||||
validate: undefined!,
|
||||
focus: undefined!
|
||||
};
|
||||
let button: azdata.ButtonComponent = Object.assign({}, componentBase, {
|
||||
onDidClick: onClick.event
|
||||
});
|
||||
let radioButton: azdata.RadioButtonComponent = Object.assign({}, componentBase, {
|
||||
onDidClick: onClick.event
|
||||
});
|
||||
let container = {
|
||||
clearItems: () => { },
|
||||
addItems: () => { },
|
||||
addItem: () => { },
|
||||
removeItem: () => true,
|
||||
insertItem: () => { },
|
||||
items: [],
|
||||
setLayout: () => { }
|
||||
};
|
||||
let form: azdata.FormContainer = Object.assign({}, componentBase, container, {
|
||||
});
|
||||
let flex: azdata.FlexContainer = Object.assign({}, componentBase, container, {
|
||||
});
|
||||
|
||||
let buttonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = {
|
||||
component: () => button,
|
||||
withProperties: () => buttonBuilder,
|
||||
withValidation: () => buttonBuilder
|
||||
};
|
||||
let radioButtonBuilder: azdata.ComponentBuilder<azdata.ButtonComponent> = {
|
||||
component: () => radioButton,
|
||||
withProperties: () => radioButtonBuilder,
|
||||
withValidation: () => radioButtonBuilder
|
||||
};
|
||||
let inputBox: () => azdata.InputBoxComponent = () => Object.assign({}, componentBase, {
|
||||
onTextChanged: undefined!,
|
||||
onEnterKeyPressed: undefined!,
|
||||
value: ''
|
||||
});
|
||||
let declarativeTable: () => azdata.DeclarativeTableComponent = () => Object.assign({}, componentBase, {
|
||||
onDataChanged: undefined!,
|
||||
data: [],
|
||||
columns: []
|
||||
});
|
||||
|
||||
let loadingComponent: () => azdata.LoadingComponent = () => Object.assign({}, componentBase, {
|
||||
loading: false,
|
||||
component: undefined!
|
||||
});
|
||||
|
||||
let declarativeTableBuilder: azdata.ComponentBuilder<azdata.DeclarativeTableComponent> = {
|
||||
component: () => declarativeTable(),
|
||||
withProperties: () => declarativeTableBuilder,
|
||||
withValidation: () => declarativeTableBuilder
|
||||
};
|
||||
|
||||
let loadingBuilder: azdata.LoadingComponentBuilder = {
|
||||
component: () => loadingComponent(),
|
||||
withProperties: () => loadingBuilder,
|
||||
withValidation: () => loadingBuilder,
|
||||
withItem: () => loadingBuilder
|
||||
};
|
||||
|
||||
let formBuilder: azdata.FormBuilder = Object.assign({}, {
|
||||
component: () => form,
|
||||
addFormItem: () => { },
|
||||
insertFormItem: () => { },
|
||||
removeFormItem: () => true,
|
||||
addFormItems: () => { },
|
||||
withFormItems: () => formBuilder,
|
||||
withProperties: () => formBuilder,
|
||||
withValidation: () => formBuilder,
|
||||
withItems: () => formBuilder,
|
||||
withLayout: () => formBuilder
|
||||
});
|
||||
|
||||
let flexBuilder: azdata.FlexBuilder = Object.assign({}, {
|
||||
component: () => flex,
|
||||
withProperties: () => flexBuilder,
|
||||
withValidation: () => flexBuilder,
|
||||
withItems: () => flexBuilder,
|
||||
withLayout: () => flexBuilder
|
||||
});
|
||||
|
||||
let inputBoxBuilder: azdata.ComponentBuilder<azdata.InputBoxComponent> = {
|
||||
component: () => {
|
||||
let r = inputBox();
|
||||
return r;
|
||||
},
|
||||
withProperties: () => inputBoxBuilder,
|
||||
withValidation: () => inputBoxBuilder
|
||||
};
|
||||
|
||||
let view: azdata.ModelView = {
|
||||
onClosed: undefined!,
|
||||
connection: undefined!,
|
||||
serverInfo: undefined!,
|
||||
valid: true,
|
||||
onValidityChanged: undefined!,
|
||||
validate: undefined!,
|
||||
initializeModel: () => { return Promise.resolve(); },
|
||||
modelBuilder: {
|
||||
radioCardGroup: undefined!,
|
||||
navContainer: undefined!,
|
||||
divContainer: undefined!,
|
||||
flexContainer: () => flexBuilder,
|
||||
splitViewContainer: undefined!,
|
||||
dom: undefined!,
|
||||
card: undefined!,
|
||||
inputBox: () => inputBoxBuilder,
|
||||
checkBox: undefined!,
|
||||
radioButton: () => radioButtonBuilder,
|
||||
webView: undefined!,
|
||||
editor: undefined!,
|
||||
diffeditor: undefined!,
|
||||
text: () => inputBoxBuilder,
|
||||
image: undefined!,
|
||||
button: () => buttonBuilder,
|
||||
dropDown: undefined!,
|
||||
tree: undefined!,
|
||||
listBox: undefined!,
|
||||
table: undefined!,
|
||||
declarativeTable: () => declarativeTableBuilder,
|
||||
dashboardWidget: undefined!,
|
||||
dashboardWebview: undefined!,
|
||||
formContainer: () => formBuilder,
|
||||
groupContainer: undefined!,
|
||||
toolbarContainer: undefined!,
|
||||
loadingComponent: () => loadingBuilder,
|
||||
fileBrowserTree: undefined!,
|
||||
hyperlink: undefined!
|
||||
}
|
||||
};
|
||||
let tab: azdata.window.DialogTab = {
|
||||
title: '',
|
||||
content: '',
|
||||
registerContent: async (handler) => {
|
||||
try {
|
||||
await handler(view);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
},
|
||||
onValidityChanged: undefined!,
|
||||
valid: true,
|
||||
modelView: undefined!
|
||||
};
|
||||
|
||||
let dialogButton: azdata.window.Button = {
|
||||
label: '',
|
||||
enabled: true,
|
||||
hidden: false,
|
||||
onClick: onClick.event,
|
||||
|
||||
};
|
||||
let dialogMessage: azdata.window.DialogMessage = {
|
||||
text: '',
|
||||
};
|
||||
let dialog: azdata.window.Dialog = {
|
||||
title: '',
|
||||
isWide: false,
|
||||
content: [],
|
||||
okButton: dialogButton,
|
||||
cancelButton: dialogButton,
|
||||
customButtons: [],
|
||||
message: dialogMessage,
|
||||
registerCloseValidator: () => { },
|
||||
registerOperation: () => { },
|
||||
onValidityChanged: new vscode.EventEmitter<boolean>().event,
|
||||
registerContent: () => { },
|
||||
modelView: undefined!,
|
||||
valid: true
|
||||
};
|
||||
apiWrapper.setup(x => x.createTab(TypeMoq.It.isAny())).returns(() => tab);
|
||||
apiWrapper.setup(x => x.createModelViewDialog(TypeMoq.It.isAny())).returns(() => dialog);
|
||||
apiWrapper.setup(x => x.openDialog(TypeMoq.It.isAny())).returns(() => { });
|
||||
let connection = new azdata.connection.ConnectionProfile();
|
||||
apiWrapper.setup(x => x.getCurrentConnection()).returns(() => { return Promise.resolve(connection); });
|
||||
apiWrapper.setup(x => x.getUriForConnection(TypeMoq.It.isAny())).returns(() => { return Promise.resolve('connectionUrl'); });
|
||||
|
||||
let languageExtensionService: mssql.ILanguageExtensionService = {
|
||||
listLanguages: () => { return Promise.resolve([]); },
|
||||
deleteLanguage: () => { return Promise.resolve(); },
|
||||
updateLanguage: () => { return Promise.resolve(); }
|
||||
};
|
||||
|
||||
|
||||
return {
|
||||
apiWrapper: apiWrapper,
|
||||
view: view,
|
||||
languageExtensionService: languageExtensionService,
|
||||
onClick: onClick,
|
||||
dialogModel: TypeMoq.Mock.ofType(LanguageService)
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as constants from '../../common/constants';
|
||||
import { LanguageViewBase, LanguageUpdateModel } from './languageViewBase';
|
||||
import { LanguageContentView } from './languageContentView';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class AddEditLanguageTab extends LanguageViewBase {
|
||||
private _dialogTab: azdata.window.DialogTab;
|
||||
public languageName: azdata.TextComponent | undefined;
|
||||
private _editMode: boolean = false;
|
||||
public saveButton: azdata.ButtonComponent | undefined;
|
||||
public languageView: LanguageContentView | undefined;
|
||||
|
||||
constructor(
|
||||
apiWrapper: ApiWrapper,
|
||||
parent: LanguageViewBase,
|
||||
private _languageUpdateModel: LanguageUpdateModel) {
|
||||
super(apiWrapper, parent.root, parent);
|
||||
this._editMode = !this._languageUpdateModel.newLang;
|
||||
this._dialogTab = apiWrapper.createTab(constants.extLangNewLanguageTabTitle);
|
||||
this._dialogTab.registerContent(async view => {
|
||||
let language = this._languageUpdateModel.language;
|
||||
let content = this._languageUpdateModel.content;
|
||||
this.languageName = view.modelBuilder.inputBox().withProperties({
|
||||
value: language.name,
|
||||
width: '150px',
|
||||
enabled: !this._editMode
|
||||
}).withValidation(component => component.value !== '').component();
|
||||
|
||||
let formBuilder = view.modelBuilder.formContainer();
|
||||
formBuilder.addFormItem({
|
||||
component: this.languageName,
|
||||
title: constants.extLangLanguageName,
|
||||
required: true
|
||||
});
|
||||
|
||||
this.languageView = new LanguageContentView(this._apiWrapper, this, view.modelBuilder, formBuilder, content);
|
||||
|
||||
if (!this._editMode) {
|
||||
this.saveButton = view.modelBuilder.button().withProperties({
|
||||
label: constants.extLangInstallButtonText,
|
||||
width: '100px'
|
||||
}).component();
|
||||
this.saveButton.onDidClick(async () => {
|
||||
try {
|
||||
await this.updateLanguage(this.updatedData);
|
||||
} catch (err) {
|
||||
this.showErrorMessage(constants.extLangInstallFailedError, err);
|
||||
}
|
||||
});
|
||||
|
||||
formBuilder.addFormItem({
|
||||
component: this.saveButton,
|
||||
title: ''
|
||||
});
|
||||
}
|
||||
|
||||
await view.initializeModel(formBuilder.component());
|
||||
await this.reset();
|
||||
});
|
||||
}
|
||||
|
||||
public get updatedData(): LanguageUpdateModel {
|
||||
return {
|
||||
language: {
|
||||
name: this.languageName?.value || '',
|
||||
contents: this._languageUpdateModel.language.contents
|
||||
},
|
||||
content: this.languageView?.updatedContent || this._languageUpdateModel.content,
|
||||
newLang: this._languageUpdateModel.newLang
|
||||
};
|
||||
}
|
||||
|
||||
public get tab(): azdata.window.DialogTab {
|
||||
return this._dialogTab;
|
||||
}
|
||||
|
||||
public async reset(): Promise<void> {
|
||||
if (this.languageName) {
|
||||
this.languageName.value = this._languageUpdateModel.language.name;
|
||||
}
|
||||
this.languageView?.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
|
||||
import * as constants from '../../common/constants';
|
||||
import { LanguageViewBase } from './languageViewBase';
|
||||
import { LanguagesTable } from './languagesTable';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class CurrentLanguagesTab extends LanguageViewBase {
|
||||
|
||||
private _installedLangsTab: azdata.window.DialogTab;
|
||||
|
||||
private _locationComponent: azdata.TextComponent | undefined;
|
||||
private _installLanguagesTable: azdata.DeclarativeTableComponent | undefined;
|
||||
private _languageTable: LanguagesTable | undefined;
|
||||
private _loader: azdata.LoadingComponent | undefined;
|
||||
|
||||
constructor(apiWrapper: ApiWrapper, parent: LanguageViewBase) {
|
||||
super(apiWrapper, parent.root, parent);
|
||||
this._installedLangsTab = this._apiWrapper.createTab(constants.extLangInstallTabTitle);
|
||||
|
||||
this._installedLangsTab.registerContent(async view => {
|
||||
|
||||
// TODO: only supporting single location for now. We should add a drop down for multi locations mode
|
||||
//
|
||||
let locationTitle = await this.getLocationTitle();
|
||||
this._locationComponent = view.modelBuilder.text().withProperties({
|
||||
value: locationTitle
|
||||
}).component();
|
||||
|
||||
this._languageTable = new LanguagesTable(apiWrapper, view.modelBuilder, this);
|
||||
this._installLanguagesTable = this._languageTable.table;
|
||||
|
||||
let formModel = view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
component: this._locationComponent,
|
||||
title: constants.extLangTarget
|
||||
}, {
|
||||
component: this._installLanguagesTable,
|
||||
title: ''
|
||||
}]).component();
|
||||
|
||||
this._loader = view.modelBuilder.loadingComponent()
|
||||
.withItem(formModel)
|
||||
.withProperties({
|
||||
loading: true
|
||||
}).component();
|
||||
|
||||
await view.initializeModel(this._loader);
|
||||
await this.reset();
|
||||
});
|
||||
}
|
||||
|
||||
public get tab(): azdata.window.DialogTab {
|
||||
return this._installedLangsTab;
|
||||
}
|
||||
|
||||
private async onLoading(): Promise<void> {
|
||||
if (this._loader) {
|
||||
await this._loader.updateProperties({ loading: true });
|
||||
}
|
||||
}
|
||||
|
||||
private async onLoaded(): Promise<void> {
|
||||
if (this._loader) {
|
||||
await this._loader.updateProperties({ loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
public async reset(): Promise<void> {
|
||||
await this.onLoading();
|
||||
|
||||
try {
|
||||
await this._languageTable?.reset();
|
||||
} catch (err) {
|
||||
this.showErrorMessage(constants.getErrorMessage(err));
|
||||
} finally {
|
||||
await this.onLoaded();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 * as vscode from 'vscode';
|
||||
import * as constants from '../../common/constants';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class FileBrowserDialog {
|
||||
|
||||
private _selectedPathTextBox: azdata.InputBoxComponent | undefined;
|
||||
private _fileBrowserDialog: azdata.window.Dialog | undefined;
|
||||
private _fileBrowserTree: azdata.FileBrowserTreeComponent | undefined;
|
||||
|
||||
private _onPathSelected: vscode.EventEmitter<string> = new vscode.EventEmitter<string>();
|
||||
public readonly onPathSelected: vscode.Event<string> = this._onPathSelected.event;
|
||||
|
||||
constructor(private _apiWrapper: ApiWrapper, private ownerUri: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog to browse server files and folders.
|
||||
*/
|
||||
public showDialog(): void {
|
||||
let fileBrowserTitle = '';
|
||||
this._fileBrowserDialog = this._apiWrapper.createModelViewDialog(fileBrowserTitle);
|
||||
let fileBrowserTab = this._apiWrapper.createTab(constants.extLangFileBrowserTabTitle);
|
||||
this._fileBrowserDialog.content = [fileBrowserTab];
|
||||
fileBrowserTab.registerContent(async (view) => {
|
||||
this._fileBrowserTree = view.modelBuilder.fileBrowserTree()
|
||||
.withProperties({ ownerUri: this.ownerUri, width: 420, height: 700 })
|
||||
.component();
|
||||
this._selectedPathTextBox = view.modelBuilder.inputBox()
|
||||
.withProperties({ inputType: 'text' })
|
||||
.component();
|
||||
this._fileBrowserTree.onDidChange((args) => {
|
||||
if (this._selectedPathTextBox) {
|
||||
this._selectedPathTextBox.value = args.fullPath;
|
||||
}
|
||||
});
|
||||
|
||||
let fileBrowserContainer = view.modelBuilder.formContainer()
|
||||
.withFormItems([{
|
||||
component: this._fileBrowserTree,
|
||||
title: ''
|
||||
}, {
|
||||
component: this._selectedPathTextBox,
|
||||
title: constants.extLangSelectedPath
|
||||
}
|
||||
]).component();
|
||||
view.initializeModel(fileBrowserContainer);
|
||||
});
|
||||
this._fileBrowserDialog.okButton.onClick(() => {
|
||||
if (this._selectedPathTextBox) {
|
||||
let selectedPath = this._selectedPathTextBox.value || '';
|
||||
this._onPathSelected.fire(selectedPath);
|
||||
}
|
||||
});
|
||||
|
||||
this._fileBrowserDialog.cancelButton.onClick(() => {
|
||||
this._onPathSelected.fire('');
|
||||
});
|
||||
this._fileBrowserDialog.okButton.label = constants.extLangOkButtonText;
|
||||
this._fileBrowserDialog.cancelButton.label = constants.extLangCancelButtonText;
|
||||
this._apiWrapper.openDialog(this._fileBrowserDialog);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as mssql from '../../../../mssql/src/mssql';
|
||||
import { LanguageViewBase } from './languageViewBase';
|
||||
import * as constants from '../../common/constants';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class LanguageContentView extends LanguageViewBase {
|
||||
|
||||
private _serverPath: azdata.RadioButtonComponent;
|
||||
private _localPath: azdata.RadioButtonComponent;
|
||||
public extensionFile: azdata.TextComponent;
|
||||
public extensionFileName: azdata.TextComponent;
|
||||
public envVariables: azdata.TextComponent;
|
||||
public parameters: azdata.TextComponent;
|
||||
private _isLocalPath: boolean = true;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(
|
||||
apiWrapper: ApiWrapper,
|
||||
parent: LanguageViewBase,
|
||||
private _modelBuilder: azdata.ModelBuilder,
|
||||
private _formBuilder: azdata.FormBuilder,
|
||||
private _languageContent: mssql.ExternalLanguageContent | undefined,
|
||||
) {
|
||||
super(apiWrapper, parent.root, parent);
|
||||
this._localPath = this._modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
value: 'local',
|
||||
name: 'extensionLocation',
|
||||
label: constants.extLangLocal,
|
||||
checked: true
|
||||
}).component();
|
||||
|
||||
this._serverPath = this._modelBuilder.radioButton()
|
||||
.withProperties({
|
||||
value: 'server',
|
||||
name: 'extensionLocation',
|
||||
label: this.getServerTitle(),
|
||||
}).component();
|
||||
|
||||
this._localPath.onDidClick(() => {
|
||||
this._isLocalPath = true;
|
||||
});
|
||||
this._serverPath.onDidClick(() => {
|
||||
this._isLocalPath = false;
|
||||
});
|
||||
|
||||
|
||||
let flexRadioButtonsModel = this._modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'row',
|
||||
justifyContent: 'space-between'
|
||||
//width: parent.componentMaxLength
|
||||
}).withItems([
|
||||
this._localPath, this._serverPath]
|
||||
).component();
|
||||
|
||||
this.extensionFile = this._modelBuilder.inputBox().withProperties({
|
||||
value: '',
|
||||
width: parent.componentMaxLength - parent.browseButtonMaxLength - parent.spaceBetweenComponentsLength
|
||||
}).component();
|
||||
let fileBrowser = this._modelBuilder.button().withProperties({
|
||||
label: '...',
|
||||
width: parent.browseButtonMaxLength,
|
||||
CSSStyles: {
|
||||
'text-align': 'end'
|
||||
}
|
||||
}).component();
|
||||
|
||||
let flexFilePathModel = this._modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'row',
|
||||
justifyContent: 'space-between'
|
||||
}).withItems([
|
||||
this.extensionFile, fileBrowser]
|
||||
).component();
|
||||
this.filePathSelected(args => {
|
||||
this.extensionFile.value = args.filePath;
|
||||
});
|
||||
fileBrowser.onDidClick(async () => {
|
||||
this.onOpenFileBrowser({ filePath: '', target: this._isLocalPath ? constants.localhost : this.connectionUrl });
|
||||
});
|
||||
|
||||
this.extensionFileName = this._modelBuilder.inputBox().withProperties({
|
||||
value: '',
|
||||
width: parent.componentMaxLength
|
||||
}).component();
|
||||
|
||||
this.envVariables = this._modelBuilder.inputBox().withProperties({
|
||||
value: '',
|
||||
width: parent.componentMaxLength
|
||||
}).component();
|
||||
this.parameters = this._modelBuilder.inputBox().withProperties({
|
||||
value: '',
|
||||
width: parent.componentMaxLength
|
||||
}).component();
|
||||
|
||||
this.load();
|
||||
|
||||
this._formBuilder.addFormItems([{
|
||||
component: flexRadioButtonsModel,
|
||||
title: constants.extLangExtensionFileLocation
|
||||
}, {
|
||||
component: flexFilePathModel,
|
||||
title: constants.extLangExtensionFilePath,
|
||||
required: true
|
||||
}, {
|
||||
component: this.extensionFileName,
|
||||
title: constants.extLangExtensionFileName,
|
||||
required: true
|
||||
}, {
|
||||
component: this.envVariables,
|
||||
title: constants.extLangEnvVariables
|
||||
}, {
|
||||
component: this.parameters,
|
||||
title: constants.extLangParameters
|
||||
}]);
|
||||
}
|
||||
|
||||
private load() {
|
||||
if (this._languageContent) {
|
||||
this._isLocalPath = this._languageContent.isLocalFile;
|
||||
this._localPath.checked = this._isLocalPath;
|
||||
this._serverPath.checked = !this._isLocalPath;
|
||||
this.extensionFile.value = this._languageContent.pathToExtension;
|
||||
this.extensionFileName.value = this._languageContent.extensionFileName;
|
||||
this.envVariables.value = this._languageContent.environmentVariables;
|
||||
this.parameters.value = this._languageContent.parameters;
|
||||
}
|
||||
}
|
||||
|
||||
public async reset(): Promise<void> {
|
||||
this._isLocalPath = true;
|
||||
this._localPath.checked = this._isLocalPath;
|
||||
this._serverPath.checked = !this._isLocalPath;
|
||||
this.load();
|
||||
}
|
||||
|
||||
public get updatedContent(): mssql.ExternalLanguageContent {
|
||||
return {
|
||||
pathToExtension: this.extensionFile.value || '',
|
||||
extensionFileName: this.extensionFileName.value || '',
|
||||
parameters: this.parameters.value || '',
|
||||
environmentVariables: this.envVariables.value || '',
|
||||
isLocalFile: this._isLocalPath || false,
|
||||
platform: this._languageContent?.platform
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as constants from '../../common/constants';
|
||||
import { AddEditLanguageTab } from './addEditLanguageTab';
|
||||
import { LanguageViewBase, LanguageUpdateModel } from './languageViewBase';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class LanguageEditDialog extends LanguageViewBase {
|
||||
|
||||
public addNewLanguageTab: AddEditLanguageTab | undefined;
|
||||
|
||||
constructor(
|
||||
apiWrapper: ApiWrapper,
|
||||
parent: LanguageViewBase,
|
||||
private _languageUpdateModel: LanguageUpdateModel) {
|
||||
super(apiWrapper, parent.root, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog to edit a language or a content of a language
|
||||
*/
|
||||
public showDialog(): void {
|
||||
this._dialog = this._apiWrapper.createModelViewDialog(constants.extLangDialogTitle);
|
||||
|
||||
this.addNewLanguageTab = new AddEditLanguageTab(this._apiWrapper, this, this._languageUpdateModel);
|
||||
|
||||
this._dialog.cancelButton.label = constants.extLangCancelButtonText;
|
||||
this._dialog.okButton.label = constants.extLangSaveButtonText;
|
||||
|
||||
this.dialog?.registerCloseValidator(async (): Promise<boolean> => {
|
||||
return await this.onSave();
|
||||
});
|
||||
|
||||
this._dialog.content = [this.addNewLanguageTab.tab];
|
||||
this._apiWrapper.openDialog(this._dialog);
|
||||
}
|
||||
|
||||
public async onSave(): Promise<boolean> {
|
||||
if (this.addNewLanguageTab) {
|
||||
try {
|
||||
await this.updateLanguage(this.addNewLanguageTab.updatedData);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.showErrorMessage(constants.extLangUpdateFailedError, err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Resets the tabs for given provider Id
|
||||
*/
|
||||
public async reset(): Promise<void> {
|
||||
await this.addNewLanguageTab?.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as constants from '../../common/constants';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
import * as mssql from '../../../../mssql/src/mssql';
|
||||
import * as path from 'path';
|
||||
|
||||
export interface LanguageUpdateModel {
|
||||
language: mssql.ExternalLanguage,
|
||||
content: mssql.ExternalLanguageContent,
|
||||
newLang: boolean
|
||||
}
|
||||
|
||||
export interface FileBrowseEventArgs {
|
||||
filePath: string,
|
||||
target: string
|
||||
}
|
||||
|
||||
export abstract class LanguageViewBase {
|
||||
protected _dialog: azdata.window.Dialog | undefined;
|
||||
public connection: azdata.connection.ConnectionProfile | undefined;
|
||||
public connectionUrl: string = '';
|
||||
|
||||
// Events
|
||||
//
|
||||
protected _onEdit: vscode.EventEmitter<LanguageUpdateModel> = new vscode.EventEmitter<LanguageUpdateModel>();
|
||||
public readonly onEdit: vscode.Event<LanguageUpdateModel> = this._onEdit.event;
|
||||
|
||||
protected _onUpdate: vscode.EventEmitter<LanguageUpdateModel> = new vscode.EventEmitter<LanguageUpdateModel>();
|
||||
public readonly onUpdate: vscode.Event<LanguageUpdateModel> = this._onUpdate.event;
|
||||
|
||||
protected _onDelete: vscode.EventEmitter<LanguageUpdateModel> = new vscode.EventEmitter<LanguageUpdateModel>();
|
||||
public readonly onDelete: vscode.Event<LanguageUpdateModel> = this._onDelete.event;
|
||||
|
||||
protected _fileBrowser: vscode.EventEmitter<FileBrowseEventArgs> = new vscode.EventEmitter<FileBrowseEventArgs>();
|
||||
public readonly fileBrowser: vscode.Event<FileBrowseEventArgs> = this._fileBrowser.event;
|
||||
|
||||
protected _filePathSelected: vscode.EventEmitter<FileBrowseEventArgs> = new vscode.EventEmitter<FileBrowseEventArgs>();
|
||||
public readonly filePathSelected: vscode.Event<FileBrowseEventArgs> = this._filePathSelected.event;
|
||||
|
||||
protected _onUpdated: vscode.EventEmitter<LanguageUpdateModel> = new vscode.EventEmitter<LanguageUpdateModel>();
|
||||
public readonly onUpdated: vscode.Event<LanguageUpdateModel> = this._onUpdated.event;
|
||||
|
||||
protected _onList: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
|
||||
public readonly onList: vscode.Event<void> = this._onList.event;
|
||||
|
||||
protected _onListLoaded: vscode.EventEmitter<mssql.ExternalLanguage[]> = new vscode.EventEmitter<mssql.ExternalLanguage[]>();
|
||||
public readonly onListLoaded: vscode.Event<mssql.ExternalLanguage[]> = this._onListLoaded.event;
|
||||
|
||||
protected _onFailed: vscode.EventEmitter<any> = new vscode.EventEmitter<any>();
|
||||
public readonly onFailed: vscode.Event<any> = this._onFailed.event;
|
||||
|
||||
public componentMaxLength = 350;
|
||||
public browseButtonMaxLength = 20;
|
||||
public spaceBetweenComponentsLength = 10;
|
||||
|
||||
constructor(protected _apiWrapper: ApiWrapper, protected _root?: string, protected _parent?: LanguageViewBase, ) {
|
||||
if (this._parent) {
|
||||
if (!this._root) {
|
||||
this._root = this._parent.root;
|
||||
}
|
||||
this.connection = this._parent.connection;
|
||||
this.connectionUrl = this._parent.connectionUrl;
|
||||
}
|
||||
this.registerEvents();
|
||||
}
|
||||
|
||||
private registerEvents() {
|
||||
if (this._parent) {
|
||||
this._dialog = this._parent.dialog;
|
||||
this.fileBrowser(url => {
|
||||
this._parent?.onOpenFileBrowser(url);
|
||||
});
|
||||
this.onUpdate(model => {
|
||||
this._parent?.onUpdateLanguage(model);
|
||||
});
|
||||
this.onEdit(model => {
|
||||
this._parent?.onEditLanguage(model);
|
||||
});
|
||||
this.onDelete(model => {
|
||||
this._parent?.onDeleteLanguage(model);
|
||||
});
|
||||
this.onList(() => {
|
||||
this._parent?.onListLanguages();
|
||||
});
|
||||
this._parent.filePathSelected(x => {
|
||||
this.onFilePathSelected(x);
|
||||
});
|
||||
this._parent.onUpdated(x => {
|
||||
this.onUpdatedLanguage(x);
|
||||
});
|
||||
this._parent.onFailed(x => {
|
||||
this.onActionFailed(x);
|
||||
});
|
||||
this._parent.onListLoaded(x => {
|
||||
this.onListLanguageLoaded(x);
|
||||
});
|
||||
}
|
||||
}
|
||||
public async getLocationTitle(): Promise<string> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection) {
|
||||
return `${connection.serverName} ${connection.databaseName ? connection.databaseName : constants.extLangLocal}`;
|
||||
}
|
||||
return constants.packageManagerNoConnection;
|
||||
}
|
||||
|
||||
public getServerTitle(): string {
|
||||
if (this.connection) {
|
||||
return this.connection.serverName;
|
||||
}
|
||||
return constants.packageManagerNoConnection;
|
||||
}
|
||||
|
||||
private async getCurrentConnectionUrl(): Promise<string> {
|
||||
let connection = await this.getCurrentConnection();
|
||||
if (connection) {
|
||||
return await this._apiWrapper.getUriForConnection(connection.connectionId);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private async getCurrentConnection(): Promise<azdata.connection.ConnectionProfile> {
|
||||
return await this._apiWrapper.getCurrentConnection();
|
||||
}
|
||||
|
||||
public async loadConnection(): Promise<void> {
|
||||
this.connection = await this.getCurrentConnection();
|
||||
this.connectionUrl = await this.getCurrentConnectionUrl();
|
||||
}
|
||||
|
||||
public updateLanguage(updateModel: LanguageUpdateModel): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.onUpdateLanguage(updateModel);
|
||||
this.onUpdated(() => {
|
||||
resolve();
|
||||
});
|
||||
this.onFailed(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public deleteLanguage(model: LanguageUpdateModel): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.onDeleteLanguage(model);
|
||||
this.onUpdated(() => {
|
||||
resolve();
|
||||
});
|
||||
this.onFailed(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public listLanguages(): Promise<mssql.ExternalLanguage[]> {
|
||||
return new Promise<mssql.ExternalLanguage[]>((resolve, reject) => {
|
||||
this.onListLanguages();
|
||||
this.onListLoaded(list => {
|
||||
resolve(list);
|
||||
});
|
||||
this.onFailed(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Dialog model instance
|
||||
*/
|
||||
public get dialog(): azdata.window.Dialog | undefined {
|
||||
return this._dialog;
|
||||
}
|
||||
|
||||
public set dialog(value: azdata.window.Dialog | undefined) {
|
||||
this._dialog = value;
|
||||
}
|
||||
|
||||
public showInfoMessage(message: string): void {
|
||||
this.showMessage(message, azdata.window.MessageLevel.Information);
|
||||
}
|
||||
|
||||
public showErrorMessage(message: string, error?: any): void {
|
||||
this.showMessage(`${message} ${constants.getErrorMessage(error)}`, azdata.window.MessageLevel.Error);
|
||||
}
|
||||
|
||||
public onUpdateLanguage(model: LanguageUpdateModel): void {
|
||||
this._onUpdate.fire(model);
|
||||
}
|
||||
|
||||
public onUpdatedLanguage(model: LanguageUpdateModel): void {
|
||||
this._onUpdated.fire(model);
|
||||
}
|
||||
|
||||
public onActionFailed(error: any): void {
|
||||
this._onFailed.fire(error);
|
||||
}
|
||||
|
||||
public onListLanguageLoaded(list: mssql.ExternalLanguage[]): void {
|
||||
this._onListLoaded.fire(list);
|
||||
}
|
||||
|
||||
public onEditLanguage(model: LanguageUpdateModel): void {
|
||||
this._onEdit.fire(model);
|
||||
}
|
||||
|
||||
public onDeleteLanguage(model: LanguageUpdateModel): void {
|
||||
this._onDelete.fire(model);
|
||||
}
|
||||
|
||||
public onListLanguages(): void {
|
||||
this._onList.fire();
|
||||
}
|
||||
|
||||
public onOpenFileBrowser(fileBrowseArgs: FileBrowseEventArgs): void {
|
||||
this._fileBrowser.fire(fileBrowseArgs);
|
||||
}
|
||||
|
||||
public onFilePathSelected(fileBrowseArgs: FileBrowseEventArgs): void {
|
||||
this._filePathSelected.fire(fileBrowseArgs);
|
||||
}
|
||||
|
||||
private showMessage(message: string, level: azdata.window.MessageLevel): void {
|
||||
if (this._dialog) {
|
||||
this._dialog.message = {
|
||||
text: message,
|
||||
level: level
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public get root(): string {
|
||||
return this._root || '';
|
||||
}
|
||||
|
||||
public asAbsolutePath(filePath: string): string {
|
||||
return path.join(this._root || '', filePath);
|
||||
}
|
||||
|
||||
public abstract reset(): Promise<void>;
|
||||
|
||||
public createNewContent(): mssql.ExternalLanguageContent {
|
||||
return {
|
||||
extensionFileName: '',
|
||||
isLocalFile: true,
|
||||
pathToExtension: '',
|
||||
};
|
||||
}
|
||||
|
||||
public createNewLanguage(): mssql.ExternalLanguage {
|
||||
return {
|
||||
name: '',
|
||||
contents: []
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CurrentLanguagesTab } from './currentLanguagesTab';
|
||||
import { AddEditLanguageTab } from './addEditLanguageTab';
|
||||
import { LanguageViewBase } from './languageViewBase';
|
||||
import * as constants from '../../common/constants';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class LanguagesDialog extends LanguageViewBase {
|
||||
|
||||
public currentLanguagesTab: CurrentLanguagesTab | undefined;
|
||||
public addNewLanguageTab: AddEditLanguageTab | undefined;
|
||||
|
||||
constructor(
|
||||
apiWrapper: ApiWrapper,
|
||||
root: string) {
|
||||
super(apiWrapper, root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a dialog to manage packages used by notebooks.
|
||||
*/
|
||||
public showDialog(): void {
|
||||
this.dialog = this._apiWrapper.createModelViewDialog(constants.extLangDialogTitle);
|
||||
|
||||
this.currentLanguagesTab = new CurrentLanguagesTab(this._apiWrapper, this);
|
||||
|
||||
let languageUpdateModel = {
|
||||
language: this.createNewLanguage(),
|
||||
content: this.createNewContent(),
|
||||
newLang: true
|
||||
};
|
||||
this.addNewLanguageTab = new AddEditLanguageTab(this._apiWrapper, this, languageUpdateModel);
|
||||
|
||||
this.dialog.okButton.hidden = true;
|
||||
this.dialog.cancelButton.label = constants.extLangDoneButtonText;
|
||||
this.dialog.content = [this.currentLanguagesTab.tab, this.addNewLanguageTab.tab];
|
||||
|
||||
this.dialog.registerCloseValidator(() => {
|
||||
return false; // Blocks Enter key from closing dialog.
|
||||
});
|
||||
|
||||
this._apiWrapper.openDialog(this.dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the tabs for given provider Id
|
||||
*/
|
||||
public async reset(): Promise<void> {
|
||||
await this.currentLanguagesTab?.reset();
|
||||
await this.addNewLanguageTab?.reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as constants from '../../common/constants';
|
||||
import * as mssql from '../../../../mssql/src/mssql';
|
||||
import { LanguageViewBase } from './languageViewBase';
|
||||
import { ApiWrapper } from '../../common/apiWrapper';
|
||||
|
||||
export class LanguagesTable extends LanguageViewBase {
|
||||
|
||||
private _table: azdata.DeclarativeTableComponent;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(apiWrapper: ApiWrapper, private _modelBuilder: azdata.ModelBuilder, parent: LanguageViewBase) {
|
||||
super(apiWrapper, parent.root, parent);
|
||||
this._table = _modelBuilder.declarativeTable()
|
||||
.withProperties<azdata.DeclarativeTableProperties>(
|
||||
{
|
||||
columns: [
|
||||
{ // Name
|
||||
displayName: constants.extLangLanguageName,
|
||||
ariaLabel: constants.extLangLanguageName,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: 100,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Platform
|
||||
displayName: constants.extLangLanguagePlatform,
|
||||
ariaLabel: constants.extLangLanguagePlatform,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: 150,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Created Date
|
||||
displayName: constants.extLangLanguageCreatedDate,
|
||||
ariaLabel: constants.extLangLanguageCreatedDate,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
isReadOnly: true,
|
||||
width: 150,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Action
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: 50,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
},
|
||||
{ // Action
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
isReadOnly: true,
|
||||
width: 50,
|
||||
headerCssStyles: {
|
||||
...constants.cssStyles.tableHeader
|
||||
},
|
||||
rowCssStyles: {
|
||||
...constants.cssStyles.tableRow
|
||||
},
|
||||
}
|
||||
],
|
||||
data: [],
|
||||
ariaLabel: constants.mlsConfigTitle
|
||||
})
|
||||
.component();
|
||||
}
|
||||
|
||||
public get table(): azdata.DeclarativeTableComponent {
|
||||
return this._table;
|
||||
}
|
||||
|
||||
public async loadData(): Promise<void> {
|
||||
let languages: mssql.ExternalLanguage[] | undefined;
|
||||
|
||||
languages = await this.listLanguages();
|
||||
let tableData: any[][] = [];
|
||||
|
||||
if (languages) {
|
||||
|
||||
languages.forEach(language => {
|
||||
if (!language.contents || language.contents.length === 0) {
|
||||
language.contents.push(this.createNewContent());
|
||||
}
|
||||
|
||||
tableData = tableData.concat(language.contents.map(content => this.createTableRow(language, content)));
|
||||
});
|
||||
}
|
||||
|
||||
this._table.data = tableData;
|
||||
}
|
||||
|
||||
private createTableRow(language: mssql.ExternalLanguage, content: mssql.ExternalLanguageContent): any[] {
|
||||
if (this._modelBuilder) {
|
||||
let dropLanguageButton = this._modelBuilder.button().withProperties({
|
||||
label: '',
|
||||
title: constants.deleteTitle,
|
||||
iconPath: {
|
||||
dark: this.asAbsolutePath('images/dark/delete_inverse.svg'),
|
||||
light: this.asAbsolutePath('images/light/delete.svg')
|
||||
},
|
||||
width: 15,
|
||||
height: 15
|
||||
}).component();
|
||||
dropLanguageButton.onDidClick(async () => {
|
||||
await this.deleteLanguage({
|
||||
language: language,
|
||||
content: content,
|
||||
newLang: false
|
||||
});
|
||||
});
|
||||
|
||||
let editLanguageButton = this._modelBuilder.button().withProperties({
|
||||
label: '',
|
||||
title: constants.deleteTitle,
|
||||
iconPath: {
|
||||
dark: this.asAbsolutePath('images/dark/edit_inverse.svg'),
|
||||
light: this.asAbsolutePath('images/light/edit.svg')
|
||||
},
|
||||
width: 15,
|
||||
height: 15
|
||||
}).component();
|
||||
editLanguageButton.onDidClick(() => {
|
||||
this.onEditLanguage({
|
||||
language: language,
|
||||
content: content,
|
||||
newLang: false
|
||||
});
|
||||
});
|
||||
return [language.name, content.platform, language.createdDate, dropLanguageButton, editLanguageButton];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public async reset(): Promise<void> {
|
||||
await this.loadData();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user