From 155ea4c7077e7cc4ed12ea70a6dcf0a5d473cf1f Mon Sep 17 00:00:00 2001 From: brian-harris <61598682+brian-harris@users.noreply.github.com> Date: Tue, 21 Sep 2021 23:31:36 -0700 Subject: [PATCH] add error banner for failed migration cutover and cancel migration (#17106) --- extensions/sql-migration/src/.eslintrc.json | 13 ++++ extensions/sql-migration/src/api/azure.ts | 2 +- extensions/sql-migration/src/api/utils.ts | 8 ++ .../sql-migration/src/constants/strings.ts | 1 + .../src/dashboard/sqlServerDashboard.ts | 26 +++---- .../assessmentResultsDialog.ts | 2 +- .../assessmentResults/sqlDatabasesTree.ts | 30 ++++---- .../createResourceGroupDialog.ts | 14 ++-- .../createSqlMigrationServiceDialog.ts | 30 ++++---- .../migrationCutover/confirmCutoverDialog.ts | 10 +-- .../migrationCutoverDialog.ts | 75 ++++++++++--------- .../migrationCutoverDialogModel.ts | 10 ++- .../migrationStatus/migrationStatusDialog.ts | 47 +++++------- .../sqlMigrationServiceDetailsDialog.ts | 14 ++-- extensions/sql-migration/src/main.ts | 4 +- .../src/models/migrationLocalStorage.ts | 22 ++++-- .../src/models/migrationWizardPage.ts | 2 +- .../sql-migration/src/models/stateMachine.ts | 8 +- .../src/wizard/accountsSelectionPage.ts | 6 +- .../src/wizard/databaseBackupPage.ts | 69 +++++++++-------- .../src/wizard/databaseSelectorPage.ts | 4 +- .../src/wizard/integrationRuntimePage.ts | 10 +-- .../src/wizard/skuRecommendationPage.ts | 40 +++++----- .../src/wizard/sqlSourceConfigurationPage.ts | 2 +- .../sql-migration/src/wizard/summaryPage.ts | 18 ++--- .../src/wizard/wizardController.ts | 7 +- 26 files changed, 250 insertions(+), 224 deletions(-) create mode 100644 extensions/sql-migration/src/.eslintrc.json diff --git a/extensions/sql-migration/src/.eslintrc.json b/extensions/sql-migration/src/.eslintrc.json new file mode 100644 index 0000000000..503681affa --- /dev/null +++ b/extensions/sql-migration/src/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "parserOptions": { + "project": "./extensions/sql-migration/tsconfig.json" + }, + "rules": { + "@typescript-eslint/no-floating-promises": [ + "error", + { + "ignoreVoid": true + } + ] + } +} diff --git a/extensions/sql-migration/src/api/azure.ts b/extensions/sql-migration/src/api/azure.ts index 93d3dbb50d..f21b5ef00f 100644 --- a/extensions/sql-migration/src/api/azure.ts +++ b/extensions/sql-migration/src/api/azure.ts @@ -547,7 +547,7 @@ export interface TargetLocation { export interface BackupFileInfo { fileName: string; - status: 'Arrived' | 'Uploading' | 'Uploaded' | 'Restoring' | 'Restored' | 'Cancelled' | 'Ignored'; + status: 'Arrived' | 'Uploading' | 'Uploaded' | 'Restoring' | 'Restored' | 'Canceled' | 'Ignored'; totalSize: number; dataRead: number; dataWritten: number; diff --git a/extensions/sql-migration/src/api/utils.ts b/extensions/sql-migration/src/api/utils.ts index 0fcf6d7875..3dba319026 100644 --- a/extensions/sql-migration/src/api/utils.ts +++ b/extensions/sql-migration/src/api/utils.ts @@ -230,6 +230,14 @@ export function get12HourTime(date: Date | undefined): string { return (date ? date : new Date()).toLocaleTimeString([], localeTimeStringOptions); } +export function displayDialogErrorMessage(dialog: window.Dialog, text: string, error: Error): void { + dialog.message = { + level: window.MessageLevel.Error, + text: text, + description: error.message, + }; +} + export function clearDialogMessage(dialog: window.Dialog): void { dialog.message = { text: '' diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 212aaccd5b..e390f6ed7b 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -337,6 +337,7 @@ export const YES = localize('sql.migration.yes', "Yes"); export const NO = localize('sql.migration.no', "No"); export const EMPTY_TABLE_TEXT = localize('sql.migration.empty.table.text', "No backup files"); export const EMPTY_TABLE_SUBTEXT = localize('sql.migration.empty.table.subtext', "If results were expected, verify the connection to the SQL Server instance."); +export const MIGRATION_CUTOVER_ERROR = localize('sql.migration.cutover.error', 'An error occurred while initiating cutover.'); //Migration confirm cutover dialog export const COMPLETING_CUTOVER_WARNING = localize('sql.migration.completing.cutover.warning', "Completing cutover without restoring all the backups may result in a data loss."); diff --git a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts index 6afc1d25d1..1c3b49e253 100644 --- a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts +++ b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts @@ -105,7 +105,7 @@ export class DashboardWidget { })); await view.initializeModel(container); - this.refreshMigrations(); + await this.refreshMigrations(); }); } @@ -267,7 +267,7 @@ export class DashboardWidget { this._viewAllMigrationsButton.enabled = false; this._migrationStatusCardLoadingContainer.loading = true; try { - this.setCurrentMigrations(await this.getMigrations()); + await this.setCurrentMigrations(await this.getMigrations()); const migrations = await this.getCurrentMigrations(); const inProgressMigrations = filterMigrations(migrations, AdsMigrationStatus.ONGOING); let warningCount = 0; @@ -663,17 +663,17 @@ export class DashboardWidget { let accounts = await azdata.accounts.getAllAccounts(); if (accounts.length !== 0) { - addAccountImage.updateCssStyles({ + await addAccountImage.updateCssStyles({ 'display': 'none' }); - addAccountText.updateCssStyles({ + await addAccountText.updateCssStyles({ 'display': 'none' }); - addAccountButton.updateCssStyles({ + await addAccountButton.updateCssStyles({ 'display': 'none' }); - this._migrationStatusCardsContainer.updateCssStyles({ 'visibility': 'visible' }); - this._viewAllMigrationsButton.updateCssStyles({ 'visibility': 'visible' }); + await this._migrationStatusCardsContainer.updateCssStyles({ 'visibility': 'visible' }); + await this._viewAllMigrationsButton.updateCssStyles({ 'visibility': 'visible' }); } await this.refreshMigrations(); })); @@ -692,17 +692,17 @@ export class DashboardWidget { let accounts = await azdata.accounts.getAllAccounts(); if (accounts.length === 0) { - addAccountImage.updateCssStyles({ + await addAccountImage.updateCssStyles({ 'display': 'block' }); - addAccountText.updateCssStyles({ + await addAccountText.updateCssStyles({ 'display': 'block' }); - addAccountButton.updateCssStyles({ + await addAccountButton.updateCssStyles({ 'display': 'block' }); - this._migrationStatusCardsContainer.updateCssStyles({ 'visibility': 'hidden' }); - this._viewAllMigrationsButton.updateCssStyles({ 'visibility': 'hidden' }); + await this._migrationStatusCardsContainer.updateCssStyles({ 'visibility': 'hidden' }); + await this._viewAllMigrationsButton.updateCssStyles({ 'visibility': 'hidden' }); } this._inProgressMigrationButton = this.createStatusCard( @@ -774,7 +774,7 @@ export class DashboardWidget { loc.MIGRATION_NOT_STARTED ); this._disposables.push(this._notStartedMigrationCard.container.onDidClick((e) => { - vscode.window.showInformationMessage('Feature coming soon'); + void vscode.window.showInformationMessage('Feature coming soon'); })); this._migrationStatusCardLoadingContainer = view.modelBuilder.loadingComponent().withItem(this._migrationStatusCardsContainer).component(); diff --git a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts index 41249225f9..4bd787b94c 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts @@ -89,7 +89,7 @@ export class AssessmentResultsDialog { } else { this._model._miDbs = this._tree.selectedDbs(); } - this._skuRecommendationPage.refreshCardText(); + await this._skuRecommendationPage.refreshCardText(); this.model.refreshDatabaseBackupPage = true; this._isOpen = false; } diff --git a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts index b65abc184c..c4c849b6fb 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts @@ -183,8 +183,8 @@ export class SqlDatabaseTree { } ).component(); - this._disposables.push(this._databaseTable.onDataChanged(() => { - this._databaseCount.updateProperties({ + this._disposables.push(this._databaseTable.onDataChanged(async () => { + await this._databaseCount.updateProperties({ 'value': constants.DATABASES(this.selectedDbs().length, this._model._databaseAssessment.length) }); })); @@ -198,10 +198,10 @@ export class SqlDatabaseTree { this._dbName.value = this._dbNames[e.row]; this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues.length); this._recommendation.value = constants.ISSUES_DETAILS; - this._resultComponent.updateCssStyles({ + await this._resultComponent.updateCssStyles({ 'display': 'block' }); - this._dbMessageContainer.updateCssStyles({ + await this._dbMessageContainer.updateCssStyles({ 'display': 'none' }); await this.refreshResults(); @@ -296,10 +296,10 @@ export class SqlDatabaseTree { this._disposables.push(this._instanceTable.onRowSelected(async (e) => { this._activeIssues = this._model._assessmentResults?.issues; this._dbName.value = this._serverName; - this._resultComponent.updateCssStyles({ + await this._resultComponent.updateCssStyles({ 'display': 'block' }); - this._dbMessageContainer.updateCssStyles({ + await this._dbMessageContainer.updateCssStyles({ 'display': 'none' }); this._recommendation.value = constants.WARNINGS_DETAILS; @@ -778,37 +778,37 @@ export class SqlDatabaseTree { if (this._targetType === MigrationTargetType.SQLMI) { if (this._activeIssues.length === 0) { /// show no issues here - this._assessmentsTable.updateCssStyles({ + await this._assessmentsTable.updateCssStyles({ 'display': 'none', 'border-right': 'none' }); - this._assessmentContainer.updateCssStyles({ + await this._assessmentContainer.updateCssStyles({ 'display': 'none' }); - this._noIssuesContainer.updateCssStyles({ + await this._noIssuesContainer.updateCssStyles({ 'display': 'flex' }); } else { - this._assessmentContainer.updateCssStyles({ + await this._assessmentContainer.updateCssStyles({ 'display': 'flex' }); - this._assessmentsTable.updateCssStyles({ + await this._assessmentsTable.updateCssStyles({ 'display': 'flex', 'border-right': 'solid 1px' }); - this._noIssuesContainer.updateCssStyles({ + await this._noIssuesContainer.updateCssStyles({ 'display': 'none' }); } } else { - this._assessmentsTable.updateCssStyles({ + await this._assessmentsTable.updateCssStyles({ 'display': 'none', 'border-right': 'none' }); - this._assessmentContainer.updateCssStyles({ + await this._assessmentContainer.updateCssStyles({ 'display': 'none' }); - this._noIssuesContainer.updateCssStyles({ + await this._noIssuesContainer.updateCssStyles({ 'display': 'flex' }); this._recommendationTitle.value = constants.ASSESSMENT_RESULTS; diff --git a/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts b/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts index ff82eac962..0ae2da4c9c 100644 --- a/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts +++ b/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts @@ -65,8 +65,8 @@ export class CreateResourceGroupDialog { return valid; }).component(); - this._disposables.push(resourceGroupName.onTextChanged(e => { - errorBox.updateCssStyles({ + this._disposables.push(resourceGroupName.onTextChanged(async e => { + await errorBox.updateCssStyles({ 'display': 'none' }); })); @@ -78,7 +78,7 @@ export class CreateResourceGroupDialog { }).component(); this._disposables.push(okButton.onDidClick(async e => { - errorBox.updateCssStyles({ + await errorBox.updateCssStyles({ 'display': 'none' }); okButton.enabled = false; @@ -88,12 +88,12 @@ export class CreateResourceGroupDialog { const resourceGroup = await createResourceGroup(this._azureAccount, this._subscription, resourceGroupName.value!, this._location); this._creationEvent.emit('done', resourceGroup); } catch (e) { - errorBox.updateCssStyles({ + await errorBox.updateCssStyles({ 'display': 'inline' }); errorBox.text = e.toString(); cancelButton.enabled = true; - resourceGroupName.validate(); + await resourceGroupName.validate(); } finally { loading.loading = false; } @@ -182,8 +182,8 @@ export class CreateResourceGroupDialog { d => { try { d.dispose(); } catch { } }); })); - return view.initializeModel(form).then(v => { - resourceGroupName.focus(); + return view.initializeModel(form).then(async v => { + await resourceGroupName.focus(); }); }); this._dialogObject.okButton.label = constants.APPLY; diff --git a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts index 0880b676dc..d98f11a04a 100644 --- a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts +++ b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts @@ -158,8 +158,8 @@ export class CreateSqlMigrationServiceDialog { d => { try { d.dispose(); } catch { } }); })); - return view.initializeModel(form).then(() => { - this.populateSubscriptions(); + return view.initializeModel(form).then(async () => { + await this.populateSubscriptions(); }); }); @@ -167,15 +167,15 @@ export class CreateSqlMigrationServiceDialog { this._testConnectionButton.hidden = true; this._disposables.push(this._testConnectionButton.onClick(async (e) => { this._refreshLoadingComponent.loading = true; - this._connectionStatus.updateCssStyles({ + await this._connectionStatus.updateCssStyles({ 'display': 'none' }); try { await this.refreshStatus(); } catch (e) { - vscode.window.showErrorMessage(e); + void vscode.window.showErrorMessage(e); } - this._connectionStatus.updateCssStyles({ + await this._connectionStatus.updateCssStyles({ 'display': 'inline' }); this._refreshLoadingComponent.loading = false; @@ -275,7 +275,7 @@ export class CreateSqlMigrationServiceDialog { name: createdResourceGroup.name }; this.migrationServiceResourceGroupDropdown.loading = false; - this.migrationServiceResourceGroupDropdown.focus(); + await this.migrationServiceResourceGroupDropdown.focus(); } })); @@ -547,7 +547,7 @@ export class CreateSqlMigrationServiceDialog { const state = migrationServiceStatus.properties.integrationRuntimeState; if (state === 'Online') { - this._connectionStatus.updateProperties({ + await this._connectionStatus.updateProperties({ text: constants.SERVICE_READY(this._createdMigrationService!.name, this.irNodes.join(', ')), style: 'success', CSSStyles: { @@ -557,7 +557,7 @@ export class CreateSqlMigrationServiceDialog { this._dialogObject.okButton.enabled = true; } else { this._connectionStatus.text = constants.SERVICE_NOT_READY(this._createdMigrationService!.name); - this._connectionStatus.updateProperties({ + await this._connectionStatus.updateProperties({ text: constants.SERVICE_NOT_READY(this._createdMigrationService!.name), style: 'warning', CSSStyles: { @@ -581,9 +581,9 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.COPY_KEY1, }).component(); - this._disposables.push(this._copyKey1Button.onDidClick((e) => { - vscode.env.clipboard.writeText(this.migrationServiceAuthKeyTable.dataValues![0][1].value); - vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); + this._disposables.push(this._copyKey1Button.onDidClick(async (e) => { + await vscode.env.clipboard.writeText(this.migrationServiceAuthKeyTable.dataValues![0][1].value); + void vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); })); this._copyKey2Button = this._view.modelBuilder.button().withProps({ @@ -592,9 +592,9 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.COPY_KEY2, }).component(); - this._disposables.push(this._copyKey2Button.onDidClick((e) => { - vscode.env.clipboard.writeText(this.migrationServiceAuthKeyTable.dataValues![1][1].value); - vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); + this._disposables.push(this._copyKey2Button.onDidClick(async (e) => { + await vscode.env.clipboard.writeText(this.migrationServiceAuthKeyTable.dataValues![1][1].value); + void vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); })); this._refreshKey1Button = this._view.modelBuilder.button().withProps({ @@ -617,7 +617,7 @@ export class CreateSqlMigrationServiceDialog { //TODO: add refresh logic })); - this.migrationServiceAuthKeyTable.updateProperties({ + await this.migrationServiceAuthKeyTable.updateProperties({ dataValues: [ [ { diff --git a/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts b/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts index d3210532d5..3daec4cb3d 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts @@ -115,9 +115,9 @@ export class ConfirmCutoverDialog { this._dialogObject.okButton.enabled = false; this._dialogObject.okButton.label = constants.COMPLETE_CUTOVER; - this._disposables.push(this._dialogObject.okButton.onClick((e) => { - this.migrationCutoverModel.startCutover(); - vscode.window.showInformationMessage(constants.CUTOVER_IN_PROGRESS(this.migrationCutoverModel._migration.migrationContext.properties.sourceDatabaseName)); + this._disposables.push(this._dialogObject.okButton.onClick(async (e) => { + await this.migrationCutoverModel.startCutover(); + void vscode.window.showInformationMessage(constants.CUTOVER_IN_PROGRESS(this.migrationCutoverModel._migration.migrationContext.properties.sourceDatabaseName)); })); const formBuilder = view.modelBuilder.formContainer().withFormItems( @@ -235,14 +235,14 @@ export class ConfirmCutoverDialog { if (expanded) { containerHeading.iconPath = IconPathHelper.expandButtonClosed; containerHeading.iconHeight = 12; - fileTable.updateCssStyles({ + await fileTable.updateCssStyles({ 'display': 'none' }); } else { containerHeading.iconPath = IconPathHelper.expandButtonOpen; containerHeading.iconHeight = 8; - fileTable.updateCssStyles({ + await fileTable.updateCssStyles({ 'display': 'inline' }); } diff --git a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts index f0b1f8092a..3d73aec3f4 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts @@ -6,10 +6,10 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import { IconPathHelper } from '../../constants/iconPathHelper'; -import { MigrationContext, MigrationStatus } from '../../models/migrationLocalStorage'; +import { BackupFileInfoStatus, MigrationContext, MigrationStatus } from '../../models/migrationLocalStorage'; import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel'; import * as loc from '../../constants/strings'; -import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, getMigrationStatusImage, SupportedAutoRefreshIntervals, clearDialogMessage } from '../../api/utils'; +import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, getMigrationStatusImage, SupportedAutoRefreshIntervals, clearDialogMessage, displayDialogErrorMessage } from '../../api/utils'; import { EOL } from 'os'; import { ConfirmCutoverDialog } from './confirmCutoverDialog'; @@ -201,7 +201,7 @@ export class MigrationCutoverDialog { let formItems = [ { component: this.migrationContainerHeader() }, { component: this._view.modelBuilder.separator().withProps({ width: 1000 }).component() }, - { component: this.migrationInfoGrid() }, + { component: await this.migrationInfoGrid() }, { component: this._view.modelBuilder.separator().withProps({ width: 1000 }).component() }, { component: this._fileCount }, { component: this._fileTable }, @@ -316,6 +316,10 @@ export class MigrationCutoverDialog { const dialog = new ConfirmCutoverDialog(this._model); await dialog.initialize(); await this.refreshStatus(); + + if (this._model.CutoverError) { + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CUTOVER_ERROR, this._model.CutoverError); + } })); headerActions.addItem(this._cutoverButton, { @@ -336,10 +340,13 @@ export class MigrationCutoverDialog { }).component(); this._disposables.push(this._cancelButton.onDidClick((e) => { - vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, { modal: true }, loc.YES, loc.NO).then(async (v) => { + void vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, { modal: true }, loc.YES, loc.NO).then(async (v) => { if (v === loc.YES) { await this._model.cancelMigration(); await this.refreshStatus(); + if (this._model.CancelMigrationError) { + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CANCELLATION_ERROR, this._model.CancelMigrationError); + } } }); })); @@ -382,9 +389,9 @@ export class MigrationCutoverDialog { this._disposables.push(this._copyDatabaseMigrationDetails.onDidClick(async (e) => { await this.refreshStatus(); - vscode.env.clipboard.writeText(this.getMigrationDetails()); + await vscode.env.clipboard.writeText(this.getMigrationDetails()); - vscode.window.showInformationMessage(loc.DETAILS_COPIED); + void vscode.window.showInformationMessage(loc.DETAILS_COPIED); })); headerActions.addItem(this._copyDatabaseMigrationDetails, { @@ -451,7 +458,7 @@ export class MigrationCutoverDialog { return header; } - private migrationInfoGrid(): azdata.FlexContainer { + private async migrationInfoGrid(): Promise { const addInfoFieldToContainer = (infoField: InfoFieldSchema, container: azdata.FlexContainer): void => { container.addItem(infoField.flexContainer, { CSSStyles: { @@ -463,9 +470,9 @@ export class MigrationCutoverDialog { const flexServer = this._view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this._sourceDatabaseInfoField = this.createInfoField(loc.SOURCE_DATABASE, ''); - this._sourceDetailsInfoField = this.createInfoField(loc.SOURCE_SERVER, ''); - this._sourceVersionInfoField = this.createInfoField(loc.SOURCE_VERSION, ''); + this._sourceDatabaseInfoField = await this.createInfoField(loc.SOURCE_DATABASE, ''); + this._sourceDetailsInfoField = await this.createInfoField(loc.SOURCE_SERVER, ''); + this._sourceVersionInfoField = await this.createInfoField(loc.SOURCE_VERSION, ''); addInfoFieldToContainer(this._sourceDatabaseInfoField, flexServer); addInfoFieldToContainer(this._sourceDetailsInfoField, flexServer); addInfoFieldToContainer(this._sourceVersionInfoField, flexServer); @@ -473,9 +480,9 @@ export class MigrationCutoverDialog { const flexTarget = this._view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this._targetDatabaseInfoField = this.createInfoField(loc.TARGET_DATABASE_NAME, ''); - this._targetServerInfoField = this.createInfoField(loc.TARGET_SERVER, ''); - this._targetVersionInfoField = this.createInfoField(loc.TARGET_VERSION, ''); + this._targetDatabaseInfoField = await this.createInfoField(loc.TARGET_DATABASE_NAME, ''); + this._targetServerInfoField = await this.createInfoField(loc.TARGET_SERVER, ''); + this._targetVersionInfoField = await this.createInfoField(loc.TARGET_VERSION, ''); addInfoFieldToContainer(this._targetDatabaseInfoField, flexTarget); addInfoFieldToContainer(this._targetServerInfoField, flexTarget); addInfoFieldToContainer(this._targetVersionInfoField, flexTarget); @@ -484,9 +491,9 @@ export class MigrationCutoverDialog { const flexStatus = this._view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this._migrationStatusInfoField = this.createInfoField(loc.MIGRATION_STATUS, '', false, ' '); - this._fullBackupFileOnInfoField = this.createInfoField(loc.FULL_BACKUP_FILES, '', isBlobMigration); - this._backupLocationInfoField = this.createInfoField(loc.BACKUP_LOCATION, ''); + this._migrationStatusInfoField = await this.createInfoField(loc.MIGRATION_STATUS, '', false, ' '); + this._fullBackupFileOnInfoField = await this.createInfoField(loc.FULL_BACKUP_FILES, '', isBlobMigration); + this._backupLocationInfoField = await this.createInfoField(loc.BACKUP_LOCATION, ''); addInfoFieldToContainer(this._migrationStatusInfoField, flexStatus); addInfoFieldToContainer(this._fullBackupFileOnInfoField, flexStatus); addInfoFieldToContainer(this._backupLocationInfoField, flexStatus); @@ -494,9 +501,9 @@ export class MigrationCutoverDialog { const flexFile = this._view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); - this._lastLSNInfoField = this.createInfoField(loc.LAST_APPLIED_LSN, '', isBlobMigration); - this._lastAppliedBackupInfoField = this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES, ''); - this._lastAppliedBackupTakenOnInfoField = this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES_TAKEN_ON, '', isBlobMigration); + this._lastLSNInfoField = await this.createInfoField(loc.LAST_APPLIED_LSN, '', isBlobMigration); + this._lastAppliedBackupInfoField = await this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES, ''); + this._lastAppliedBackupTakenOnInfoField = await this.createInfoField(loc.LAST_APPLIED_BACKUP_FILES_TAKEN_ON, '', isBlobMigration); addInfoFieldToContainer(this._lastLSNInfoField, flexFile); addInfoFieldToContainer(this._lastAppliedBackupInfoField, flexFile); addInfoFieldToContainer(this._lastAppliedBackupTakenOnInfoField, flexFile); @@ -558,7 +565,7 @@ export class MigrationCutoverDialog { clearDialogMessage(this._dialogObject); if (this._isOnlineMigration()) { - this._cutoverButton.updateCssStyles({ + await this._cutoverButton.updateCssStyles({ 'display': 'inline' }); } @@ -660,22 +667,22 @@ export class MigrationCutoverDialog { this._lastAppliedBackupTakenOnInfoField.text.value = lastAppliedBackupFileTakenOn! ? convertIsoTimeToLocalTime(lastAppliedBackupFileTakenOn).toLocaleString() : '-'; if (this._shouldDisplayBackupFileTable()) { - this._fileCount.updateCssStyles({ + await this._fileCount.updateCssStyles({ display: 'inline' }); - this._fileTable.updateCssStyles({ + await this._fileTable.updateCssStyles({ display: 'inline' }); this._fileCount.value = loc.ACTIVE_BACKUP_FILES_ITEMS(tableData.length); if (tableData.length === 0) { - this._emptyTableFill.updateCssStyles({ + await this._emptyTableFill.updateCssStyles({ 'display': 'flex' }); this._fileTable.height = '50px'; } else { - this._emptyTableFill.updateCssStyles({ + await this._emptyTableFill.updateCssStyles({ 'display': 'none' }); this._fileTable.height = '300px'; @@ -698,25 +705,21 @@ export class MigrationCutoverDialog { } } + this._cutoverButton.enabled = false; if (migrationStatusTextValue === MigrationStatus.InProgress) { - const restoredCount = (this._model.migrationStatus.properties.migrationStatusDetails?.activeBackupSets?.filter(a => a.listOfBackupFiles[0].status === 'Restored'))?.length ?? 0; + const restoredCount = this._model.migrationStatus.properties.migrationStatusDetails?.activeBackupSets?.filter( + (a) => a.listOfBackupFiles[0].status === BackupFileInfoStatus.Restored)?.length ?? 0; + if (restoredCount > 0 || isBlobMigration) { this._cutoverButton.enabled = true; } - } else { - this._cutoverButton.enabled = false; } this._cancelButton.enabled = migrationStatusTextValue === MigrationStatus.Creating || migrationStatusTextValue === MigrationStatus.InProgress; - } catch (e) { - this._dialogObject.message = { - level: azdata.window.MessageLevel.Error, - text: loc.MIGRATION_STATUS_REFRESH_ERROR, - description: e.message - }; + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e); console.log(e); } finally { this.isRefreshing = false; @@ -724,17 +727,17 @@ export class MigrationCutoverDialog { } } - private createInfoField(label: string, value: string, defaultHidden: boolean = false, iconPath?: azdata.IconPath): { + private async createInfoField(label: string, value: string, defaultHidden: boolean = false, iconPath?: azdata.IconPath): Promise<{ flexContainer: azdata.FlexContainer, text: azdata.TextComponent, icon?: azdata.ImageComponent - } { + }> { const flexContainer = this._view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); if (defaultHidden) { - flexContainer.updateCssStyles({ + await flexContainer.updateCssStyles({ 'display': 'none' }); } diff --git a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts index d4a0e803ce..abe3dfcb9a 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialogModel.ts @@ -4,12 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { getMigrationStatus, DatabaseMigration, startMigrationCutover, stopMigration, getMigrationAsyncOperationDetails, AzureAsyncOperationResource, BackupFileInfo, getResourceGroupFromId } from '../../api/azure'; -import { MigrationContext } from '../../models/migrationLocalStorage'; +import { BackupFileInfoStatus, MigrationContext } from '../../models/migrationLocalStorage'; import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../../telemtery'; import * as constants from '../../constants/strings'; import { getMigrationTargetType, getMigrationMode } from '../../constants/helper'; export class MigrationCutoverDialogModel { + public CutoverError?: Error; + public CancelMigrationError?: Error; public migrationStatus!: DatabaseMigration; public migrationOpStatus!: AzureAsyncOperationResource; @@ -47,6 +49,7 @@ export class MigrationCutoverDialogModel { public async startCutover(): Promise { try { + this.CutoverError = undefined; if (this.migrationStatus) { const cutover = await startMigrationCutover( this._migration.azureAccount, @@ -66,6 +69,7 @@ export class MigrationCutoverDialogModel { return cutover; } } catch (error) { + this.CutoverError = error; console.log(error); } return undefined!; @@ -73,6 +77,7 @@ export class MigrationCutoverDialogModel { public async cancelMigration(): Promise { try { + this.CancelMigrationError = undefined; if (this.migrationStatus) { const cutoverStartTime = new Date().toString(); await stopMigration( @@ -93,6 +98,7 @@ export class MigrationCutoverDialogModel { ); } } catch (error) { + this.CancelMigrationError = error; console.log(error); } return undefined!; @@ -126,7 +132,7 @@ export class MigrationCutoverDialogModel { const files: BackupFileInfo[] = []; this.migrationStatus.properties.migrationStatusDetails?.activeBackupSets?.forEach(abs => { abs.listOfBackupFiles.forEach(f => { - if (f.status !== 'Restored') { + if (f.status !== BackupFileInfoStatus.Restored) { files.push(f); } }); diff --git a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts index 492af5d2fc..db0ee16a41 100644 --- a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts @@ -10,7 +10,7 @@ import { MigrationContext, MigrationLocalStorage, MigrationStatus } from '../../ import { MigrationCutoverDialog } from '../migrationCutover/migrationCutoverDialog'; import { AdsMigrationStatus, MigrationStatusDialogModel } from './migrationStatusDialogModel'; import * as loc from '../../constants/strings'; -import { clearDialogMessage, convertTimeDifferenceToDuration, filterMigrations, getMigrationStatusImage, SupportedAutoRefreshIntervals } from '../../api/utils'; +import { clearDialogMessage, convertTimeDifferenceToDuration, displayDialogErrorMessage, filterMigrations, getMigrationStatusImage, SupportedAutoRefreshIntervals } from '../../api/utils'; import { SqlMigrationServiceDetailsDialog } from '../sqlMigrationService/sqlMigrationServiceDetailsDialog'; import { ConfirmCutoverDialog } from '../migrationCutover/confirmCutoverDialog'; import { MigrationCutoverDialogModel } from '../migrationCutover/migrationCutoverDialogModel'; @@ -103,8 +103,8 @@ export class MigrationStatusDialog { width: '360px' }).component(); - this._disposables.push(this._searchBox.onTextChanged((value) => { - this.populateMigrationTable(); + this._disposables.push(this._searchBox.onTextChanged(async (value) => { + await this.populateMigrationTable(); })); this._refresh = this._view.modelBuilder.button().withProps({ @@ -136,8 +136,8 @@ export class MigrationStatusDialog { width: '220px' }).component(); - this._disposables.push(this._statusDropdown.onValueChanged((value) => { - this.populateMigrationTable(); + this._disposables.push(this._statusDropdown.onValueChanged(async (value) => { + await this.populateMigrationTable(); })); if (this._filter) { @@ -204,16 +204,14 @@ export class MigrationStatusDialog { await cutoverDialogModel.fetchStatus(); const dialog = new ConfirmCutoverDialog(cutoverDialogModel); await dialog.initialize(); + if (cutoverDialogModel.CutoverError) { + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CUTOVER_ERROR, cutoverDialogModel.CutoverError); + } } else { await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_CUTOVER); } } catch (e) { - this._dialogObject.message = { - text: loc.MIGRATION_STATUS_REFRESH_ERROR, - description: e.message, - level: azdata.window.MessageLevel.Error - }; - + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CUTOVER_ERROR, e); console.log(e); } })); @@ -273,12 +271,7 @@ export class MigrationStatusDialog { await vscode.window.showInformationMessage(loc.DETAILS_COPIED); } catch (e) { - this._dialogObject.message = { - text: loc.MIGRATION_STATUS_REFRESH_ERROR, - description: e.message, - level: azdata.window.MessageLevel.Error - }; - + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e); console.log(e); } })); @@ -290,23 +283,22 @@ export class MigrationStatusDialog { clearDialogMessage(this._dialogObject); const migration = this._model._migrations.find(migration => migration.migrationContext.id === migrationId); if (this.canCancelMigration(migration?.migrationContext.properties.migrationStatus)) { - vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, loc.YES, loc.NO).then(async (v) => { + void vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, loc.YES, loc.NO).then(async (v) => { if (v === loc.YES) { const cutoverDialogModel = new MigrationCutoverDialogModel(migration!); await cutoverDialogModel.fetchStatus(); await cutoverDialogModel.cancelMigration(); + + if (cutoverDialogModel.CancelMigrationError) { + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CANNOT_CANCEL, cutoverDialogModel.CancelMigrationError); + } } }); } else { await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_CANCEL); } } catch (e) { - this._dialogObject.message = { - text: loc.MIGRATION_CANCELLATION_ERROR, - description: e.message, - level: azdata.window.MessageLevel.Error - }; - + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_CANCELLATION_ERROR, e); console.log(e); } })); @@ -508,12 +500,7 @@ export class MigrationStatusDialog { this._model._migrations = await MigrationLocalStorage.getMigrationsBySourceConnections(currentConnection, true); await this.populateMigrationTable(); } catch (e) { - this._dialogObject.message = { - text: loc.MIGRATION_STATUS_REFRESH_ERROR, - description: e.message, - level: azdata.window.MessageLevel.Error - }; - + displayDialogErrorMessage(this._dialogObject, loc.MIGRATION_STATUS_REFRESH_ERROR, e); console.log(e); } finally { this.isRefreshing = false; diff --git a/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts b/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts index 9c30b16253..4a06acf152 100644 --- a/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts +++ b/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts @@ -249,9 +249,9 @@ export class SqlMigrationServiceDetailsDialog { }) .component(); - this._disposables.push(copyKey1Button.onDidClick((e) => { - vscode.env.clipboard.writeText(keys.authKey1); - vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); + this._disposables.push(copyKey1Button.onDidClick(async (e) => { + await vscode.env.clipboard.writeText(keys.authKey1); + void vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); })); const copyKey2Button = view.modelBuilder @@ -265,9 +265,9 @@ export class SqlMigrationServiceDetailsDialog { }) .component(); - this._disposables.push(copyKey2Button.onDidClick((e) => { - vscode.env.clipboard.writeText(keys.authKey2); - vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); + this._disposables.push(copyKey2Button.onDidClick(async (e) => { + await vscode.env.clipboard.writeText(keys.authKey2); + void vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); })); const refreshKey1Button = view.modelBuilder @@ -296,7 +296,7 @@ export class SqlMigrationServiceDetailsDialog { this._disposables.push(refreshKey2Button.onDidClick( async (e) => await this._regenerateAuthKey(view, migrationContext, AUTH_KEY2))); - this._migrationServiceAuthKeyTable.updateProperties({ + await this._migrationServiceAuthKeyTable.updateProperties({ dataValues: [ [ { value: constants.SERVICE_KEY1_LABEL }, diff --git a/extensions/sql-migration/src/main.ts b/extensions/sql-migration/src/main.ts index 2824bd6a44..39bae28f30 100644 --- a/extensions/sql-migration/src/main.ts +++ b/extensions/sql-migration/src/main.ts @@ -40,13 +40,13 @@ class SQLMigration { const selectedNotebook = input.selectedItems[0]; if (selectedNotebook) { try { - azdata.nb.showNotebookDocument(vscode.Uri.parse(`untitled: ${selectedNotebook.label}`), { + await azdata.nb.showNotebookDocument(vscode.Uri.parse(`untitled: ${selectedNotebook.label}`), { preview: false, initialContent: (await fs.readFile(selectedNotebook.notebookPath)).toString(), initialDirtyState: false }); } catch (e) { - vscode.window.showErrorMessage(`${loc.NOTEBOOK_OPEN_ERROR} - ${e.toString()}`); + void vscode.window.showErrorMessage(`${loc.NOTEBOOK_OPEN_ERROR} - ${e.toString()}`); } input.hide(); } diff --git a/extensions/sql-migration/src/models/migrationLocalStorage.ts b/extensions/sql-migration/src/models/migrationLocalStorage.ts index 4e83af1985..fe59b9820e 100644 --- a/extensions/sql-migration/src/models/migrationLocalStorage.ts +++ b/extensions/sql-migration/src/models/migrationLocalStorage.ts @@ -59,7 +59,7 @@ export class MigrationLocalStorage { } validMigrations.push(migration); } - this.context.globalState.update(this.mementoToken, validMigrations); + await this.context.globalState.update(this.mementoToken, validMigrations); return result; } @@ -77,7 +77,7 @@ export class MigrationLocalStorage { } } - public static saveMigration( + public static async saveMigration( connectionProfile: azdata.connection.ConnectionProfile, migrationContext: DatabaseMigration, targetMI: SqlManagedInstance | SqlVMServer, @@ -85,7 +85,7 @@ export class MigrationLocalStorage { subscription: azureResource.AzureResourceSubscription, controller: SqlMigrationService, asyncURL: string, - sessionId: string): void { + sessionId: string): Promise { try { let migrationMementos: MigrationContext[] = this.context.globalState.get(this.mementoToken) || []; migrationMementos = migrationMementos.filter(m => m.migrationContext.id !== migrationContext.id); @@ -99,14 +99,14 @@ export class MigrationLocalStorage { asyncUrl: asyncURL, sessionId: sessionId }); - this.context.globalState.update(this.mementoToken, migrationMementos); + await this.context.globalState.update(this.mementoToken, migrationMementos); } catch (e) { console.log(e); } } - public static clearMigrations() { - this.context.globalState.update(this.mementoToken, ([] as MigrationContext[])); + public static async clearMigrations(): Promise { + await this.context.globalState.update(this.mementoToken, ([] as MigrationContext[])); } public static removeMigrationSecrets(migration: DatabaseMigration): DatabaseMigration { @@ -154,3 +154,13 @@ export enum ProvisioningState { Succeeded = 'Succeeded', Creating = 'Creating' } + +export enum BackupFileInfoStatus { + Arrived = 'Arrived', + Uploading = 'Uploading', + Uploaded = 'Uploaded', + Restoring = 'Restoring', + Restored = 'Restored', + Canceled = 'Canceled', + Ignored = 'Ignored' +} diff --git a/extensions/sql-migration/src/models/migrationWizardPage.ts b/extensions/sql-migration/src/models/migrationWizardPage.ts index a6fcdfca6d..c47ffdc653 100644 --- a/extensions/sql-migration/src/models/migrationWizardPage.ts +++ b/extensions/sql-migration/src/models/migrationWizardPage.ts @@ -43,7 +43,7 @@ export abstract class MigrationWizardPage { return this.handleStateChange(e); }); - this.enableQueueProcessor(); + await this.enableQueueProcessor(); } private queueActive = false; diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index b9f7441e66..3483181b0f 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -951,7 +951,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { } ); - MigrationLocalStorage.saveMigration( + await MigrationLocalStorage.saveMigration( currentConnection!, response.databaseMigration, this._targetServerInstance, @@ -961,15 +961,15 @@ export class MigrationStateModel implements Model, vscode.Disposable { response.asyncUrl, this._sessionId ); - vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', this._migrationDbs[i], this._targetServerInstance.name, this._targetDatabaseNames[i])); + void vscode.window.showInformationMessage(localize("sql.migration.starting.migration.message", 'Starting migration for database {0} to {1} - {2}', this._migrationDbs[i], this._targetServerInstance.name, this._targetDatabaseNames[i])); } } catch (e) { - vscode.window.showErrorMessage( + void vscode.window.showErrorMessage( localize('sql.migration.starting.migration.error', "An error occurred while starting the migration: '{0}'", e.message)); console.log(e); } - vscode.commands.executeCommand('sqlmigration.refreshMigrationTiles'); + await vscode.commands.executeCommand('sqlmigration.refreshMigrationTiles'); } } } diff --git a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts index 2b20bb3f53..0b0adabfa9 100644 --- a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts @@ -88,11 +88,11 @@ export class AccountsSelectionPage extends MigrationWizardPage { this.migrationStateModel._accountTenants = selectedAzureAccount.properties.tenants; this._accountTenantDropdown.values = await this.migrationStateModel.getTenantValues(); selectDropDownIndex(this._accountTenantDropdown, 0); - this._accountTenantFlexContainer.updateCssStyles({ + await this._accountTenantFlexContainer.updateCssStyles({ 'display': 'inline' }); } else { - this._accountTenantFlexContainer.updateCssStyles({ + await this._accountTenantFlexContainer.updateCssStyles({ 'display': 'none' }); } @@ -119,7 +119,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { this.wizard.message = { text: '' }; - this._azureAccountsDropdown.validate(); + await this._azureAccountsDropdown.validate(); })); const flexContainer = view.modelBuilder.flexContainer() diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts index 2936786d75..0af5a416e4 100644 --- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts +++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts @@ -126,9 +126,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { } }).component(); - this._disposables.push(this._networkShareButton.onDidChangeCheckedState((e) => { + this._disposables.push(this._networkShareButton.onDidChangeCheckedState(async (e) => { if (e) { - this.switchNetworkContainerFields(NetworkContainerType.NETWORK_SHARE); + await this.switchNetworkContainerFields(NetworkContainerType.NETWORK_SHARE); } })); @@ -141,9 +141,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { } }).component(); - this._disposables.push(this._blobContainerButton.onDidChangeCheckedState((e) => { + this._disposables.push(this._blobContainerButton.onDidChangeCheckedState(async (e) => { if (e) { - this.switchNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER); + await this.switchNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER); } })); @@ -273,8 +273,8 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - this._disposables.push(this._networkSharePath.onTextChanged((value) => { - this.validateFields(); + this._disposables.push(this._networkSharePath.onTextChanged(async (value) => { + await this.validateFields(); this.migrationStateModel._databaseBackup.networkShare.networkShareLocation = value; })); @@ -745,14 +745,14 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._networkShareButton.checked = false; this._networkTableContainer.display = 'none'; - this._networkShareContainer.updateCssStyles({ 'display': 'none' }); + await this._networkShareContainer.updateCssStyles({ 'display': 'none' }); this._blobContainerButton.checked = false; this._blobTableContainer.display = 'none'; - this._blobContainer.updateCssStyles({ 'display': 'none' }); + await this._blobContainer.updateCssStyles({ 'display': 'none' }); - this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' }); - this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' }); + await this._targetDatabaseContainer.updateCssStyles({ 'display': 'none' }); + await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': 'none' }); const connectionProfile = await this.migrationStateModel.getSourceConnectionProfile(); const queryProvider = azdata.dataprotocol.getProvider((await this.migrationStateModel.getSourceConnectionProfile()).providerId, azdata.DataProviderType.QueryProvider); const query = 'select SUSER_NAME()'; @@ -798,9 +798,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - this._disposables.push(targetDatabaseInput.onTextChanged((value) => { + this._disposables.push(targetDatabaseInput.onTextChanged(async (value) => { this.migrationStateModel._targetDatabaseNames[index] = value.trim(); - this.validateFields(); + await this.validateFields(); })); this._networkShareTargetDatabaseNames.push(targetDatabaseInput); @@ -863,9 +863,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { if (selectedIndex > -1 && !blobResourceGroupErrorStrings.includes(value)) { this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); await this.loadBlobStorageDropdown(index); - blobContainerStorageAccountDropdown.updateProperties({ enabled: true }); + await blobContainerStorageAccountDropdown.updateProperties({ enabled: true }); } else { - this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP); + await this.disableBlobTableDropdowns(index, constants.RESOURCE_GROUP); } })); this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown); @@ -875,9 +875,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { if (selectedIndex > -1 && !blobStorageAccountErrorStrings.includes(value)) { this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex); await this.loadBlobContainerDropdown(index); - blobContainerDropdown.updateProperties({ enabled: true }); + await blobContainerDropdown.updateProperties({ enabled: true }); } else { - this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT); + await this.disableBlobTableDropdowns(index, constants.STORAGE_ACCOUNT); } })); this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown); @@ -888,10 +888,10 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex); if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) { await this.loadBlobLastBackupFileDropdown(index); - blobContainerLastBackupFileDropdown.updateProperties({ enabled: true }); + await blobContainerLastBackupFileDropdown.updateProperties({ enabled: true }); } } else { - this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER); + await this.disableBlobTableDropdowns(index, constants.BLOB_CONTAINER); } })); this._blobContainerDropdowns.push(blobContainerDropdown); @@ -1062,7 +1062,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { protected async handleStateChange(e: StateChangeEvent): Promise { } - private switchNetworkContainerFields(containerType: NetworkContainerType): void { + private async switchNetworkContainerFields(containerType: NetworkContainerType): Promise { this.wizard.message = { text: '', level: azdata.window.MessageLevel.Error @@ -1070,10 +1070,10 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.wizard.nextButton.enabled = true; this.migrationStateModel._databaseBackup.networkContainerType = containerType; - this._blobContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none' }); - this._networkShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' }); - this._networkShareStorageAccountDetails.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' }); - this._targetDatabaseContainer.updateCssStyles({ 'display': 'inline' }); + await this._blobContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none' }); + await this._networkShareContainer.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' }); + await this._networkShareStorageAccountDetails.updateCssStyles({ 'display': (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none' }); + await this._targetDatabaseContainer.updateCssStyles({ 'display': 'inline' }); this._networkTableContainer.display = (containerType === NetworkContainerType.NETWORK_SHARE) ? 'inline' : 'none'; this._blobTableContainer.display = (containerType === NetworkContainerType.BLOB_CONTAINER) ? 'inline' : 'none'; @@ -1083,19 +1083,19 @@ export class DatabaseBackupPage extends MigrationWizardPage { this._blobContainerTargetDatabaseNames[index].value = v; }); - this._windowsUserAccountText.updateProperties({ + await this._windowsUserAccountText.updateProperties({ required: containerType === NetworkContainerType.NETWORK_SHARE }); - this._passwordText.updateProperties({ + await this._passwordText.updateProperties({ required: containerType === NetworkContainerType.NETWORK_SHARE }); - this._sqlSourceUsernameInput.updateProperties({ + await this._sqlSourceUsernameInput.updateProperties({ required: containerType === NetworkContainerType.NETWORK_SHARE }); - this._sqlSourcepassword.updateProperties({ + await this._sqlSourcepassword.updateProperties({ required: containerType === NetworkContainerType.NETWORK_SHARE }); - this.validateFields(); + await this.validateFields(); } @@ -1132,9 +1132,8 @@ export class DatabaseBackupPage extends MigrationWizardPage { this.migrationStateModel._databaseBackup.subscription = this.migrationStateModel._targetSubscription; - - this.loadNetworkStorageResourceGroup(); - this.loadBlobResourceGroup(); + await this.loadNetworkStorageResourceGroup(); + await this.loadBlobResourceGroup(); } private async loadNetworkStorageResourceGroup(): Promise { @@ -1219,24 +1218,24 @@ export class DatabaseBackupPage extends MigrationWizardPage { return v.value === undefined || errorStrings.includes((v.value)?.displayName); } - private disableBlobTableDropdowns(rowIndex: number, columnName: string): void { + private async disableBlobTableDropdowns(rowIndex: number, columnName: string): Promise { const dropdownProps = { enabled: false, loading: false }; const createDropdownValuesWithPrereq = (displayName: string, name: string = '') => [{ displayName, name }]; if (this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE) { this._blobContainerLastBackupFileDropdowns[rowIndex].values = createDropdownValuesWithPrereq(constants.SELECT_BLOB_CONTAINER); selectDropDownIndex(this._blobContainerLastBackupFileDropdowns[rowIndex], 0); - this._blobContainerLastBackupFileDropdowns[rowIndex]?.updateProperties(dropdownProps); + await this._blobContainerLastBackupFileDropdowns[rowIndex]?.updateProperties(dropdownProps); } if (columnName === constants.BLOB_CONTAINER) { return; } this._blobContainerDropdowns[rowIndex].values = createDropdownValuesWithPrereq(constants.SELECT_STORAGE_ACCOUNT); selectDropDownIndex(this._blobContainerDropdowns[rowIndex], 0); - this._blobContainerDropdowns[rowIndex].updateProperties(dropdownProps); + await this._blobContainerDropdowns[rowIndex].updateProperties(dropdownProps); if (columnName === constants.STORAGE_ACCOUNT) { return; } this._blobContainerStorageAccountDropdowns[rowIndex].values = createDropdownValuesWithPrereq(constants.SELECT_RESOURCE_GROUP); selectDropDownIndex(this._blobContainerStorageAccountDropdowns[rowIndex], 0); - this._blobContainerStorageAccountDropdowns[rowIndex].updateProperties(dropdownProps); + await this._blobContainerStorageAccountDropdowns[rowIndex].updateProperties(dropdownProps); } } diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts index 4e4f4a444d..607ffe4f66 100644 --- a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts +++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts @@ -288,8 +288,8 @@ export class DatabaseSelectorPage extends MigrationWizardPage { ).component(); await this._databaseSelectorTable.setDataValues(this._databaseTableValues); - this._disposables.push(this._databaseSelectorTable.onDataChanged(() => { - this._dbCount.updateProperties({ + this._disposables.push(this._databaseSelectorTable.onDataChanged(async () => { + await this._dbCount.updateProperties({ 'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length) }); })); diff --git a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts index 9b61b74928..ba38ce6142 100644 --- a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts +++ b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts @@ -106,7 +106,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this._subscription.value = this.migrationStateModel._targetSubscription.name; this._location.value = await getLocationDisplayName(this.migrationStateModel._targetServerInstance.location); this._dmsInfoContainer.display = (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE && this.migrationStateModel._sqlMigrationService) ? 'inline' : 'none'; - this.loadResourceGroupDropdown(); + await this.loadResourceGroupDropdown(); this.wizard.registerNavigationValidator((pageChangeInfo) => { if (pageChangeInfo.newPage < pageChangeInfo.lastPage) { this.wizard.message = { @@ -337,7 +337,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this._disposables.push(this._copy1.onDidClick(async (e) => { await vscode.env.clipboard.writeText(this._authKeyTable.dataValues![0][1].value); - vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); + void vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); })); this._copy2 = this._view.modelBuilder.button().withProps({ @@ -348,7 +348,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { this._disposables.push(this._copy2.onDidClick(async (e) => { await vscode.env.clipboard.writeText(this._authKeyTable.dataValues![1][1].value); - vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); + void vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); })); this._refresh1 = this._view.modelBuilder.button().withProps({ @@ -500,12 +500,12 @@ export class IntergrationRuntimePage extends MigrationWizardPage { const state = migrationService.properties.integrationRuntimeState; if (state === 'Online') { - this._dmsStatusInfoBox.updateProperties({ + await this._dmsStatusInfoBox.updateProperties({ text: constants.SERVICE_READY(this.migrationStateModel._sqlMigrationService!.name, this.migrationStateModel._nodeNames.join(', ')), style: 'success' }); } else { - this._dmsStatusInfoBox.updateProperties({ + await this._dmsStatusInfoBox.updateProperties({ text: constants.SERVICE_NOT_READY(this.migrationStateModel._sqlMigrationService!.name), style: 'error' }); diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 39848db8f1..06055fdd2d 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -92,8 +92,8 @@ export class SKURecommendationPage extends MigrationWizardPage { width: 130 }).component(); - this._disposables.push(refreshAssessmentButton.onDidClick(() => { - this.constructDetails(); + this._disposables.push(refreshAssessmentButton.onDidClick(async () => { + await this.constructDetails(); })); const chooseYourTargetText = this._view.modelBuilder.text().withProps({ @@ -225,10 +225,10 @@ export class SKURecommendationPage extends MigrationWizardPage { }); }); - this._disposables.push(this._rbg.onSelectionChanged((value) => { + this._disposables.push(this._rbg.onSelectionChanged(async (value) => { if (value) { this.assessmentGroupContainer.display = 'inline'; - this.changeTargetType(value.cardId); + await this.changeTargetType(value.cardId); } })); @@ -427,7 +427,7 @@ export class SKURecommendationPage extends MigrationWizardPage { } - private changeTargetType(newTargetType: string) { + private async changeTargetType(newTargetType: string) { // remove assessed databases that have been removed from the source selection list const miDbs = this.migrationStateModel._miDbs.filter( db => this.migrationStateModel._databaseAssessment.findIndex( @@ -452,7 +452,7 @@ export class SKURecommendationPage extends MigrationWizardPage { } this.migrationStateModel.refreshDatabaseBackupPage = true; this._targetContainer.display = (this.migrationStateModel._migrationDbs.length === 0) ? 'none' : 'inline'; - this.populateResourceInstanceDropdown(); + await this.populateResourceInstanceDropdown(); } private async constructDetails(): Promise { @@ -460,8 +460,8 @@ export class SKURecommendationPage extends MigrationWizardPage { text: '', level: azdata.window.MessageLevel.Error }; - this._assessmentComponent.updateCssStyles({ display: 'block' }); - this._formContainer.component().updateCssStyles({ display: 'none' }); + await this._assessmentComponent.updateCssStyles({ display: 'block' }); + await this._formContainer.component().updateCssStyles({ display: 'none' }); this._assessmentLoader.loading = true; const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName; @@ -499,10 +499,10 @@ errorId: ${e.errorId} console.log(e); } - this.refreshCardText(); + await this.refreshCardText(); this._assessmentLoader.loading = false; - this._assessmentComponent.updateCssStyles({ display: 'none' }); - this._formContainer.component().updateCssStyles({ display: 'block' }); + await this._assessmentComponent.updateCssStyles({ display: 'none' }); + await this._formContainer.component().updateCssStyles({ display: 'block' }); } private async populateSubscriptionDropdown(): Promise { @@ -612,14 +612,14 @@ errorId: ${e.errorId} if (this.migrationStateModel._runAssessments) { await this.constructDetails(); } - this._assessmentComponent.updateCssStyles({ + await this._assessmentComponent.updateCssStyles({ display: 'none' }); - this._formContainer.component().updateCssStyles({ + await this._formContainer.component().updateCssStyles({ display: 'block' }); - this.populateSubscriptionDropdown(); + await this.populateSubscriptionDropdown(); this.wizard.nextButton.enabled = true; } @@ -637,16 +637,16 @@ errorId: ${e.errorId} protected async handleStateChange(e: StateChangeEvent): Promise { } - public refreshDatabaseCount(selectedDbs: string[]): void { + public async refreshDatabaseCount(selectedDbs: string[]): Promise { this.wizard.message = { text: '', level: azdata.window.MessageLevel.Error }; this.migrationStateModel._migrationDbs = selectedDbs; - this.refreshCardText(); + await this.refreshCardText(); } - public refreshCardText(): void { + public async refreshCardText(): Promise { this._rbgLoader.loading = true; if (this._rbg.selectedCardId === MigrationTargetType.SQLMI) { this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs; @@ -667,20 +667,20 @@ errorId: ${e.errorId} const vmCardText = constants.CAN_BE_MIGRATED(dbCount, dbCount); this._rbg.cards[1].descriptions[1].textValue = vmCardText; - this._rbg.updateProperties({ + await this._rbg.updateProperties({ cards: this._rbg.cards }); } else { this._rbg.cards[0].descriptions[1].textValue = ''; this._rbg.cards[1].descriptions[1].textValue = ''; - this._rbg.updateProperties({ + await this._rbg.updateProperties({ cards: this._rbg.cards }); } if (this._rbg.selectedCardId) { - this.changeTargetType(this._rbg.selectedCardId); + await this.changeTargetType(this._rbg.selectedCardId); } this._rbgLoader.loading = false; diff --git a/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts b/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts index 7a57cc15db..5fc33af430 100644 --- a/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts +++ b/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts @@ -60,7 +60,7 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage { const username = results.rows[0][0].displayValue; this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin' ? MigrationSourceAuthenticationType.Sql : connectionProfile.authenticationType === 'Integrated' ? MigrationSourceAuthenticationType.Integrated : undefined!; - const sourceCredText = createHeadingTextComponent(this._view, constants.SOURCE_CREDENTIALS); + const sourceCredText = await createHeadingTextComponent(this._view, constants.SOURCE_CREDENTIALS); const enterYourCredText = createLabelTextComponent( this._view, diff --git a/extensions/sql-migration/src/wizard/summaryPage.ts b/extensions/sql-migration/src/wizard/summaryPage.ts index 7352ebdf59..33899c3671 100644 --- a/extensions/sql-migration/src/wizard/summaryPage.ts +++ b/extensions/sql-migration/src/wizard/summaryPage.ts @@ -56,8 +56,8 @@ export class SummaryPage extends MigrationWizardPage { } }).component(); - this._disposables.push(targetDatabaseHyperlink.onDidClick(e => { - targetDatabaseSummary.initialize(); + this._disposables.push(targetDatabaseHyperlink.onDidClick(async e => { + await targetDatabaseSummary.initialize(); })); const targetDatabaseRow = this._view.modelBuilder.flexContainer() @@ -87,26 +87,26 @@ export class SummaryPage extends MigrationWizardPage { this._flexContainer.addItems( [ - createHeadingTextComponent(this._view, constants.ACCOUNTS_SELECTION_PAGE_TITLE), + await createHeadingTextComponent(this._view, constants.ACCOUNTS_SELECTION_PAGE_TITLE), createInformationRow(this._view, constants.ACCOUNTS_SELECTION_PAGE_TITLE, this.migrationStateModel._azureAccount.displayInfo.displayName), - createHeadingTextComponent(this._view, constants.SOURCE_DATABASES), + await createHeadingTextComponent(this._view, constants.SOURCE_DATABASES), targetDatabaseRow, - createHeadingTextComponent(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE), + await createHeadingTextComponent(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE), createInformationRow(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE), createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name), createInformationRow(this._view, constants.LOCATION, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.location)), createInformationRow(this._view, constants.RESOURCE_GROUP, getResourceGroupFromId(this.migrationStateModel._targetServerInstance.id)), createInformationRow(this._view, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.name!)), - createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL), + await createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL), createInformationRow(this._view, constants.MODE, this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.ONLINE ? constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL : constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL), - createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_PAGE_TITLE), + await createHeadingTextComponent(this._view, constants.DATABASE_BACKUP_PAGE_TITLE), await this.createNetworkContainerRows(), - createHeadingTextComponent(this._view, constants.IR_PAGE_TITLE), + await createHeadingTextComponent(this._view, constants.IR_PAGE_TITLE), createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name), createInformationRow(this._view, constants.LOCATION, this.migrationStateModel._sqlMigrationService?.location!), createInformationRow(this._view, constants.RESOURCE_GROUP, this.migrationStateModel._sqlMigrationService?.properties?.resourceGroup!), @@ -140,7 +140,7 @@ export class SummaryPage extends MigrationWizardPage { createInformationRow(this._view, constants.BACKUP_LOCATION, constants.NETWORK_SHARE), createInformationRow(this._view, constants.NETWORK_SHARE, this.migrationStateModel._databaseBackup.networkShare.networkShareLocation), createInformationRow(this._view, constants.USER_ACCOUNT, this.migrationStateModel._databaseBackup.networkShare.windowsUser), - createHeadingTextComponent(this._view, constants.AZURE_STORAGE_ACCOUNT_TO_UPLOAD_BACKUPS), + await createHeadingTextComponent(this._view, constants.AZURE_STORAGE_ACCOUNT_TO_UPLOAD_BACKUPS), createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._databaseBackup.subscription.name), createInformationRow(this._view, constants.LOCATION, this.migrationStateModel._databaseBackup.networkShare.storageAccount.location), createInformationRow(this._view, constants.RESOURCE_GROUP, this.migrationStateModel._databaseBackup.networkShare.storageAccount.resourceGroup!), diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts index 78293b4b17..d2ed89eb34 100644 --- a/extensions/sql-migration/src/wizard/wizardController.ts +++ b/extensions/sql-migration/src/wizard/wizardController.ts @@ -30,7 +30,7 @@ export class WizardController { if (api) { this._model = new MigrationStateModel(this.extensionContext, connectionId, api.sqlMigration); this.extensionContext.subscriptions.push(this._model); - this.createWizard(this._model); + await this.createWizard(this._model); } } @@ -170,16 +170,15 @@ export function createInformationRow(view: azdata.ModelView, label: string, valu .component(); } -export function createHeadingTextComponent(view: azdata.ModelView, value: string): azdata.TextComponent { +export async function createHeadingTextComponent(view: azdata.ModelView, value: string): Promise { const component = createTextCompononent(view, value); - component.updateCssStyles({ + await component.updateCssStyles({ 'font-size': '13px', 'font-weight': 'bold', }); return component; } - export function createLabelTextComponent(view: azdata.ModelView, value: string, styles: { [key: string]: string; } = { 'width': '300px' }): azdata.TextComponent { const component = createTextCompononent(view, value, styles); return component;