From 5a428b83ae78a9af259ecdd59e410db70fcc5b9d Mon Sep 17 00:00:00 2001 From: nasc17 <69922333+nasc17@users.noreply.github.com> Date: Wed, 14 Jul 2021 15:51:42 -0700 Subject: [PATCH] Alter table to be multi-select grid for dropping PG extensions (#16143) * Check if no extensions currently present before adding * Added multiselect option for dropping extensions * Loc file changes --- extensions/arc/src/localizedConstants.ts | 4 +- .../postgres/postgresExtensionsPage.ts | 178 +++++++++--------- 2 files changed, 94 insertions(+), 88 deletions(-) diff --git a/extensions/arc/src/localizedConstants.ts b/extensions/arc/src/localizedConstants.ts index 6e189962c0..48d37d8315 100644 --- a/extensions/arc/src/localizedConstants.ts +++ b/extensions/arc/src/localizedConstants.ts @@ -43,7 +43,7 @@ export const saveText = localize('arc.save', "Save"); export const discardText = localize('arc.discard', "Discard"); export const resetPassword = localize('arc.resetPassword', "Reset Password"); export const addExtensions = localize('arc.addExtensions', "Add extensions"); -export const dropExtension = localize('arc.dropExtension', "Drop extension"); +export const dropExtensions = localize('arc.dropExtensions', "Drop extensions"); export const openInAzurePortal = localize('arc.openInAzurePortal', "Open in Azure Portal"); export const resourceGroup = localize('arc.resourceGroup', "Resource Group"); export const region = localize('arc.region', "Region"); @@ -223,7 +223,7 @@ export function extensionInstalled(name: string): string { return localize('arc. export function updatingInstance(name: string): string { return localize('arc.updatingInstance', "Updating instance '{0}'...", name); } export function instanceDeleted(name: string): string { return localize('arc.instanceDeleted', "Instance '{0}' deleted", name); } export function instanceUpdated(name: string): string { return localize('arc.instanceUpdated', "Instance '{0}' updated", name); } -export function extensionDropped(name: string): string { return localize('arc.extensionDropped', "Extension '{0}' deleted", name); } +export function extensionsDropped(name: string): string { return localize('arc.extensionsDropped', "Extensions '{0}' dropped", name); } export function extensionsAdded(name: string): string { return localize('arc.extensionsAdded', "Extensions '{0}' added", name); } export function copiedToClipboard(name: string): string { return localize('arc.copiedToClipboard', "{0} copied to clipboard", name); } export function clickTheTroubleshootButton(resourceType: string): string { return localize('arc.clickTheTroubleshootButton', "Click the troubleshoot button to open the Azure Arc {0} troubleshooting notebook.", resourceType); } diff --git a/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts b/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts index bfe9a48248..666d83c68c 100644 --- a/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts +++ b/extensions/arc/src/ui/dashboards/postgres/postgresExtensionsPage.ts @@ -11,15 +11,15 @@ import { IconPathHelper, cssStyles } from '../../../constants'; import { DashboardPage } from '../../components/dashboardPage'; import { PostgresModel } from '../../../models/postgresModel'; import { AddPGExtensionsDialog } from '../../dialogs/addPGExtensionsDialog'; -import { Deferred } from '../../../common/promise'; export class PostgresExtensionsPage extends DashboardPage { private extensionNames: string[] = []; + private droppedExtensions: string[] = []; private extensionsTable!: azdata.DeclarativeTableComponent; private extensionsLoading!: azdata.LoadingComponent; private addExtensionsButton!: azdata.ButtonComponent; - private _dropExtPromise?: Deferred; + private dropExtensionsButton!: azdata.ButtonComponent; private extensionsLink!: azdata.HyperlinkComponent; private readonly _azdataApi: azdataExt.IExtension; @@ -79,23 +79,23 @@ export class PostgresExtensionsPage extends DashboardPage { width: '100%', columns: [ { - displayName: loc.extensionName, - valueType: azdata.DeclarativeDataType.string, + displayName: '', + valueType: azdata.DeclarativeDataType.component, + width: '20px', isReadOnly: true, - width: '95%', headerCssStyles: cssStyles.tableHeader, rowCssStyles: cssStyles.tableRow }, { - displayName: loc.dropText, - valueType: azdata.DeclarativeDataType.component, - isReadOnly: false, - width: '10%', + displayName: loc.extensionName, + valueType: azdata.DeclarativeDataType.string, + isReadOnly: true, + width: '100%', headerCssStyles: cssStyles.tableHeader, rowCssStyles: cssStyles.tableRow } ], - data: [] + dataValues: [] }).component(); this.extensionsLoading = this.modelView.modelBuilder.loadingComponent() @@ -128,7 +128,8 @@ export class PostgresExtensionsPage extends DashboardPage { if (extArg) { try { this.addExtensionsButton.enabled = false; - let extensionList = this.extensionNames.join() + ',' + extArg; + this.dropExtensionsButton.enabled = false; + let extensionList = this.extensionNames.length ? this.extensionNames.join() + ',' + extArg : extArg; await vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, @@ -152,7 +153,7 @@ export class PostgresExtensionsPage extends DashboardPage { } ); - vscode.window.showInformationMessage(loc.extensionsAdded(extensionList)); + vscode.window.showInformationMessage(loc.extensionsAdded(extArg)); } catch (error) { vscode.window.showErrorMessage(loc.updateExtensionsFailed(error)); @@ -162,43 +163,20 @@ export class PostgresExtensionsPage extends DashboardPage { } })); - return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([ - { component: this.addExtensionsButton } - ]).component(); - } - - private refreshExtensionsTable(): void { - let extensions = this._postgresModel.config!.spec.engine.extensions; - this.extensionsTable.data = extensions.map(e => { - - this.extensionNames.push(e.name); - - return [e.name, this.createDropButton(e.name)]; - }); - } - - /** - * Creates drop button to add to each row of extensions table. - * Allows user to drop individual extension. - * @param name name of postgres extension the drop button will be tied to. - */ - public createDropButton(name: string): azdata.ButtonComponent { - // Can drop individual extensions - let button = this.modelView.modelBuilder.button().withProps({ - iconPath: IconPathHelper.delete, - ariaLabel: loc.dropExtension, - title: loc.dropExtension, - width: '20px', - height: '20px', - enabled: true + // Drop extensions + this.dropExtensionsButton = this.modelView.modelBuilder.button().withProps({ + label: loc.dropExtensions, + ariaLabel: loc.addExtensions, + iconPath: IconPathHelper.add, + enabled: false }).component(); this.disposables.push( - button.onDidClick(async () => { + this.dropExtensionsButton.onDidClick(async () => { try { this.addExtensionsButton.enabled = false; - button.enabled = false; - await this.dropExtension(name); + this.dropExtensionsButton.enabled = false; + await this.dropExtension(); try { await this._postgresModel.refresh(); @@ -206,63 +184,91 @@ export class PostgresExtensionsPage extends DashboardPage { vscode.window.showErrorMessage(loc.refreshFailed(error)); } - vscode.window.showInformationMessage(loc.extensionDropped(name)); + vscode.window.showInformationMessage(loc.extensionsDropped(this.droppedExtensions.join())); + this.droppedExtensions = []; } catch (error) { vscode.window.showErrorMessage(loc.updateExtensionsFailed(error)); } finally { this.addExtensionsButton.enabled = true; } + })); + + return this.modelView.modelBuilder.toolbarContainer().withToolbarItems([ + { component: this.addExtensionsButton }, + { component: this.dropExtensionsButton } + ]).component(); + } + + private refreshExtensionsTable(): void { + let extensions = this._postgresModel.config!.spec.engine.extensions; + let extensionBasicData = extensions.map(e => { + this.extensionNames.push(e.name); + return [this.createDropCheckBox(e.name), e.name]; + }); + + let extenesionFinalData: azdata.DeclarativeTableCellValue[][] = []; + extenesionFinalData = extensionBasicData.map(e => { + return e.map((value): azdata.DeclarativeTableCellValue => { + return { value: value }; + }); + }); + + this.extensionsTable.setDataValues(extenesionFinalData); + } + + /** + * Creates checkboxes to select which extensions to drop. + * Allows user to drop multiple extension. + * @param name name of postgres extension the checkbox will be tied to. + */ + public createDropCheckBox(name: string): azdata.CheckBoxComponent { + // Can select extensions to drop + let checkBox = this.modelView.modelBuilder.checkBox().withProps({ + ariaLabel: loc.dropExtensions, + CSSStyles: { ...cssStyles.text, 'margin-block-start': '0px', 'margin-block-end': '0px' } + }).component(); + + this.disposables.push( + checkBox.onChanged(() => { + if (checkBox.checked) { + this.droppedExtensions.push(name); + } else { + let index = this.droppedExtensions.indexOf(name, 0); + this.droppedExtensions.splice(index, 1); + } + this.dropExtensionsButton.enabled = this.droppedExtensions.length ? true : false; }) ); - // Dropping the citus extension is not supported. - if (name === 'citus') { - button.enabled = false; - } - - return button; + return checkBox; } /** * Calls edit on postgres extensions with an updated extensions list. - * @param name name of postgres extension to not inlcude when editing list of extensions */ - public async dropExtension(name: string): Promise { - // Only allow one drop to be happening at a time - if (this._dropExtPromise) { - vscode.window.showErrorMessage(loc.dropMultipleExtensions); - return this._dropExtPromise.promise; - } + public async dropExtension(): Promise { + this.droppedExtensions.forEach(d => { + let index = this.droppedExtensions.indexOf(d, 0); + this.extensionNames.splice(index, 1); + }); - this._dropExtPromise = new Deferred(); - try { - await vscode.window.withProgress( - { - location: vscode.ProgressLocation.Notification, - title: loc.updatingInstance(this._postgresModel.info.name), - cancellable: false - }, - async (_progress, _token): Promise => { - let index = this.extensionNames.indexOf(name, 0); - this.extensionNames.splice(index, 1); - - await this._azdataApi.azdata.arc.postgres.server.edit( - this._postgresModel.info.name, - { - extensions: this.extensionNames.join() - }, - this._postgresModel.controllerModel.azdataAdditionalEnvVars - ); - } - ); - this._dropExtPromise.resolve(); - } catch (err) { - this._dropExtPromise.reject(err); - throw err; - } finally { - this._dropExtPromise = undefined; - } + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: loc.updatingInstance(this._postgresModel.info.name), + cancellable: false + }, + async (_progress, _token): Promise => { + await this._azdataApi.azdata.arc.postgres.server.edit( + this._postgresModel.info.name, + { + extensions: this.extensionNames.join() + }, + this._postgresModel.controllerModel.azdataAdditionalEnvVars + ); + } + ); } private handleConfigUpdated(): void {