ML - dashboard icons and links (#10153)

* ML - dashboard icons and links
This commit is contained in:
Leila Lali
2020-04-28 21:21:30 -07:00
committed by GitHub
parent 046995f2a5
commit 04af41c424
145 changed files with 387 additions and 134 deletions

View File

@@ -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();
}
}

View File

@@ -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.getServerTitle();
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();
}
}
}

View File

@@ -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);
}
}

View File

@@ -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';
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
};
}
}

View File

@@ -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';
import { ApiWrapper } from '../../common/apiWrapper';
import { LanguageService } from '../../externalLanguage/languageService';
import { LanguagesDialog } from './languagesDialog';
import { LanguageEditDialog } from './languageEditDialog';
import { FileBrowserDialog } from './fileBrowserDialog';
import { LanguageViewBase, LanguageUpdateModel } from './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);
}
}

View File

@@ -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();
}
}

View File

@@ -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';
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.noConnectionError;
}
public getServerTitle(): string {
if (this.connection) {
return this.connection.serverName;
}
return constants.noConnectionError;
}
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: []
};
}
}

View File

@@ -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();
}
}

View File

@@ -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';
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();
}
}