diff --git a/extensions/sql-migration/src/api/azure.ts b/extensions/sql-migration/src/api/azure.ts index ff04f3c8e7..869f7951e3 100644 --- a/extensions/sql-migration/src/api/azure.ts +++ b/extensions/sql-migration/src/api/azure.ts @@ -422,7 +422,7 @@ export interface DatabaseMigration { export interface DatabaseMigrationProperties { scope: string; provisioningState: 'Succeeded' | 'Failed' | 'Creating'; - migrationStatus: 'InProgress' | 'Failed' | 'Succeeded' | 'Creating' | 'Completing' | 'Cancelling'; + migrationStatus: 'InProgress' | 'Failed' | 'Succeeded' | 'Creating' | 'Completing' | 'Canceling'; migrationStatusDetails?: MigrationStatusDetails; startedOn: string; endedOn: string; diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 2d210dc5e3..0936413e6b 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -395,10 +395,10 @@ export const FINISH_TIME = localize('sql.migration.finish.time', "Finish Time"); export function STATUS_VALUE(status: string, count: number): string { if (count > 0) { - return localize('sql.migration.status.error.count.some', "{0} (", StatusLookup[status]); + return localize('sql.migration.status.error.count.some', "{0} (", StatusLookup[status] ?? status); } - return localize('sql.migration.status.error.count.none', "{0}", StatusLookup[status]); + return localize('sql.migration.status.error.count.none', "{0}", StatusLookup[status] ?? status); } export interface LookupTable { @@ -410,7 +410,7 @@ export const StatusLookup: LookupTable = { ['Succeeded']: localize('sql.migration.status.succeeded', 'Succeeded'), ['Creating']: localize('sql.migration.status.creating', 'Creating'), ['Completing']: localize('sql.migration.status.completing', 'Completing'), - ['Cancelling']: localize('sql.migration.status.cancelling', 'Cancelling'), + ['Canceling']: localize('sql.migration.status.canceling', 'Canceling'), ['Failed']: localize('sql.migration.status.failed', 'Failed'), default: undefined, }; diff --git a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts index e258521380..f0f06aa48e 100644 --- a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts +++ b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts @@ -37,7 +37,6 @@ export class DashboardWidget { private _migrationStatusCardLoadingContainer!: azdata.LoadingComponent; private _view!: azdata.ModelView; - private _inProgressMigrationButton!: StatusCard; private _inProgressWarningMigrationButton!: StatusCard; private _successfulMigrationButton!: StatusCard; @@ -48,6 +47,7 @@ export class DashboardWidget { private _viewAllMigrationsButton!: azdata.ButtonComponent; private _autoRefreshHandle!: NodeJS.Timeout; + private _disposables: vscode.Disposable[] = []; constructor() { } @@ -94,10 +94,13 @@ export class DashboardWidget { 'margin-top': '20px' } }); - await view.initializeModel(container); - this._view.onClosed((e) => { + this._disposables.push(this._view.onClosed(e => { clearInterval(this._autoRefreshHandle); - }); + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + + await view.initializeModel(container); this.refreshMigrations(); }); } @@ -184,9 +187,9 @@ export class DashboardWidget { } }).component(); - preRequisiteLearnMoreLink.onDidClick((value) => { + this._disposables.push(preRequisiteLearnMoreLink.onDidClick((value) => { vscode.window.showInformationMessage(loc.COMING_SOON); - }); + })); const preReqContainer = view.modelBuilder.flexContainer().withItems([ preRequisiteListTitle, @@ -233,11 +236,11 @@ export class DashboardWidget { 'border': '1px solid' } }).component(); - buttonContainer.onDidClick(async () => { + this._disposables.push(buttonContainer.onDidClick(async () => { if (taskMetaData.command) { await vscode.commands.executeCommand(taskMetaData.command); } - }); + })); return view.modelBuilder.divContainer().withItems([buttonContainer]).component(); } @@ -558,10 +561,10 @@ export class DashboardWidget { } }).component(); - this._viewAllMigrationsButton.onDidClick(async (e) => { + this._disposables.push(this._viewAllMigrationsButton.onDidClick(async (e) => { const migrationStatus = await this.getCurrentMigrations(); new MigrationStatusDialog(migrationStatus ? migrationStatus : await this.getMigrations(), AdsMigrationStatus.ALL).initialize(); - }); + })); const refreshButton = view.modelBuilder.hyperlink().withProps({ label: loc.REFRESH, @@ -573,11 +576,11 @@ export class DashboardWidget { } }).component(); - refreshButton.onDidClick(async (e) => { + this._disposables.push(refreshButton.onDidClick(async (e) => { refreshButton.enabled = false; await this.refreshMigrations(); refreshButton.enabled = true; - }); + })); const buttonContainer = view.modelBuilder.flexContainer().withLayout({ justifyContent: 'flex-end', @@ -614,10 +617,10 @@ export class DashboardWidget { IconPathHelper.inProgressMigration, loc.MIGRATION_IN_PROGRESS ); - this._inProgressMigrationButton.container.onDidClick(async (e) => { + this._disposables.push(this._inProgressMigrationButton.container.onDidClick(async (e) => { const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING); dialog.initialize(); - }); + })); this._migrationStatusCardsContainer.addItem( this._inProgressMigrationButton.container @@ -628,10 +631,10 @@ export class DashboardWidget { loc.MIGRATION_IN_PROGRESS, '' ); - this._inProgressWarningMigrationButton.container.onDidClick(async (e) => { + this._disposables.push(this._inProgressWarningMigrationButton.container.onDidClick(async (e) => { const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.ONGOING); dialog.initialize(); - }); + })); this._migrationStatusCardsContainer.addItem( this._inProgressWarningMigrationButton.container @@ -641,10 +644,10 @@ export class DashboardWidget { IconPathHelper.completedMigration, loc.MIGRATION_COMPLETED ); - this._successfulMigrationButton.container.onDidClick(async (e) => { + this._disposables.push(this._successfulMigrationButton.container.onDidClick(async (e) => { const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.SUCCEEDED); dialog.initialize(); - }); + })); this._migrationStatusCardsContainer.addItem( this._successfulMigrationButton.container ); @@ -654,10 +657,10 @@ export class DashboardWidget { IconPathHelper.completingCutover, loc.MIGRATION_CUTOVER_CARD ); - this._completingMigrationButton.container.onDidClick(async (e) => { + this._disposables.push(this._completingMigrationButton.container.onDidClick(async (e) => { const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.COMPLETING); dialog.initialize(); - }); + })); this._migrationStatusCardsContainer.addItem( this._completingMigrationButton.container ); @@ -666,10 +669,10 @@ export class DashboardWidget { IconPathHelper.error, loc.MIGRATION_FAILED ); - this._failedMigrationButton.container.onDidClick(async (e) => { + this._disposables.push(this._failedMigrationButton.container.onDidClick(async (e) => { const dialog = new MigrationStatusDialog(await this.getCurrentMigrations(), AdsMigrationStatus.FAILED); dialog.initialize(); - }); + })); this._migrationStatusCardsContainer.addItem( this._failedMigrationButton.container ); @@ -678,9 +681,9 @@ export class DashboardWidget { IconPathHelper.notStartedMigration, loc.MIGRATION_NOT_STARTED ); - this._notStartedMigrationCard.container.onDidClick((e) => { + this._disposables.push(this._notStartedMigrationCard.container.onDidClick((e) => { vscode.window.showInformationMessage('Feature coming soon'); - }); + })); this._migrationStatusCardLoadingContainer = view.modelBuilder.loadingComponent().withItem(this._migrationStatusCardsContainer).component(); @@ -843,11 +846,11 @@ export class DashboardWidget { 'margin': '0px' } }).component(); - video1Container.onDidClick(async () => { + this._disposables.push(video1Container.onDidClick(async () => { if (linkMetaData.link) { await vscode.env.openExternal(vscode.Uri.parse(linkMetaData.link)); } - }); + })); videosContainer.addItem(video1Container, { CSSStyles: { 'background-image': `url(${vscode.Uri.file(linkMetaData.iconPath?.light)})`, diff --git a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts index 8f6e026e1e..9bfd322903 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/assessmentResultsDialog.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine'; import { SqlDatabaseTree } from './sqlDatabasesTree'; import { SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql'; @@ -26,9 +27,8 @@ export class AssessmentResultsDialog { // Dialog Name for Telemetry public dialogName: string | undefined; - private _tree: SqlDatabaseTree; - + private _disposables: vscode.Disposable[] = []; constructor(public ownerUri: string, public model: MigrationStateModel, public title: string, private _skuRecommendationPage: SKURecommendationPage, private _targetType: MigrationTargetType) { this._model = model; @@ -46,6 +46,11 @@ export class AssessmentResultsDialog { }).component(); flex.addItem(await this._tree.createRootContainer(view), { flex: '1 1 auto' }); + this._disposables.push(view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(flex); resolve(); } catch (ex) { @@ -61,10 +66,10 @@ export class AssessmentResultsDialog { this.dialog = azdata.window.createModelViewDialog(this.title, this.title, 'wide'); this.dialog.okButton.label = AssessmentResultsDialog.OkButtonText; - this.dialog.okButton.onClick(async () => await this.execute()); + this._disposables.push(this.dialog.okButton.onClick(async () => await this.execute())); this.dialog.cancelButton.label = AssessmentResultsDialog.CancelButtonText; - this.dialog.cancelButton.onClick(async () => await this.cancel()); + this._disposables.push(this.dialog.cancelButton.onClick(async () => await this.cancel())); const dialogSetupPromises: Thenable[] = []; diff --git a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts index dd9984a9c3..793ac2fe9e 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts @@ -3,6 +3,7 @@ * 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 { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql'; import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine'; import * as constants from '../../constants/strings'; @@ -77,6 +78,7 @@ export class SqlDatabaseTree { private _serverName!: string; private _dbNames!: string[]; private _databaseCount!: azdata.TextComponent; + private _disposables: vscode.Disposable[] = []; constructor( private _model: MigrationStateModel, @@ -99,6 +101,10 @@ export class SqlDatabaseTree { this._rootContainer.addItem(this._resultComponent, { flex: '0 0 auto' }); this._rootContainer.addItem(selectDbMessage, { flex: '1 1 auto' }); + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); return this._rootContainer; } @@ -167,12 +173,14 @@ export class SqlDatabaseTree { ] } ).component(); - this._databaseTable.onDataChanged(() => { + + this._disposables.push(this._databaseTable.onDataChanged(() => { this._databaseCount.updateProperties({ 'value': constants.DATABASES(this.selectedDbs().length, this._model._databaseAssessment.length) }); - }); - this._databaseTable.onRowSelected(async (e) => { + })); + + this._disposables.push(this._databaseTable.onRowSelected(async (e) => { if (this._targetType === MigrationTargetType.SQLMI) { this._activeIssues = this._model._assessmentResults?.databaseAssessments[e.row].issues; } else { @@ -188,7 +196,7 @@ export class SqlDatabaseTree { 'display': 'none' }); await this.refreshResults(); - }); + })); const tableContainer = this._view.modelBuilder.divContainer().withItems([this._databaseTable]).withProps({ CSSStyles: { @@ -206,7 +214,7 @@ export class SqlDatabaseTree { width: 200 }).component(); - resourceSearchBox.onTextChanged(value => this._filterTableList(value)); + this._disposables.push(resourceSearchBox.onTextChanged(value => this._filterTableList(value))); const searchContainer = this._view.modelBuilder.divContainer().withItems([resourceSearchBox]).withProps({ CSSStyles: { @@ -270,7 +278,7 @@ export class SqlDatabaseTree { } }).component(); - this._instanceTable.onRowSelected(async (e) => { + this._disposables.push(this._instanceTable.onRowSelected(async (e) => { this._activeIssues = this._model._assessmentResults?.issues; this._dbName.value = this._serverName; this._resultComponent.updateCssStyles({ @@ -284,7 +292,7 @@ export class SqlDatabaseTree { if (this._targetType === MigrationTargetType.SQLMI) { await this.refreshResults(); } - }); + })); return instanceContainer; } @@ -506,10 +514,10 @@ export class SqlDatabaseTree { } ).component(); - this._impactedObjectsTable.onRowSelected((e) => { + this._disposables.push(this._impactedObjectsTable.onRowSelected((e) => { const impactedObject = e.row > -1 ? this._impactedObjects[e.row] : undefined; this.refreshImpactedObject(impactedObject); - }); + })); const objectDetailsTitle = this._view.modelBuilder.text().withProps({ value: constants.OBJECT_DETAILS, @@ -714,10 +722,10 @@ export class SqlDatabaseTree { } ).component(); - this._assessmentResultsTable.onRowSelected(async (e) => { + this._disposables.push(this._assessmentResultsTable.onRowSelected(async (e) => { const selectedIssue = e.row > -1 ? this._activeIssues[e.row] : undefined; await this.refreshAssessmentDetails(selectedIssue); - }); + })); const container = this._view.modelBuilder.flexContainer().withItems([this._assessmentResultsTable]).withLayout({ flexFlow: 'column', diff --git a/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts b/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts index 1911df1320..ff82eac962 100644 --- a/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts +++ b/extensions/sql-migration/src/dialog/createResourceGroup/createResourceGroupDialog.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { azureResource } from 'azureResource'; import { EventEmitter } from 'events'; import { createResourceGroup } from '../../api/azure'; @@ -13,6 +14,7 @@ export class CreateResourceGroupDialog { private _dialogObject!: azdata.window.Dialog; private _view!: azdata.ModelView; private _creationEvent: EventEmitter = new EventEmitter; + private _disposables: vscode.Disposable[] = []; constructor(private _azureAccount: azdata.Account, private _subscription: azureResource.AzureResourceSubscription, private _location: string) { this._dialogObject = azdata.window.createModelViewDialog( @@ -63,11 +65,11 @@ export class CreateResourceGroupDialog { return valid; }).component(); - resourceGroupName.onTextChanged(e => { + this._disposables.push(resourceGroupName.onTextChanged(e => { errorBox.updateCssStyles({ 'display': 'none' }); - }); + })); const okButton = view.modelBuilder.button().withProps({ label: constants.OK, @@ -75,7 +77,7 @@ export class CreateResourceGroupDialog { enabled: false }).component(); - okButton.onDidClick(async e => { + this._disposables.push(okButton.onDidClick(async e => { errorBox.updateCssStyles({ 'display': 'none' }); @@ -95,16 +97,16 @@ export class CreateResourceGroupDialog { } finally { loading.loading = false; } - }); + })); const cancelButton = view.modelBuilder.button().withProps({ label: constants.CANCEL, width: '80px' }).component(); - cancelButton.onDidClick(e => { + this._disposables.push(cancelButton.onDidClick(e => { this._creationEvent.emit('done', undefined); - }); + })); const loading = view.modelBuilder.loadingComponent().withProps({ loading: false, @@ -174,6 +176,12 @@ export class CreateResourceGroupDialog { 'padding': '0px !important' } }).component(); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + return view.initializeModel(form).then(v => { resourceGroupName.focus(); }); diff --git a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts index f10fcc0441..13d51216c7 100644 --- a/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts +++ b/extensions/sql-migration/src/dialog/createSqlMigrationService/createSqlMigrationServiceDialog.ts @@ -47,6 +47,7 @@ export class CreateSqlMigrationServiceDialog { private _isBlobContainerUsed: boolean = false; private irNodes: string[] = []; + private _disposables: vscode.Disposable[] = []; public async createNewDms(migrationStateModel: MigrationStateModel, resourceGroupPreset: string): Promise { this._model = migrationStateModel; @@ -64,7 +65,7 @@ export class CreateSqlMigrationServiceDialog { width: '80px' }).component(); - this._formSubmitButton.onDidClick(async (e) => { + this._disposables.push(this._formSubmitButton.onDidClick(async (e) => { this._dialogObject.message = { text: '' }; @@ -122,7 +123,7 @@ export class CreateSqlMigrationServiceDialog { this.setFormEnabledState(true); return; } - }); + })); this._statusLoadingComponent = view.modelBuilder.loadingComponent().withProps({ loadingText: constants.LOADING_MIGRATION_SERVICES, @@ -153,6 +154,11 @@ export class CreateSqlMigrationServiceDialog { const form = formBuilder.withLayout({ width: '100%' }).component(); + this._disposables.push(view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + return view.initializeModel(form).then(() => { this.populateSubscriptions(); }); @@ -160,7 +166,7 @@ export class CreateSqlMigrationServiceDialog { this._testConnectionButton = azdata.window.createButton(constants.TEST_CONNECTION); this._testConnectionButton.hidden = true; - this._testConnectionButton.onClick(async (e) => { + this._disposables.push(this._testConnectionButton.onClick(async (e) => { this._refreshLoadingComponent.loading = true; this._connectionStatus.updateCssStyles({ 'display': 'none' @@ -174,17 +180,16 @@ export class CreateSqlMigrationServiceDialog { 'display': 'inline' }); this._refreshLoadingComponent.loading = false; - }); + })); this._dialogObject.customButtons = [this._testConnectionButton]; this._dialogObject.content = [tab]; this._dialogObject.okButton.enabled = false; azdata.window.openDialog(this._dialogObject); - this._dialogObject.cancelButton.onClick((e) => { - }); - this._dialogObject.okButton.onClick((e) => { + this._disposables.push(this._dialogObject.cancelButton.onClick((e) => { })); + this._disposables.push(this._dialogObject.okButton.onClick((e) => { this._doneButtonEvent.emit('done', this._createdMigrationService, this._selectedResourceGroup); - }); + })); this._isBlobContainerUsed = this._model._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER; @@ -249,7 +254,7 @@ export class CreateSqlMigrationServiceDialog { url: '' }).component(); - this._createResourceGroupLink.onDidClick(async e => { + this._disposables.push(this._createResourceGroupLink.onDidClick(async e => { const createResourceGroupDialog = new CreateResourceGroupDialog(this._model._azureAccount, this._model._targetSubscription, this._model._targetServerInstance.location); const createdResourceGroup = await createResourceGroupDialog.initialize(); if (createdResourceGroup) { @@ -265,7 +270,7 @@ export class CreateSqlMigrationServiceDialog { this.migrationServiceResourceGroupDropdown.loading = false; this.migrationServiceResourceGroupDropdown.focus(); } - }); + })); this.migrationServiceNameText = this._view.modelBuilder.inputBox().component(); @@ -549,10 +554,10 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.COPY_KEY1, }).component(); - this._copyKey1Button.onDidClick((e) => { + 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._copyKey2Button = this._view.modelBuilder.button().withProps({ title: constants.COPY_KEY2, @@ -560,10 +565,10 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.COPY_KEY2, }).component(); - this._copyKey2Button.onDidClick((e) => { + 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._refreshKey1Button = this._view.modelBuilder.button().withProps({ title: constants.REFRESH_KEY1, @@ -571,8 +576,9 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.REFRESH_KEY1, }).component(); - this._refreshKey1Button.onDidClick((e) => {//TODO: add refresh logic - }); + this._disposables.push(this._refreshKey1Button.onDidClick((e) => { + //TODO: add refresh logic + })); this._refreshKey2Button = this._view.modelBuilder.button().withProps({ title: constants.REFRESH_KEY2, @@ -580,8 +586,9 @@ export class CreateSqlMigrationServiceDialog { ariaLabel: constants.REFRESH_KEY2, }).component(); - this._refreshKey2Button.onDidClick((e) => { //TODO: add refresh logic - }); + this._disposables.push(this._refreshKey2Button.onDidClick((e) => { + //TODO: add refresh logic + })); 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 77528412e5..89db53e62e 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/confirmCutoverDialog.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationCutoverDialogModel } from './migrationCutoverDialogModel'; import * as constants from '../../constants/strings'; -import * as vscode from 'vscode'; import { SqlManagedInstance } from '../../api/azure'; export class ConfirmCutoverDialog { private _dialogObject!: azdata.window.Dialog; private _view!: azdata.ModelView; + private _disposables: vscode.Disposable[] = []; constructor(private migrationCutoverModel: MigrationCutoverDialogModel) { this._dialogObject = azdata.window.createModelViewDialog('', 'ConfirmCutoverDialog', 500); @@ -74,9 +75,9 @@ export class ConfirmCutoverDialog { label: constants.CONFIRM_CUTOVER_CHECKBOX, }).component(); - confirmCheckbox.onChanged(e => { + this._disposables.push(confirmCheckbox.onChanged(e => { this._dialogObject.okButton.enabled = e; - }); + })); const cutoverWarning = this._view.modelBuilder.infoBox().withProps({ text: constants.COMPLETING_CUTOVER_WARNING, @@ -119,10 +120,10 @@ export class ConfirmCutoverDialog { this._dialogObject.okButton.enabled = false; this._dialogObject.okButton.label = constants.COMPLETE_CUTOVER; - this._dialogObject.okButton.onClick((e) => { + this._disposables.push(this._dialogObject.okButton.onClick((e) => { this.migrationCutoverModel.startCutover(); vscode.window.showInformationMessage(constants.CUTOVER_IN_PROGRESS(this.migrationCutoverModel._migration.migrationContext.properties.sourceDatabaseName)); - }); + })); const formBuilder = view.modelBuilder.formContainer().withFormItems( [ @@ -135,6 +136,12 @@ export class ConfirmCutoverDialog { } ); const form = formBuilder.withLayout({ width: '100%' }).component(); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + return view.initializeModel(form); }); this._dialogObject.content = [tab]; diff --git a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts index 0b4d42f3e3..bcab9c9240 100644 --- a/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationCutover/migrationCutoverDialog.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { IconPathHelper } from '../../constants/iconPathHelper'; import { MigrationContext } from '../../models/migrationLocalStorage'; import { MigrationCutoverDialogModel, MigrationStatus } from './migrationCutoverDialogModel'; import * as loc from '../../constants/strings'; import { convertByteSizeToReadableUnit, convertIsoTimeToLocalTime, getSqlServerName, SupportedAutoRefreshIntervals } from '../../api/utils'; import { EOL } from 'os'; -import * as vscode from 'vscode'; import { ConfirmCutoverDialog } from './confirmCutoverDialog'; const refreshFrequency: SupportedAutoRefreshIntervals = 30000; @@ -42,6 +42,7 @@ export class MigrationCutoverDialog { private _fileCount!: azdata.TextComponent; private fileTable!: azdata.TableComponent; private _autoRefreshHandle!: any; + private _disposables: vscode.Disposable[] = []; readonly _infoFieldWidth: string = '250px'; @@ -270,9 +271,12 @@ export class MigrationCutoverDialog { { horizontal: false } ); const form = formBuilder.withLayout({ width: '100%' }).component(); - this._view.onClosed(e => { + + this._disposables.push(this._view.onClosed(e => { clearInterval(this._autoRefreshHandle); - }); + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); return view.initializeModel(form).then((value) => { this.refreshStatus(); @@ -286,9 +290,9 @@ export class MigrationCutoverDialog { this._dialogObject.cancelButton.hidden = true; this._dialogObject.okButton.label = loc.CLOSE; - this._dialogObject.okButton.onClick(e => { + this._disposables.push(this._dialogObject.okButton.onClick(e => { clearInterval(this._autoRefreshHandle); - }); + })); azdata.window.openDialog(this._dialogObject); } @@ -364,12 +368,12 @@ export class MigrationCutoverDialog { } }).component(); - this._cutoverButton.onDidClick(async (e) => { + this._disposables.push(this._cutoverButton.onDidClick(async (e) => { await this.refreshStatus(); const dialog = new ConfirmCutoverDialog(this._model); await dialog.initialize(); await this.refreshStatus(); - }); + })); headerActions.addItem(this._cutoverButton, { flex: '0' @@ -387,14 +391,14 @@ export class MigrationCutoverDialog { } }).component(); - this._cancelButton.onDidClick((e) => { + this._disposables.push(this._cancelButton.onDidClick((e) => { 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(); } }); - }); + })); headerActions.addItem(this._cancelButton, { flex: '0' @@ -413,8 +417,8 @@ export class MigrationCutoverDialog { } }).component(); - this._refreshButton.onDidClick( - async (e) => await this.refreshStatus()); + this._disposables.push(this._refreshButton.onDidClick( + async (e) => await this.refreshStatus())); headerActions.addItem(this._refreshButton, { flex: '0', @@ -432,7 +436,7 @@ export class MigrationCutoverDialog { } }).component(); - this._copyDatabaseMigrationDetails.onDidClick(async (e) => { + this._disposables.push(this._copyDatabaseMigrationDetails.onDidClick(async (e) => { await this.refreshStatus(); if (this._model.migrationOpStatus) { vscode.env.clipboard.writeText(JSON.stringify({ @@ -444,7 +448,7 @@ export class MigrationCutoverDialog { } vscode.window.showInformationMessage(loc.DETAILS_COPIED); - }); + })); headerActions.addItem(this._copyDatabaseMigrationDetails, { flex: '0', diff --git a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts index 2aff820d64..902d033c85 100644 --- a/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts +++ b/extensions/sql-migration/src/dialog/migrationStatus/migrationStatusDialog.ts @@ -31,6 +31,7 @@ export class MigrationStatusDialog { private _statusTable!: azdata.DeclarativeTableComponent; private _refreshLoader!: azdata.LoadingComponent; private _autoRefreshHandle!: NodeJS.Timeout; + private _disposables: vscode.Disposable[] = []; constructor(migrations: MigrationContext[], private _filter: AdsMigrationStatus) { this._model = new MigrationStatusDialogModel(migrations); @@ -48,9 +49,9 @@ export class MigrationStatusDialog { width: '220px' }).component(); - this._statusDropdown.onValueChanged((value) => { + this._disposables.push(this._statusDropdown.onValueChanged((value) => { this.populateMigrationTable(); - }); + })); if (this._filter) { this._statusDropdown.value = (this._statusDropdown.values).find((value) => { @@ -76,17 +77,20 @@ export class MigrationStatusDialog { } ); const form = formBuilder.withLayout({ width: '100%' }).component(); - this._view.onClosed(e => { + this._disposables.push(this._view.onClosed(e => { clearInterval(this._autoRefreshHandle); - }); + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + return view.initializeModel(form); }); this._dialogObject.content = [tab]; this._dialogObject.cancelButton.hidden = true; this._dialogObject.okButton.label = loc.CLOSE; - this._dialogObject.okButton.onClick(e => { + this._disposables.push(this._dialogObject.okButton.onClick(e => { clearInterval(this._autoRefreshHandle); - }); + })); azdata.window.openDialog(this._dialogObject); } @@ -100,9 +104,9 @@ export class MigrationStatusDialog { width: '360px' }).component(); - this._searchBox.onTextChanged((value) => { + this._disposables.push(this._searchBox.onTextChanged((value) => { this.populateMigrationTable(); - }); + })); this._refresh = this._view.modelBuilder.button().withProps({ iconPath: { @@ -115,9 +119,9 @@ export class MigrationStatusDialog { label: loc.REFRESH_BUTTON_LABEL, }).component(); - this._refresh.onDidClick((e) => { + this._disposables.push(this._refresh.onDidClick((e) => { this.refreshTable(); - }); + })); const flexContainer = this._view.modelBuilder.flexContainer().withProps({ width: 900, @@ -170,7 +174,7 @@ export class MigrationStatusDialog { } private registerCommands(): void { - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.cutover', async (migrationId: string) => { try { @@ -186,9 +190,9 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.view.database', async (migrationId: string) => { try { @@ -198,9 +202,9 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.view.target', async (migrationId: string) => { try { @@ -210,9 +214,9 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.view.service', async (migrationId: string) => { try { @@ -222,9 +226,9 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.copy.migration', async (migrationId: string) => { try { @@ -244,9 +248,9 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); - vscode.commands.registerCommand( + this._disposables.push(vscode.commands.registerCommand( 'sqlmigration.cancel.migration', async (migrationId: string) => { try { @@ -265,7 +269,7 @@ export class MigrationStatusDialog { } catch (e) { console.log(e); } - }); + })); } private async populateMigrationTable(): Promise { @@ -336,8 +340,8 @@ export class MigrationStatusDialog { CSSStyles: statusCellStyles }).component(); - databaseHyperLink.onDidClick( - async (e) => await (new MigrationCutoverDialog(migration)).initialize()); + this._disposables.push(databaseHyperLink.onDidClick( + async (e) => await (new MigrationCutoverDialog(migration)).initialize())); return this._view.modelBuilder .flexContainer() @@ -563,7 +567,7 @@ export class MigrationStatusDialog { return IconPathHelper.notStartedMigration; case 'Completing': return IconPathHelper.completingCutover; - case 'Cancelling': + case 'Canceling': return IconPathHelper.cancel; case 'Failed': default: diff --git a/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts b/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts index 16dad8719d..b784ff997a 100644 --- a/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts +++ b/extensions/sql-migration/src/dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog.ts @@ -30,6 +30,7 @@ export class SqlMigrationServiceDetailsDialog { private _dialog: azdata.window.Dialog; private _migrationServiceAuthKeyTable!: azdata.DeclarativeTableComponent; + private _disposables: vscode.Disposable[] = []; constructor(private migrationContext: MigrationContext) { this._dialog = azdata.window.createModelViewDialog( @@ -41,9 +42,16 @@ export class SqlMigrationServiceDetailsDialog { async initialize(): Promise { this._dialog.registerContent( - async (view: azdata.ModelView) => await this.createServiceContent( - view, - this.migrationContext)); + async (view: azdata.ModelView) => { + this._disposables.push(view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + + await this.createServiceContent( + view, + this.migrationContext); + }); this._dialog.okButton.label = constants.SQL_MIGRATION_SERVICE_DETAILS_BUTTON_LABEL; this._dialog.okButton.focused = true; @@ -238,10 +246,10 @@ export class SqlMigrationServiceDetailsDialog { }) .component(); - copyKey1Button.onDidClick((e) => { + this._disposables.push(copyKey1Button.onDidClick((e) => { vscode.env.clipboard.writeText(keys.authKey1); vscode.window.showInformationMessage(constants.SERVICE_KEY1_COPIED_HELP); - }); + })); const copyKey2Button = view.modelBuilder .button() @@ -254,10 +262,10 @@ export class SqlMigrationServiceDetailsDialog { }) .component(); - copyKey2Button.onDidClick((e) => { + this._disposables.push(copyKey2Button.onDidClick((e) => { vscode.env.clipboard.writeText(keys.authKey2); vscode.window.showInformationMessage(constants.SERVICE_KEY2_COPIED_HELP); - }); + })); const refreshKey1Button = view.modelBuilder .button() @@ -269,8 +277,8 @@ export class SqlMigrationServiceDetailsDialog { ariaLabel: constants.REFRESH_KEY1, }) .component(); - refreshKey1Button.onDidClick( - async (e) => await this._regenerateAuthKey(view, migrationContext, AUTH_KEY1)); + this._disposables.push(refreshKey1Button.onDidClick( + async (e) => await this._regenerateAuthKey(view, migrationContext, AUTH_KEY1))); const refreshKey2Button = view.modelBuilder .button() @@ -282,8 +290,8 @@ export class SqlMigrationServiceDetailsDialog { ariaLabel: constants.REFRESH_KEY2, }) .component(); - refreshKey2Button.onDidClick( - async (e) => await this._regenerateAuthKey(view, migrationContext, AUTH_KEY2)); + this._disposables.push(refreshKey2Button.onDidClick( + async (e) => await this._regenerateAuthKey(view, migrationContext, AUTH_KEY2))); this._migrationServiceAuthKeyTable.updateProperties({ dataValues: [ diff --git a/extensions/sql-migration/src/main.ts b/extensions/sql-migration/src/main.ts index ae57d2eb9f..b7953e148d 100644 --- a/extensions/sql-migration/src/main.ts +++ b/extensions/sql-migration/src/main.ts @@ -36,7 +36,7 @@ class SQLMigration { input.items = NotebookPathHelper.getAllMigrationNotebooks(); - input.onDidAccept(async (e) => { + this.context.subscriptions.push(input.onDidAccept(async (e) => { const selectedNotebook = input.selectedItems[0]; if (selectedNotebook) { try { @@ -50,7 +50,7 @@ class SQLMigration { } input.hide(); } - }); + })); input.show(); }), diff --git a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts index 465ea52c62..ee1408e658 100644 --- a/extensions/sql-migration/src/wizard/accountsSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/accountsSelectionPage.ts @@ -16,6 +16,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { private _azureAccountsDropdown!: azdata.DropDownComponent; private _accountTenantDropdown!: azdata.DropDownComponent; private _accountTenantFlexContainer!: azdata.FlexContainer; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.ACCOUNTS_SELECTION_PAGE_TITLE), migrationStateModel); @@ -32,6 +33,9 @@ export class AccountsSelectionPage extends MigrationWizardPage { ); await view.initializeModel(form.component()); await this.populateAzureAccountsDropdown(); + this._disposables.push(view.onClosed(e => + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }))); } private createAzureAccountsDropdown(view: azdata.ModelView): azdata.FormComponent { @@ -74,7 +78,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { return false; }).component(); - this._azureAccountsDropdown.onValueChanged(async (value) => { + this._disposables.push(this._azureAccountsDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._azureAccountsDropdown, value); if (selectedIndex > -1) { const selectedAzureAccount = this.migrationStateModel.getAccount(selectedIndex); @@ -97,7 +101,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { this.migrationStateModel._databaseBackup.subscription = undefined!; await this._azureAccountsDropdown.validate(); } - }); + })); const linkAccountButton = view.modelBuilder.hyperlink() .withProps({ @@ -109,14 +113,14 @@ export class AccountsSelectionPage extends MigrationWizardPage { }) .component(); - linkAccountButton.onDidClick(async (event) => { + this._disposables.push(linkAccountButton.onDidClick(async (event) => { await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount'); await this.populateAzureAccountsDropdown(); this.wizard.message = { text: '' }; this._azureAccountsDropdown.validate(); - }); + })); const flexContainer = view.modelBuilder.flexContainer() .withLayout({ @@ -152,7 +156,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { fireOnTextChange: true, }).component(); - this._accountTenantDropdown.onValueChanged(value => { + this._disposables.push(this._accountTenantDropdown.onValueChanged(value => { /** * Replacing all the tenants in azure account with the tenant user has selected. * All azure requests will only run on this tenant from now on @@ -164,7 +168,7 @@ export class AccountsSelectionPage extends MigrationWizardPage { this.migrationStateModel._targetSubscription = undefined!; this.migrationStateModel._databaseBackup.subscription = undefined!; } - }); + })); this._accountTenantFlexContainer = view.modelBuilder.flexContainer() .withLayout({ diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts index 423c9c57b1..dcc5cbb972 100644 --- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts +++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { EOL } from 'os'; import { getStorageAccountAccessKeys } from '../api/azure'; import { MigrationWizardPage } from '../models/migrationWizardPage'; @@ -48,8 +49,8 @@ export class DatabaseBackupPage extends MigrationWizardPage { private _networkShareTargetDatabaseNames: azdata.InputBoxComponent[] = []; private _blobContainerTargetDatabaseNames: azdata.InputBoxComponent[] = []; - private _existingDatabases: string[] = []; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_PAGE_TITLE), migrationStateModel); @@ -87,9 +88,13 @@ export class DatabaseBackupPage extends MigrationWizardPage { } ] ); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(form.component()); - - } private createBackupLocationComponent(): azdata.FlexContainer { @@ -111,11 +116,11 @@ export class DatabaseBackupPage extends MigrationWizardPage { } }).component(); - networkShareButton.onDidChangeCheckedState((e) => { + this._disposables.push(networkShareButton.onDidChangeCheckedState((e) => { if (e) { this.switchNetworkContainerFields(NetworkContainerType.NETWORK_SHARE); } - }); + })); const blobContainerButton = this._view.modelBuilder.radioButton() .withProps({ @@ -126,11 +131,11 @@ export class DatabaseBackupPage extends MigrationWizardPage { } }).component(); - blobContainerButton.onDidChangeCheckedState((e) => { + this._disposables.push(blobContainerButton.onDidChangeCheckedState((e) => { if (e) { this.switchNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER); } - }); + })); const flexContainer = this._view.modelBuilder.flexContainer().withItems( [ @@ -189,9 +194,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { enabled: false, width: WIZARD_INPUT_COMPONENT_WIDTH }).component(); - this._sqlSourceUsernameInput.onTextChanged(value => { + this._disposables.push(this._sqlSourceUsernameInput.onTextChanged(value => { this.migrationStateModel._sqlServerUsername = value; - }); + })); const sqlPasswordLabel = this._view.modelBuilder.text().withProps({ value: constants.DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL, @@ -206,9 +211,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { inputType: 'password', width: WIZARD_INPUT_COMPONENT_WIDTH }).component(); - this._sqlSourcepassword.onTextChanged(value => { + this._disposables.push(this._sqlSourcepassword.onTextChanged(value => { this.migrationStateModel._sqlServerPassword = value; - }); + })); const networkShareHeading = this._view.modelBuilder.text().withProps({ @@ -254,10 +259,10 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - this._networkSharePath.onTextChanged((value) => { + this._disposables.push(this._networkSharePath.onTextChanged((value) => { this.validateFields(); this.migrationStateModel._databaseBackup.networkShare.networkShareLocation = value; - }); + })); const networkShareInfoBox = this._view.modelBuilder.infoBox().withProps({ text: constants.DATABASE_SERVICE_ACCOUNT_INFO_TEXT, @@ -295,9 +300,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - this._windowsUserAccountText.onTextChanged((value) => { + this._disposables.push(this._windowsUserAccountText.onTextChanged((value) => { this.migrationStateModel._databaseBackup.networkShare.windowsUser = value; - }); + })); const passwordLabel = this._view.modelBuilder.text() .withProps({ @@ -315,12 +320,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { required: true, width: WIZARD_INPUT_COMPONENT_WIDTH }).component(); - this._passwordText.onTextChanged((value) => { + this._disposables.push(this._passwordText.onTextChanged((value) => { this.migrationStateModel._databaseBackup.networkShare.password = value; - }); - - - + })); const flexContainer = this._view.modelBuilder.flexContainer().withItems( [ @@ -560,13 +562,13 @@ export class DatabaseBackupPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._networkShareStorageAccountResourceGroupDropdown.onValueChanged(async (value) => { + this._disposables.push(this._networkShareStorageAccountResourceGroupDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._networkShareStorageAccountResourceGroupDropdown, value); if (selectedIndex > -1) { this.migrationStateModel._databaseBackup.networkShare.resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); await this.loadNetworkShareStorageDropdown(); } - }); + })); const storageAccountLabel = this._view.modelBuilder.text() .withProps({ @@ -585,12 +587,12 @@ export class DatabaseBackupPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._networkShareContainerStorageAccountDropdown.onValueChanged((value) => { + this._disposables.push(this._networkShareContainerStorageAccountDropdown.onValueChanged((value) => { const selectedIndex = findDropDownItemIndex(this._networkShareContainerStorageAccountDropdown, value); if (selectedIndex > -1) { this.migrationStateModel._databaseBackup.networkShare.storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex); } - }); + })); this._networkShareContainerStorageAccountRefreshButton = this._view.modelBuilder.button().withProps({ iconPath: IconPathHelper.refresh, @@ -600,9 +602,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { ariaLabel: constants.REFRESH, }).component(); - this._networkShareContainerStorageAccountRefreshButton.onDidClick(async (value) => { + this._disposables.push(this._networkShareContainerStorageAccountRefreshButton.onDidClick(async (value) => { await this.loadNetworkShareStorageDropdown(); - }); + })); const storageAccountContainer = this._view.modelBuilder.flexContainer().component(); @@ -685,10 +687,10 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - targetDatabaseInput.onTextChanged((value) => { + this._disposables.push(targetDatabaseInput.onTextChanged((value) => { this.migrationStateModel._targetDatabaseNames[index] = value.trim(); this.validateFields(); - }); + })); this._networkShareTargetDatabaseNames.push(targetDatabaseInput); const blobtargetDatabaseInput = this._view.modelBuilder.inputBox().withProps({ @@ -710,9 +712,9 @@ export class DatabaseBackupPage extends MigrationWizardPage { } return true; }).component(); - blobtargetDatabaseInput.onTextChanged((value) => { + this._disposables.push(blobtargetDatabaseInput.onTextChanged((value) => { this.migrationStateModel._targetDatabaseNames[index] = value.trim(); - }); + })); this._blobContainerTargetDatabaseNames.push(blobtargetDatabaseInput); const blobContainerResourceDropdown = this._view.modelBuilder.dropDown().withProps({ @@ -721,14 +723,14 @@ export class DatabaseBackupPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - blobContainerResourceDropdown.onValueChanged(async (value) => { + this._disposables.push(blobContainerResourceDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(blobContainerResourceDropdown, value); if (selectedIndex > -1 && value !== constants.RESOURCE_GROUP_NOT_FOUND) { this.migrationStateModel._databaseBackup.blobs[index].resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); } await this.loadblobStorageDropdown(index); - }); + })); this._blobContainerResourceGroupDropdowns.push(blobContainerResourceDropdown); const blobContainerStorageAccountDropdown = this._view.modelBuilder.dropDown() @@ -739,13 +741,13 @@ export class DatabaseBackupPage extends MigrationWizardPage { fireOnTextChange: true, }).component(); - blobContainerStorageAccountDropdown.onValueChanged(async (value) => { + this._disposables.push(blobContainerStorageAccountDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(blobContainerStorageAccountDropdown, value); if (selectedIndex > -1 && value !== constants.NO_STORAGE_ACCOUNT_FOUND) { this.migrationStateModel._databaseBackup.blobs[index].storageAccount = this.migrationStateModel.getStorageAccount(selectedIndex); } await this.loadBlobContainerDropdown(index); - }); + })); this._blobContainerStorageAccountDropdowns.push(blobContainerStorageAccountDropdown); const blobContainerDropdown = this._view.modelBuilder.dropDown() @@ -755,12 +757,12 @@ export class DatabaseBackupPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - blobContainerDropdown.onValueChanged(value => { + this._disposables.push(blobContainerDropdown.onValueChanged(value => { const selectedIndex = findDropDownItemIndex(blobContainerDropdown, value); if (selectedIndex > -1 && value !== constants.NO_BLOBCONTAINERS_FOUND) { this.migrationStateModel._databaseBackup.blobs[index].blobContainer = this.migrationStateModel.getBlobContainer(selectedIndex); } - }); + })); this._blobContainerDropdowns.push(blobContainerDropdown); }); diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts index f1587efa52..356de8c2c3 100644 --- a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts +++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts @@ -4,10 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import * as constants from '../constants/strings'; -import { IconPath, IconPathHelper } from '../constants/iconPathHelper'; +// import { IconPath, IconPathHelper } from '../constants/iconPathHelper'; import { debounce } from '../api/utils'; const headerLeft: azdata.CssStyles = { @@ -41,6 +42,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage { private _dbNames!: string[]; private _dbCount!: azdata.TextComponent; private _databaseTableValues!: azdata.DeclarativeTableCellValue[][]; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.SOURCE_CONFIGURATION, 'MigrationModePage'), migrationStateModel); @@ -56,6 +58,11 @@ export class DatabaseSelectorPage extends MigrationWizardPage { }).component(); flex.addItem(await this.createRootContainer(view), { flex: '1 1 auto' }); + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(flex); } @@ -82,7 +89,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage { width: 200 }).component(); - resourceSearchBox.onTextChanged(value => this._filterTableList(value)); + this._disposables.push(resourceSearchBox.onTextChanged(value => this._filterTableList(value))); const searchContainer = this._view.modelBuilder.divContainer().withItems([resourceSearchBox]).withProps({ CSSStyles: { @@ -142,7 +149,8 @@ export class DatabaseSelectorPage extends MigrationWizardPage { enabled: selectable }, { - value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, finalResult[index].options.name), + // value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, finalResult[index].options.name), + value: finalResult[index].options.name, style: styleLeft }, { @@ -207,7 +215,8 @@ export class DatabaseSelectorPage extends MigrationWizardPage { }, { displayName: constants.DATABASE, - valueType: azdata.DeclarativeDataType.component, + // valueType: azdata.DeclarativeDataType.component, + valueType: azdata.DeclarativeDataType.string, width: 100, isReadOnly: true, headerCssStyles: headerLeft @@ -238,11 +247,11 @@ export class DatabaseSelectorPage extends MigrationWizardPage { ).component(); await this._databaseSelectorTable.setDataValues(this._databaseTableValues); - this._databaseSelectorTable.onDataChanged(() => { + this._disposables.push(this._databaseSelectorTable.onDataChanged(() => { this._dbCount.updateProperties({ 'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length) }); - }); + })); const flex = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', height: '100%', @@ -271,42 +280,41 @@ export class DatabaseSelectorPage extends MigrationWizardPage { return result; } - private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer { + // private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer { + // const iconComponent = this._view.modelBuilder.image().withProps({ + // iconPath: icon, + // iconWidth: '16px', + // iconHeight: '16px', + // width: '20px', + // height: '20px' + // }).component(); + // const textComponent = this._view.modelBuilder.text().withProps({ + // value: text, + // title: text, + // CSSStyles: { + // 'margin': '0px', + // 'width': '110px' + // } + // }).component(); - const iconComponent = this._view.modelBuilder.image().withProps({ - iconPath: icon, - iconWidth: '16px', - iconHeight: '16px', - width: '20px', - height: '20px' - }).component(); - const textComponent = this._view.modelBuilder.text().withProps({ - value: text, - title: text, - CSSStyles: { - 'margin': '0px', - 'width': '110px' - } - }).component(); + // const cellContainer = this._view.modelBuilder.flexContainer().withProps({ + // CSSStyles: { + // 'justify-content': 'left' + // } + // }).component(); + // cellContainer.addItem(iconComponent, { + // flex: '0', + // CSSStyles: { + // 'width': '32px' + // } + // }); + // cellContainer.addItem(textComponent, { + // CSSStyles: { + // 'width': 'auto' + // } + // }); - const cellContainer = this._view.modelBuilder.flexContainer().withProps({ - CSSStyles: { - 'justify-content': 'left' - } - }).component(); - cellContainer.addItem(iconComponent, { - flex: '0', - CSSStyles: { - 'width': '32px' - } - }); - cellContainer.addItem(textComponent, { - CSSStyles: { - 'width': 'auto' - } - }); - - return cellContainer; - } + // return cellContainer; + // } } diff --git a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts index 61bac5e744..1f21cb935f 100644 --- a/extensions/sql-migration/src/wizard/integrationRuntimePage.ts +++ b/extensions/sql-migration/src/wizard/integrationRuntimePage.ts @@ -34,6 +34,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { private _copy2!: azdata.ButtonComponent; private _refresh1!: azdata.ButtonComponent; private _refresh2!: azdata.ButtonComponent; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel); @@ -50,14 +51,14 @@ export class IntergrationRuntimePage extends MigrationWizardPage { } }).component(); - createNewMigrationService.onDidClick(async (e) => { + this._disposables.push(createNewMigrationService.onDidClick(async (e) => { const dialog = new CreateSqlMigrationServiceDialog(); const createdDmsResult = await dialog.createNewDms(this.migrationStateModel, (this._resourceGroupDropdown.value).displayName); this.migrationStateModel._sqlMigrationServiceResourceGroup = createdDmsResult.resourceGroup; this.migrationStateModel._sqlMigrationService = createdDmsResult.service; await this.loadResourceGroupDropdown(); await this.populateDms(createdDmsResult.resourceGroup); - }); + })); this._statusLoadingComponent = view.modelBuilder.loadingComponent().withItem(this.createDMSDetailsContainer()).component(); @@ -91,6 +92,12 @@ export class IntergrationRuntimePage extends MigrationWizardPage { ] ); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(this._form.component()); } @@ -187,12 +194,12 @@ export class IntergrationRuntimePage extends MigrationWizardPage { fireOnTextChange: true, }).component(); - this._resourceGroupDropdown.onValueChanged(async (value) => { + this._disposables.push(this._resourceGroupDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._resourceGroupDropdown, value); if (selectedIndex > -1) { await this.populateDms(value); } - }); + })); const migrationServcieDropdownLabel = this._view.modelBuilder.text().withProps({ value: constants.IR_PAGE_TITLE, @@ -209,7 +216,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { fireOnTextChange: true, }).component(); - this._dmsDropdown.onValueChanged(async (value) => { + this._disposables.push(this._dmsDropdown.onValueChanged(async (value) => { if (value && value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) { if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) { this._dmsInfoContainer.display = 'inline'; @@ -225,7 +232,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage { } else { this._dmsInfoContainer.display = 'none'; } - }); + })); const flexContainer = this._view.modelBuilder.flexContainer().withItems([ descriptionText, @@ -267,14 +274,14 @@ export class IntergrationRuntimePage extends MigrationWizardPage { ariaLabel: constants.REFRESH, }).component(); - this._refreshButton.onDidClick(async (e) => { + this._disposables.push(this._refreshButton.onDidClick(async (e) => { this._connectionStatusLoader.loading = true; try { await this.loadStatus(); } finally { this._connectionStatusLoader.loading = false; } - }); + })); const connectionLabelContainer = this._view.modelBuilder.flexContainer().withProps({ CSSStyles: { @@ -318,10 +325,10 @@ export class IntergrationRuntimePage extends MigrationWizardPage { ariaLabel: constants.COPY_KEY1, }).component(); - this._copy1.onDidClick(async (e) => { + 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); - }); + })); this._copy2 = this._view.modelBuilder.button().withProps({ title: constants.COPY_KEY2, @@ -329,10 +336,10 @@ export class IntergrationRuntimePage extends MigrationWizardPage { ariaLabel: constants.COPY_KEY2, }).component(); - this._copy2.onDidClick(async (e) => { + 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); - }); + })); this._refresh1 = this._view.modelBuilder.button().withProps({ title: constants.REFRESH_KEY1, diff --git a/extensions/sql-migration/src/wizard/migrationModePage.ts b/extensions/sql-migration/src/wizard/migrationModePage.ts index cf6f7fa68c..928d38912f 100644 --- a/extensions/sql-migration/src/wizard/migrationModePage.ts +++ b/extensions/sql-migration/src/wizard/migrationModePage.ts @@ -11,6 +11,7 @@ import * as constants from '../constants/strings'; export class MigrationModePage extends MigrationWizardPage { private _view!: azdata.ModelView; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL, 'MigrationModePage'), migrationStateModel); @@ -25,6 +26,11 @@ export class MigrationModePage extends MigrationWizardPage { this.migrationModeContainer(), ] ); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); await view.initializeModel(form.component()); } @@ -64,11 +70,11 @@ export class MigrationModePage extends MigrationWizardPage { this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.ONLINE; - onlineButton.onDidChangeCheckedState((e) => { + this._disposables.push(onlineButton.onDidChangeCheckedState((e) => { if (e) { this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.ONLINE; } - }); + })); const offlineButton = this._view.modelBuilder.radioButton().withProps({ label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL, @@ -88,13 +94,13 @@ export class MigrationModePage extends MigrationWizardPage { }).component(); - offlineButton.onDidChangeCheckedState((e) => { + this._disposables.push(offlineButton.onDidChangeCheckedState((e) => { if (e) { vscode.window.showInformationMessage('Feature coming soon'); onlineButton.checked = true; //this.migrationStateModel._databaseBackup.migrationCutover = MigrationCutover.OFFLINE; TODO: Enable when offline mode is supported. } - }); + })); const flexContainer = this._view.modelBuilder.flexContainer().withItems( [ diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 2ba1be0339..5f3da71130 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationStateModel, MigrationTargetType, StateChangeEvent } from '../models/stateMachine'; import { AssessmentResultsDialog } from '../dialog/assessmentResults/assessmentResultsDialog'; import * as constants from '../constants/strings'; -import * as vscode from 'vscode'; import { EOL } from 'os'; import { IconPath, IconPathHelper } from '../constants/iconPathHelper'; import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController'; @@ -46,6 +46,7 @@ export class SKURecommendationPage extends MigrationWizardPage { private _databaseSelectedHelperText!: azdata.TextComponent; private assessmentGroupContainer!: azdata.FlexContainer; private _targetContainer!: azdata.FlexContainer; + private _disposables: vscode.Disposable[] = []; private _supportedProducts: Product[] = [ { @@ -148,6 +149,11 @@ export class SKURecommendationPage extends MigrationWizardPage { this._rootContainer.addItem(this._assessmentComponent, { flex: '0 0 auto' }); this._rootContainer.addItem(this._formContainer.component(), { flex: '0 0 auto' }); + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await this._view.initializeModel(this._rootContainer); } @@ -209,12 +215,12 @@ export class SKURecommendationPage extends MigrationWizardPage { }); }); - this._rbg.onSelectionChanged((value) => { + this._disposables.push(this._rbg.onSelectionChanged((value) => { if (value) { this.assessmentGroupContainer.display = 'inline'; this.changeTargetType(value.cardId); } - }); + })); this._rbgLoader = this._view.modelBuilder.loadingComponent().withItem( this._rbg @@ -247,7 +253,7 @@ export class SKURecommendationPage extends MigrationWizardPage { let miDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLMI); let vmDialog = new AssessmentResultsDialog('ownerUri', this.migrationStateModel, constants.ASSESSMENT_TILE(serverName), this, MigrationTargetType.SQLVM); - button.onDidClick(async (e) => { + this._disposables.push(button.onDidClick(async (e) => { if (this._rbg.selectedCardId === MigrationTargetType.SQLVM) { this._rbg.selectedCardId = MigrationTargetType.SQLVM; await vmDialog.openDialog(); @@ -255,7 +261,7 @@ export class SKURecommendationPage extends MigrationWizardPage { this._rbg.selectedCardId = MigrationTargetType.SQLMI; await miDialog.openDialog(); } - }); + })); this._databaseSelectedHelperText = this._view.modelBuilder.text().withProps({ CSSStyles: { @@ -296,7 +302,7 @@ export class SKURecommendationPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._managedInstanceSubscriptionDropdown.onValueChanged(async (value) => { + this._disposables.push(this._managedInstanceSubscriptionDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._managedInstanceSubscriptionDropdown, value); if (selectedIndex > -1) { this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex); @@ -304,7 +310,7 @@ export class SKURecommendationPage extends MigrationWizardPage { this.migrationStateModel._sqlMigrationService = undefined!; await this.populateLocationAndResourceGroupDropdown(); } - }); + })); const azureLocationLabel = this._view.modelBuilder.text().withProps({ value: constants.LOCATION, @@ -320,13 +326,13 @@ export class SKURecommendationPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._azureLocationDropdown.onValueChanged(async (value) => { + this._disposables.push(this._azureLocationDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._azureLocationDropdown, value); if (selectedIndex > -1) { this.migrationStateModel._location = this.migrationStateModel.getLocation(selectedIndex); await this.populateResourceInstanceDropdown(); } - }); + })); const azureResourceGroupLabel = this._view.modelBuilder.text().withProps({ value: constants.RESOURCE_GROUP, @@ -342,13 +348,13 @@ export class SKURecommendationPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._azureResourceGroupDropdown.onValueChanged(async (value) => { + this._disposables.push(this._azureResourceGroupDropdown.onValueChanged(async (value) => { const selectedIndex = findDropDownItemIndex(this._azureResourceGroupDropdown, value); if (selectedIndex > -1) { this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex); await this.populateResourceInstanceDropdown(); } - }); + })); this._resourceDropdownLabel = this._view.modelBuilder.text().withProps({ value: constants.MANAGED_INSTANCE, width: WIZARD_INPUT_COMPONENT_WIDTH, @@ -364,7 +370,7 @@ export class SKURecommendationPage extends MigrationWizardPage { editable: true, fireOnTextChange: true, }).component(); - this._resourceDropdown.onValueChanged(value => { + this._disposables.push(this._resourceDropdown.onValueChanged(value => { const selectedIndex = findDropDownItemIndex(this._resourceDropdown, value); if (selectedIndex > -1 && value !== constants.NO_MANAGED_INSTANCE_FOUND && @@ -376,7 +382,7 @@ export class SKURecommendationPage extends MigrationWizardPage { this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(selectedIndex); } } - }); + })); return this._view.modelBuilder.flexContainer().withItems( [ diff --git a/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts b/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts index 74d8524678..299d93ccc2 100644 --- a/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts +++ b/extensions/sql-migration/src/wizard/sqlSourceConfigurationPage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationSourceAuthenticationType, MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import * as constants from '../constants/strings'; @@ -13,6 +14,7 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage { private _view!: azdata.ModelView; private _usernameInput!: azdata.InputBoxComponent; private _password!: azdata.InputBoxComponent; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.SOURCE_CONFIGURATION, 'MigrationModePage'), migrationStateModel); @@ -26,6 +28,12 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage { await this.createSourceCredentialContainer(), ] ); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(form.component()); } @@ -107,9 +115,9 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage { enabled: false, width: WIZARD_INPUT_COMPONENT_WIDTH }).component(); - this._usernameInput.onTextChanged(value => { + this._disposables.push(this._usernameInput.onTextChanged(value => { this.migrationStateModel._sqlServerUsername = value; - }); + })); const passwordLabel = this._view.modelBuilder.text().withProps({ value: constants.DATABASE_BACKUP_NETWORK_SHARE_PASSWORD_LABEL, @@ -125,9 +133,9 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage { inputType: 'password', width: WIZARD_INPUT_COMPONENT_WIDTH }).component(); - this._password.onTextChanged(value => { + this._disposables.push(this._password.onTextChanged(value => { this.migrationStateModel._sqlServerPassword = value; - }); + })); const container = this._view.modelBuilder.flexContainer().withItems( [ diff --git a/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts index 42d04ac435..8a4c07f061 100644 --- a/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts @@ -179,7 +179,7 @@ export class SubscriptionSelectionPage extends MigrationWizardPage { } public async onPageLeave(): Promise { - this.disposables.forEach(d => d.dispose()); + this.disposables.forEach(d => { try { d.dispose(); } catch { } }); } protected async handleStateChange(e: StateChangeEvent): Promise { diff --git a/extensions/sql-migration/src/wizard/summaryPage.ts b/extensions/sql-migration/src/wizard/summaryPage.ts index a5f4ec9204..ea8abd0795 100644 --- a/extensions/sql-migration/src/wizard/summaryPage.ts +++ b/extensions/sql-migration/src/wizard/summaryPage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; +import * as vscode from 'vscode'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationMode, MigrationStateModel, MigrationTargetType, NetworkContainerType, StateChangeEvent } from '../models/stateMachine'; import * as constants from '../constants/strings'; @@ -14,6 +15,7 @@ import { TargetDatabaseSummaryDialog } from '../dialog/targetDatabaseSummary/tar export class SummaryPage extends MigrationWizardPage { private _view!: azdata.ModelView; private _flexContainer!: azdata.FlexContainer; + private _disposables: vscode.Disposable[] = []; constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { super(wizard, azdata.window.createWizardPage(constants.SUMMARY_PAGE_TITLE), migrationStateModel); @@ -32,6 +34,12 @@ export class SummaryPage extends MigrationWizardPage { } ] ); + + this._disposables.push(this._view.onClosed(e => { + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }); + })); + await view.initializeModel(form.component()); } @@ -48,9 +56,9 @@ export class SummaryPage extends MigrationWizardPage { } }).component(); - targetDatabaseHyperlink.onDidClick(e => { + this._disposables.push(targetDatabaseHyperlink.onDidClick(e => { targetDatabaseSummary.initialize(); - }); + })); const targetDatabaseRow = this._view.modelBuilder.flexContainer() .withLayout( diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts index 86071fa0e5..ea4138037f 100644 --- a/extensions/sql-migration/src/wizard/wizardController.ts +++ b/extensions/sql-migration/src/wizard/wizardController.ts @@ -9,7 +9,6 @@ import { MigrationStateModel } from '../models/stateMachine'; import * as loc from '../constants/strings'; import { MigrationWizardPage } from '../models/migrationWizardPage'; import { SKURecommendationPage } from './skuRecommendationPage'; -// import { SubscriptionSelectionPage } from './subscriptionSelectionPage'; import { DatabaseBackupPage } from './databaseBackupPage'; import { AccountsSelectionPage } from './accountsSelectionPage'; import { IntergrationRuntimePage } from './integrationRuntimePage'; @@ -61,13 +60,13 @@ export class WizardController { wizardSetupPromises.push(...pages.map(p => p.registerWizardContent())); wizardSetupPromises.push(wizard.open()); - wizard.onPageChanged(async (pageChangeInfo: azdata.window.WizardPageChangeInfo) => { + this.extensionContext.subscriptions.push(wizard.onPageChanged(async (pageChangeInfo: azdata.window.WizardPageChangeInfo) => { const newPage = pageChangeInfo.newPage; const lastPage = pageChangeInfo.lastPage; await pages[lastPage]?.onPageLeave(); await pages[newPage]?.onPageEnter(); - }); + })); wizard.registerNavigationValidator(async validator => { // const lastPage = validator.lastPage; @@ -82,9 +81,9 @@ export class WizardController { await Promise.all(wizardSetupPromises); await pages[0].onPageEnter(); - wizard.doneButton.onClick(async (e) => { + this.extensionContext.subscriptions.push(wizard.doneButton.onClick(async (e) => { await stateModel.startMigration(); - }); + })); } }