From 6556eaa1ed110535fed9f8b9d19ed69014bc0021 Mon Sep 17 00:00:00 2001 From: Ram Uday Kumar <95093687+Ramudaykumar@users.noreply.github.com> Date: Fri, 1 Sep 2023 10:34:36 +0530 Subject: [PATCH] Revalidate failed IR Validation steps (#24237) --- extensions/sql-migration/images/redo.svg | 3 + .../src/constants/iconPathHelper.ts | 5 + .../sql-migration/src/constants/strings.ts | 1 + .../validationResults/validateIrDialog.ts | 117 +++++++++++++++--- 4 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 extensions/sql-migration/images/redo.svg diff --git a/extensions/sql-migration/images/redo.svg b/extensions/sql-migration/images/redo.svg new file mode 100644 index 0000000000..9d982e5520 --- /dev/null +++ b/extensions/sql-migration/images/redo.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/sql-migration/src/constants/iconPathHelper.ts b/extensions/sql-migration/src/constants/iconPathHelper.ts index 7f6c03665a..6beca19ed0 100644 --- a/extensions/sql-migration/src/constants/iconPathHelper.ts +++ b/extensions/sql-migration/src/constants/iconPathHelper.ts @@ -40,6 +40,7 @@ export class IconPathHelper { public static emptyTable: IconPath; public static addAzureAccount: IconPath; public static retry: IconPath; + public static redo: IconPath; public static edit: IconPath; public static restartDataCollection: IconPath; public static stop: IconPath; @@ -167,6 +168,10 @@ export class IconPathHelper { light: context.asAbsolutePath('images/retry.svg'), dark: context.asAbsolutePath('images/retry.svg') }; + IconPathHelper.redo = { + light: context.asAbsolutePath('images/redo.svg'), + dark: context.asAbsolutePath('images/redo.svg') + }; IconPathHelper.edit = { light: context.asAbsolutePath('images/edit.svg'), dark: context.asAbsolutePath('images/edit.svg') diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 261351bb46..17b215952c 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -802,6 +802,7 @@ export const VALIDATION_STATE_FAILED = localize('sql.migration.validation.state. export const VALIDATE_IR_DONE_BUTTON = localize('sql.migration.validate.ir.done.button', "Done"); export const VALIDATE_IR_HEADING = localize('sql.migration.validate.ir.heading', "We are validating the following:"); export const VALIDATE_IR_START_VALIDATION = localize('sql.migration.validate.ir.start.validation', "Start validation"); +export const VALIDATE_IR_FAILED_REVALIDATION = localize('sql.migration.validate.ir.failed.revalidation', "Revalidate failed steps"); export const VALIDATE_IR_STOP_VALIDATION = localize('sql.migration.validate.ir.stop.validation', "Stop validation"); export const VALIDATE_IR_COPY_RESULTS = localize('sql.migration.validate.ir.copy.results', "Copy validation results"); export const VALIDATE_IR_RESULTS_HEADING = localize('sql.migration.validate.ir.results.heading', "Validation step details"); diff --git a/extensions/sql-migration/src/dialog/validationResults/validateIrDialog.ts b/extensions/sql-migration/src/dialog/validationResults/validateIrDialog.ts index ee6d561d89..2b2915af39 100644 --- a/extensions/sql-migration/src/dialog/validationResults/validateIrDialog.ts +++ b/extensions/sql-migration/src/dialog/validationResults/validateIrDialog.ts @@ -50,6 +50,7 @@ export class ValidateIrDialog { private _resultsTable!: azdata.TableComponent; private _startLoader!: azdata.LoadingComponent; private _startButton!: azdata.ButtonComponent; + private _revalidationButton!: azdata.ButtonComponent; private _cancelButton!: azdata.ButtonComponent; private _copyButton!: azdata.ButtonComponent; private _validationResult: any[][] = []; @@ -144,6 +145,17 @@ export class ValidateIrDialog { label: constants.VALIDATE_IR_STOP_VALIDATION, enabled: false, }).component(); + + this._revalidationButton = view.modelBuilder.button() + .withProps({ + iconPath: IconPathHelper.redo, + iconHeight: 18, + iconWidth: 18, + width: 150, + label: constants.VALIDATE_IR_FAILED_REVALIDATION, + enabled: false, + }).component(); + this._copyButton = view.modelBuilder.button() .withProps({ iconPath: IconPathHelper.copy, @@ -157,6 +169,10 @@ export class ValidateIrDialog { this._disposables.push( this._startButton.onDidClick( async (e) => await this._runValidation())); + + this._disposables.push( + this._revalidationButton.onDidClick( + async (e) => await this._runFailedRevalidation())); this._disposables.push( this._cancelButton.onDidClick( e => { @@ -172,6 +188,7 @@ export class ValidateIrDialog { .withToolbarItems([ { component: this._startButton }, { component: this._cancelButton }, + { component: this._revalidationButton }, { component: this._copyButton }]) .component(); @@ -262,6 +279,7 @@ export class ValidateIrDialog { try { this._startLoader.loading = true; this._startButton.enabled = false; + this._revalidationButton.enabled = false; this._cancelButton.enabled = true; this._copyButton.enabled = false; this._dialog!.okButton.enabled = false; @@ -274,6 +292,30 @@ export class ValidateIrDialog { } finally { this._startLoader.loading = false; this._startButton.enabled = true; + this._revalidationButton.enabled = !this._model.isIrTargetValidated; + this._cancelButton.enabled = false; + this._copyButton.enabled = true; + this._dialog!.okButton.enabled = this._model.isIrTargetValidated; + this._dialog!.cancelButton.enabled = !this._model.isIrTargetValidated; + } + } + + private async _runFailedRevalidation(results?: ValidationResult[]): Promise { + try { + this._startLoader.loading = true; + this._startButton.enabled = false; + this._revalidationButton.enabled = false; + this._cancelButton.enabled = true; + this._copyButton.enabled = false; + this._dialog!.okButton.enabled = false; + this._dialog!.cancelButton.enabled = true; + if (!this._model.isIrTargetValidated) { + await this._revalidate(); + } + } finally { + this._startLoader.loading = false; + this._startButton.enabled = true; + this._revalidationButton.enabled = !this._model.isIrTargetValidated; this._cancelButton.enabled = false; this._copyButton.enabled = true; this._dialog!.okButton.enabled = this._model.isIrTargetValidated; @@ -373,7 +415,19 @@ export class ValidateIrDialog { this._saveResults(); } - private async _validateDatabaseMigration(): Promise { + private async _revalidate(): Promise { + await this._initIrResultsForRevalidation(); + + if (this._model.isSqlDbTarget) { + await this._validateSqlDbMigration(true); + } else { + await this._validateDatabaseMigration(true); + } + + this._saveResults(); + } + + private async _validateDatabaseMigration(skipSuccessfulSteps: boolean = false): Promise { const currentConnection = await getSourceConnectionProfile(); const sourceServerName = currentConnection?.serverName!; const encryptConnection = getEncryptConnectionValue(currentConnection); @@ -426,17 +480,21 @@ export class ValidateIrDialog { }; // validate integration runtime (IR) is online - if (!await validate(sourceDatabaseName, networkShare, true, false, false, false)) { - this._canceled = true; - await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]) - return; + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + if (!await validate(sourceDatabaseName, networkShare, true, false, false, false)) { + this._canceled = true; + await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]) + return; + } } testNumber++; // validate blob container connectivity - if (!await validate(sourceDatabaseName, networkShare, false, false, false, true)) { - await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]) - return; + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + if (!await validate(sourceDatabaseName, networkShare, false, false, false, true)) { + await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]) + return; + } } for (let i = 0; i < databaseCount; i++) { @@ -448,14 +506,18 @@ export class ValidateIrDialog { break; } // validate source connectivity - await validate(sourceDatabaseName, networkShare, false, false, true, false); + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + await validate(sourceDatabaseName, networkShare, false, false, true, false); + } testNumber++; if (this._canceled) { await this._updateValidateIrResults(testNumber, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]) break; } // valdiate source location / network share connectivity - await validate(sourceDatabaseName, networkShare, false, true, false, false); + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + await validate(sourceDatabaseName, networkShare, false, true, false, false); + } } } @@ -469,7 +531,7 @@ export class ValidateIrDialog { return error; } - private async _validateSqlDbMigration(): Promise { + private async _validateSqlDbMigration(skipSuccessfulSteps: boolean = false): Promise { const currentConnection = await getSourceConnectionProfile(); const sourceServerName = currentConnection?.serverName!; const encryptConnection = getEncryptConnectionValue(currentConnection); @@ -521,10 +583,12 @@ export class ValidateIrDialog { }; // validate IR is online - if (!await validate(sourceDatabaseName, targetDatabaseName, true, false, false)) { - this._canceled = true; - await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]); - return; + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + if (!await validate(sourceDatabaseName, targetDatabaseName, true, false, false)) { + this._canceled = true; + await this._updateValidateIrResults(testNumber + 1, ValidateIrState.Canceled, [constants.VALIDATE_IR_VALIDATION_CANCELED]); + return; + } } for (let i = 0; i < databaseCount; i++) { @@ -537,7 +601,9 @@ export class ValidateIrDialog { break; } // validate source connectivity - await validate(sourceDatabaseName, targetDatabaseName, false, true, false); + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + await validate(sourceDatabaseName, targetDatabaseName, false, true, false); + } testNumber++; if (this._canceled) { @@ -545,7 +611,9 @@ export class ValidateIrDialog { break; } // validate target connectivity - await validate(sourceDatabaseName, targetDatabaseName, false, false, true); + if (!skipSuccessfulSteps || this._validationResult[testNumber][ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + await validate(sourceDatabaseName, targetDatabaseName, false, false, true); + } } } @@ -584,6 +652,21 @@ export class ValidateIrDialog { await this._resultsTable.updateProperty('data', data); } + private async _initIrResultsForRevalidation(results?: ValidationResult[]): Promise { + this._valdiationErrors = []; + this._canceled = false; + let testNumber: number = 0; + + this._validationResult.forEach(async element => { + if (element[ValidationResultIndex.state] !== ValidateIrState.Succeeded) { + await this._updateValidateIrResults(testNumber++, ValidateIrState.Pending); + } + else { + testNumber++; + } + }); + } + private async _initSqlDbIrResults(results?: ValidationResult[]): Promise { this._validationResult = []; this._addValidationResult(constants.VALIDATE_IR_VALIDATION_RESULT_LABEL_SHIR);