From 0dd544aff4883e141a183fb2a21d392a5ac95d13 Mon Sep 17 00:00:00 2001 From: goyal-anjali <66509246+goyal-anjali@users.noreply.github.com> Date: Thu, 7 Oct 2021 22:43:31 +0530 Subject: [PATCH] Add callout dialog from error count to detailed error message on migration status dialog (#16850) --- .../sql-migration/src/constants/strings.ts | 2 + .../migrationCutoverDialogModel.ts | 12 +++ .../migrationStatus/migrationStatusDialog.ts | 74 ++++++++++++++++--- 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index f175f4fcbc..3f9b8c8961 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -223,6 +223,8 @@ export const NAME_OF_NEW_RESOURCE_GROUP = localize('sql.migration.name.of.new.rg export const DATA_UPLOADED_INFO = localize('sql.migration.data.uploaded.info', "Comparison of the actual amount of data read from the source and the actual amount of data uploaded to the target."); export const COPY_THROUGHPUT_INFO = localize('sql.migration.copy.throughput.info', "Data movement throughput achieved during the migration of your database backups to Azure. This is the rate of data transfer, calculated by data read divided by duration of backups migration to Azure."); // common strings +export const WARNING = localize('sql.migration.warning', "Warning"); +export const ERROR = localize('sql.migration.error', "Error"); export const LEARN_MORE = localize('sql.migration.learn.more', "Learn more"); export const LEARN_MORE_ABOUT_PRE_REQS = localize('sql.migration.learn.more.pre.reqs', "Learn more about things you need before starting a migration."); export const SUBSCRIPTION = localize('sql.migration.subscription', "Subscription"); diff --git a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts index abe3dfcb9a..98cb0c9e06 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts @@ -7,6 +7,7 @@ import { getMigrationStatus, DatabaseMigration, startMigrationCutover, stopMigra import { BackupFileInfoStatus, MigrationContext } from '../../models/migrationLocalStorage'; import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../../telemtery'; import * as constants from '../../constants/strings'; +import { EOL } from 'os'; import { getMigrationTargetType, getMigrationMode } from '../../constants/helper'; export class MigrationCutoverDialogModel { @@ -75,6 +76,17 @@ export class MigrationCutoverDialogModel { return undefined!; } + public async fetchErrors(): Promise { + const errors = []; + await this.fetchStatus(); + errors.push(this.migrationOpStatus.error?.message); + errors.push(this._migration.asyncOperationResult?.error?.message); + errors.push(this.migrationStatus.properties.migrationFailureError?.message); + return errors + .filter((e, i, arr) => e !== undefined && i === arr.indexOf(e)) + .join(EOL); + } + public async cancelMigration(): Promise { try { this.CancelMigrationError = undefined; diff --git a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts index db0ee16a41..e62a930ebe 100644 --- a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts @@ -436,12 +436,45 @@ export class MigrationStatusDialog { warningCount++; } - return this._getStatusControl(migrationStatus, warningCount); + return this._getStatusControl(migrationStatus, warningCount, migration); } - private _getStatusControl(status: string, count: number): azdata.FlexContainer { + public openCalloutDialog(dialogHeading: string, dialogName?: string, calloutMessageText?: string): void { + const dialog = azdata.window.createModelViewDialog(dialogHeading, dialogName, 288, 'callout', 'left', true, false, + { + xPos: 0, + yPos: 0, + width: 20, + height: 20 + }); + const tab: azdata.window.DialogTab = azdata.window.createTab(''); + tab.registerContent(async view => { + const warningContentContainer = view.modelBuilder.divContainer().component(); + const messageTextComponent = view.modelBuilder.text().withProps({ + value: calloutMessageText, + CSSStyles: { + 'font-size': '12px', + 'line-height': '16px', + 'margin': '0 0 12px 0', + 'display': '-webkit-box', + '-webkit-box-orient': 'vertical', + '-webkit-line-clamp': '5', + 'overflow': 'hidden' + } + }).component(); + warningContentContainer.addItem(messageTextComponent); + + await view.initializeModel(warningContentContainer); + }); + + dialog.content = [tab]; + + azdata.window.openDialog(dialog); + } + + private _getStatusControl(status: string, count: number, migration: MigrationContext): azdata.DivContainer { const control = this._view.modelBuilder - .flexContainer() + .divContainer() .withItems([ // migration status icon this._view.modelBuilder.image() @@ -465,23 +498,42 @@ export class MigrationStatusDialog { .component(); if (count > 0) { - control.addItems([ - // migration warning / error image - this._view.modelBuilder.image().withProps({ + const migrationWarningImage = this._view.modelBuilder.image() + .withProps({ iconPath: this._statusInfoMap(status), iconHeight: statusImageSize, iconWidth: statusImageSize, height: statusImageSize, width: statusImageSize, CSSStyles: imageCellStyles - }).component(), - // migration warning / error counts - this._view.modelBuilder.text().withProps({ - value: loc.STATUS_WARNING_COUNT(status, count), + }).component(); + + const migrationWarningCount = this._view.modelBuilder.hyperlink() + .withProps({ + label: loc.STATUS_WARNING_COUNT(status, count) ?? '', + ariaLabel: loc.ERROR, + url: '', height: statusImageSize, CSSStyles: statusCellStyles, - }).component() + }).component(); + + control.addItems([ + migrationWarningImage, + migrationWarningCount ]); + + this._disposables.push(migrationWarningCount.onDidClick(async () => { + const cutoverDialogModel = new MigrationCutoverDialogModel(migration!); + const errors = await cutoverDialogModel.fetchErrors(); + this.openCalloutDialog( + status === MigrationStatus.InProgress + || status === MigrationStatus.Completing + ? loc.WARNING + : loc.ERROR, + 'input-table-row-dialog', + errors + ); + })); } return control;