mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-20 17:22:51 -05:00
Add IR Migration configuration Validation to SQL Migration extension (#21386)
* re-factor and consolidate wizard pages * validation WIP 11/10 * validate ir dialog * navigation fixes * bump version to 1.2.0 * add resource strings and fix navigatin issue * map validation state to resource string clean up * address review comments * fix typos, address review comments * address review feedback, readability * fix res string, validation check, col width * bug fixes, nav, sqldb migration * fix nav/refresh/visibility issues * fix nav issues, cancel pending validation items * update error text / position * fix localization bug
This commit is contained in:
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
|
||||
import { EOL } from 'os';
|
||||
import { getStorageAccountAccessKeys } from '../api/azure';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { Blob, MigrationMode, MigrationSourceAuthenticationType, MigrationStateModel, MigrationTargetType, NetworkContainerType, NetworkShare, StateChangeEvent } from '../models/stateMachine';
|
||||
import { Blob, MigrationMode, MigrationSourceAuthenticationType, MigrationStateModel, MigrationTargetType, NetworkContainerType, NetworkShare, StateChangeEvent, ValidateIrState, ValidationResult } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
@@ -16,9 +16,11 @@ import * as utils from '../api/utils';
|
||||
import { logError, TelemetryViews } from '../telemtery';
|
||||
import * as styles from '../constants/styles';
|
||||
import { TableMigrationSelectionDialog } from '../dialog/tableMigrationSelection/tableMigrationSelectionDialog';
|
||||
import { ValidateIrDialog } from '../dialog/validationResults/validateIrDialog';
|
||||
|
||||
const WIZARD_TABLE_COLUMN_WIDTH = '200px';
|
||||
const WIZARD_TABLE_COLUMN_WIDTH_SMALL = '170px';
|
||||
const VALIDATE_IR_CUSTOM_BUTTON_INDEX = 0;
|
||||
|
||||
const blobResourceGroupErrorStrings = [constants.RESOURCE_GROUP_NOT_FOUND];
|
||||
const blobStorageAccountErrorStrings = [constants.NO_STORAGE_ACCOUNT_FOUND, constants.SELECT_RESOURCE_GROUP_PROMPT];
|
||||
@@ -28,9 +30,6 @@ const blobFileErrorStrings = [constants.NO_BLOBFILES_FOUND, constants.SELECT_BLO
|
||||
export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
|
||||
private _networkShareButton!: azdata.RadioButtonComponent;
|
||||
private _blobContainerButton!: azdata.RadioButtonComponent;
|
||||
|
||||
private _sourceConnectionContainer!: azdata.FlexContainer;
|
||||
private _networkShareContainer!: azdata.FlexContainer;
|
||||
private _windowsUserAccountText!: azdata.InputBoxComponent;
|
||||
@@ -62,7 +61,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private _networkShareTargetDatabaseNames: azdata.InputBoxComponent[] = [];
|
||||
private _blobContainerTargetDatabaseNames: azdata.InputBoxComponent[] = [];
|
||||
private _networkShareLocations: azdata.InputBoxComponent[] = [];
|
||||
private _radioButtonContainer!: azdata.FlexContainer;
|
||||
private _networkDetailsContainer!: azdata.FlexContainer;
|
||||
|
||||
private _existingDatabases: string[] = [];
|
||||
@@ -81,21 +79,24 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
this._view = view;
|
||||
|
||||
this._radioButtonContainer = this.createBackupLocationComponent();
|
||||
this._sourceConnectionContainer = this.createSourceCredentialsContainer();
|
||||
this._networkDetailsContainer = this.createNetworkDetailsContainer();
|
||||
this._targetDatabaseContainer = this.createTargetDatabaseContainer();
|
||||
this._networkShareStorageAccountDetails = this.createNetworkShareStorageAccountDetailsContainer();
|
||||
this._migrationTableSection = this._migrationTableSelectionContainer();
|
||||
|
||||
this._disposables.push(
|
||||
this.wizard.customButtons[VALIDATE_IR_CUSTOM_BUTTON_INDEX].onClick(
|
||||
async e => await this._validateIr()));
|
||||
|
||||
const form = this._view.modelBuilder.formContainer()
|
||||
.withFormItems([
|
||||
{ title: '', component: this._radioButtonContainer },
|
||||
{ title: '', component: this._sourceConnectionContainer },
|
||||
{ title: '', component: this._networkDetailsContainer },
|
||||
{ title: '', component: this._migrationTableSection },
|
||||
{ title: '', component: this._networkShareStorageAccountDetails },
|
||||
{ title: '', component: this._targetDatabaseContainer },
|
||||
{ title: '', component: this._networkShareStorageAccountDetails }])
|
||||
{ title: '', component: this._migrationTableSection },
|
||||
])
|
||||
.withProps({ CSSStyles: { 'padding-top': '0' } })
|
||||
.component();
|
||||
|
||||
@@ -108,56 +109,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
await view.initializeModel(form);
|
||||
}
|
||||
|
||||
private createBackupLocationComponent(): azdata.FlexContainer {
|
||||
const buttonGroup = 'networkContainer';
|
||||
|
||||
const selectLocationText = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_PAGE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.BODY_CSS }
|
||||
}).component();
|
||||
|
||||
this._networkShareButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
name: buttonGroup,
|
||||
label: constants.DATABASE_BACKUP_NC_NETWORK_SHARE_RADIO_LABEL,
|
||||
checked: this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE,
|
||||
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
||||
}).component();
|
||||
|
||||
this._disposables.push(
|
||||
this._networkShareButton.onDidChangeCheckedState(async checked => {
|
||||
if (checked) {
|
||||
await this.switchNetworkContainerFields(NetworkContainerType.NETWORK_SHARE);
|
||||
}
|
||||
}));
|
||||
|
||||
this._blobContainerButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
name: buttonGroup,
|
||||
label: constants.DATABASE_BACKUP_NC_BLOB_STORAGE_RADIO_LABEL,
|
||||
checked: this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER,
|
||||
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
||||
}).component();
|
||||
|
||||
this._disposables.push(
|
||||
this._blobContainerButton.onDidChangeCheckedState(async checked => {
|
||||
if (checked) {
|
||||
await this.switchNetworkContainerFields(NetworkContainerType.BLOB_CONTAINER);
|
||||
}
|
||||
}));
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
selectLocationText,
|
||||
this._networkShareButton,
|
||||
this._blobContainerButton])
|
||||
.withLayout({ flexFlow: 'column' })
|
||||
.component();
|
||||
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
private createNetworkDetailsContainer(): azdata.FlexContainer {
|
||||
this._networkShareContainer = this.createNetworkShareContainer();
|
||||
this._blobContainer = this.createBlobContainer();
|
||||
@@ -200,7 +151,10 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._sqlSourceUsernameInput.onTextChanged(
|
||||
value => this.migrationStateModel._sqlServerUsername = value));
|
||||
value => {
|
||||
this.migrationStateModel._sqlServerUsername = value;
|
||||
this._resetValidationUI();
|
||||
}));
|
||||
|
||||
const sqlPasswordLabel = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
@@ -218,7 +172,10 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._sqlSourcePassword.onTextChanged(
|
||||
value => this.migrationStateModel._sqlServerPassword = value));
|
||||
value => {
|
||||
this.migrationStateModel._sqlServerPassword = value;
|
||||
this._resetValidationUI();
|
||||
}));
|
||||
|
||||
return this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
@@ -273,7 +230,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
CSSStyles: { ...styles.BODY_CSS, 'margin-top': '-1em' }
|
||||
})
|
||||
.withValidation((component) => {
|
||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if (this.migrationStateModel.isBackupContainerNetworkShare) {
|
||||
if (component.value) {
|
||||
if (!/^[A-Za-z0-9\\\._-]{7,}$/.test(component.value)) {
|
||||
return false;
|
||||
@@ -287,6 +244,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
for (let i = 0; i < this.migrationStateModel._databaseBackup.networkShares.length; i++) {
|
||||
this.migrationStateModel._databaseBackup.networkShares[i].windowsUser = value;
|
||||
}
|
||||
this._resetValidationUI();
|
||||
}));
|
||||
|
||||
const passwordLabel = this._view.modelBuilder.text()
|
||||
@@ -309,6 +267,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
for (let i = 0; i < this.migrationStateModel._databaseBackup.networkShares.length; i++) {
|
||||
this.migrationStateModel._databaseBackup.networkShares[i].password = value;
|
||||
}
|
||||
this._resetValidationUI();
|
||||
}));
|
||||
|
||||
return this._view.modelBuilder.flexContainer()
|
||||
@@ -325,6 +284,13 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
.component();
|
||||
}
|
||||
|
||||
private _resetValidationUI(): void {
|
||||
if (this.wizard.message.level === azdata.window.MessageLevel.Information) {
|
||||
this.wizard.message = { text: '' };
|
||||
}
|
||||
this.migrationStateModel.resetIrValidationResults();
|
||||
}
|
||||
|
||||
private createBlobContainer(): azdata.FlexContainer {
|
||||
const blobHeading = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
@@ -612,6 +578,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
for (let i = 0; i < this.migrationStateModel._databaseBackup.networkShares.length; i++) {
|
||||
this.migrationStateModel._databaseBackup.networkShares[i].storageAccount = selectedStorageAccount;
|
||||
}
|
||||
this.migrationStateModel.resetIrValidationResults();
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -658,40 +625,130 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
.component();
|
||||
}
|
||||
|
||||
private async _updatePageControlsVisibility(containerType: NetworkContainerType): Promise<void> {
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
const isNetworkShare = containerType === NetworkContainerType.NETWORK_SHARE;
|
||||
const isBlobContainer = containerType === NetworkContainerType.BLOB_CONTAINER;
|
||||
private async _updatePageControlsVisibility(): Promise<void> {
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
const isNetworkShare = this.migrationStateModel.isBackupContainerNetworkShare;
|
||||
const isBlobContainer = this.migrationStateModel.isBackupContainerBlobContainer;
|
||||
|
||||
await utils.updateControlDisplay(this._sourceConnectionContainer, isSqlDbTarget || isNetworkShare);
|
||||
await utils.updateControlDisplay(this._migrationTableSection, isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._radioButtonContainer, !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._networkDetailsContainer, !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._targetDatabaseContainer, !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._networkShareStorageAccountDetails, !isSqlDbTarget);
|
||||
|
||||
await utils.updateControlDisplay(this._networkShareContainer, isNetworkShare);
|
||||
await utils.updateControlDisplay(this._networkShareStorageAccountDetails, isNetworkShare);
|
||||
await utils.updateControlDisplay(this._networkTableContainer, isNetworkShare);
|
||||
await utils.updateControlDisplay(this._blobContainer, isBlobContainer);
|
||||
await utils.updateControlDisplay(this._blobTableContainer, isBlobContainer);
|
||||
await utils.updateControlDisplay(this._networkShareContainer, isNetworkShare && !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._networkShareStorageAccountDetails, isNetworkShare && !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._networkTableContainer, isNetworkShare && !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._blobContainer, isBlobContainer && !isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._blobTableContainer, isBlobContainer && !isSqlDbTarget);
|
||||
|
||||
await this._windowsUserAccountText.updateProperties({ required: isNetworkShare });
|
||||
await this._passwordText.updateProperties({ required: isNetworkShare });
|
||||
await this._windowsUserAccountText.updateProperties({ required: isNetworkShare && !isSqlDbTarget });
|
||||
await this._passwordText.updateProperties({ required: isNetworkShare && !isSqlDbTarget });
|
||||
await this._sqlSourceUsernameInput.updateProperties({ required: isNetworkShare || isSqlDbTarget });
|
||||
await this._sqlSourcePassword.updateProperties({ required: isNetworkShare || isSqlDbTarget });
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
||||
this._networkShareButton.checked = this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE;
|
||||
this._blobContainerButton.checked = this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER;
|
||||
await this._updatePageControlsVisibility(this.migrationStateModel._databaseBackup.networkContainerType);
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
this.wizard.message = { text: '' };
|
||||
const errors: string[] = [];
|
||||
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
if (isSqlDbTarget) {
|
||||
if (!this._validateTableSelection()) {
|
||||
errors.push(constants.DATABASE_TABLE_VALIDATE_SELECTION_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
switch (this.migrationStateModel._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
if ((<azdata.CategoryValue>this._networkShareStorageAccountResourceGroupDropdown.value)?.displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._networkShareContainerStorageAccountDropdown.value)?.displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
this._blobContainerResourceGroupDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.RESOURCE_GROUP_NOT_FOUND])) {
|
||||
errors.push(constants.INVALID_BLOB_RESOURCE_GROUP_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
this._blobContainerStorageAccountDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_STORAGE_ACCOUNT_FOUND, constants.SELECT_RESOURCE_GROUP_PROMPT])) {
|
||||
errors.push(constants.INVALID_BLOB_STORAGE_ACCOUNT_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
this._blobContainerDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_BLOBCONTAINERS_FOUND, constants.SELECT_STORAGE_ACCOUNT])) {
|
||||
errors.push(constants.INVALID_BLOB_CONTAINER_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
|
||||
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
||||
this._blobContainerLastBackupFileDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_BLOBFILES_FOUND, constants.SELECT_BLOB_CONTAINER])) {
|
||||
errors.push(constants.INVALID_BLOB_LAST_BACKUP_FILE_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
const duplicates: Map<string, number[]> = new Map();
|
||||
for (let i = 0; i < this.migrationStateModel._targetDatabaseNames.length; i++) {
|
||||
const blobContainerId = this.migrationStateModel._databaseBackup.blobs[i].blobContainer?.id;
|
||||
if (duplicates.has(blobContainerId)) {
|
||||
duplicates.get(blobContainerId)?.push(i);
|
||||
} else {
|
||||
duplicates.set(blobContainerId, [i]);
|
||||
}
|
||||
}
|
||||
duplicates.forEach((d) => {
|
||||
if (d.length > 1) {
|
||||
const dupString = `${d.map(index => this.migrationStateModel._databasesForMigration[index]).join(', ')}`;
|
||||
errors.push(constants.PROVIDE_UNIQUE_CONTAINERS + dupString);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.migrationStateModel.isSqlMiTarget) {
|
||||
this.migrationStateModel._targetDatabaseNames.forEach(t => {
|
||||
// Making sure if database with same name is not present on the target Azure SQL
|
||||
if (this._existingDatabases.includes(t)) {
|
||||
errors.push(constants.DATABASE_ALREADY_EXISTS_MI(t, this.migrationStateModel._targetServerInstance.name));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.wizard.message = {
|
||||
text: errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
if (errors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.migrationStateModel.isIrMigration) {
|
||||
this.wizard.nextButton.enabled = this.migrationStateModel.isIrTargetValidated;
|
||||
this.updateValidationResultUI();
|
||||
return this.migrationStateModel.isIrTargetValidated;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
this.wizard.customButtons[VALIDATE_IR_CUSTOM_BUTTON_INDEX].hidden = !this.migrationStateModel.isIrMigration;
|
||||
await this._updatePageControlsVisibility();
|
||||
|
||||
if (this.migrationStateModel.refreshDatabaseBackupPage) {
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
if (isSqlDbTarget) {
|
||||
this.wizardPage.title = constants.DATABASE_TABLE_SELECTION_LABEL;
|
||||
this.wizardPage.description = constants.DATABASE_TABLE_SELECTION_LABEL;
|
||||
@@ -700,7 +757,15 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
try {
|
||||
const isOfflineMigration = this.migrationStateModel._databaseBackup?.migrationMode === MigrationMode.OFFLINE;
|
||||
const lastBackupFileColumnIndex = this._blobContainerTargetDatabaseNamesTable.columns.length - 1;
|
||||
this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = !isOfflineMigration;
|
||||
const oldHidden = this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden;
|
||||
const newHidden = !isOfflineMigration;
|
||||
if (oldHidden !== newHidden) {
|
||||
// clear values prior to hiding columns if changing column visibility
|
||||
// to prevent null DeclarativeTableComponent - exception / _view null
|
||||
await this._blobContainerTargetDatabaseNamesTable.setDataValues([]);
|
||||
}
|
||||
|
||||
this._blobContainerTargetDatabaseNamesTable.columns[lastBackupFileColumnIndex].hidden = newHidden;
|
||||
this._blobContainerTargetDatabaseNamesTable.columns.forEach(column => {
|
||||
column.width = isOfflineMigration
|
||||
? WIZARD_TABLE_COLUMN_WIDTH_SMALL
|
||||
@@ -712,26 +777,37 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
(await this.migrationStateModel.getSourceConnectionProfile()).providerId,
|
||||
azdata.DataProviderType.QueryProvider);
|
||||
|
||||
const query = 'select SUSER_NAME()';
|
||||
const results = await queryProvider.runQueryAndReturn(
|
||||
await (azdata.connection.getUriForConnection(
|
||||
this.migrationStateModel.sourceConnectionId)), query);
|
||||
let username = '';
|
||||
try {
|
||||
const query = 'select SUSER_NAME()';
|
||||
const ownerUri = await azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId);
|
||||
const results = await queryProvider.runQueryAndReturn(ownerUri, query);
|
||||
username = results.rows[0][0]?.displayValue;
|
||||
} catch (e) {
|
||||
username = connectionProfile.userName;
|
||||
}
|
||||
|
||||
const username = results.rows[0][0].displayValue;
|
||||
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin'
|
||||
? MigrationSourceAuthenticationType.Sql
|
||||
: connectionProfile.authenticationType === 'Integrated' // TODO: use azdata.connection.AuthenticationType.Integrated after next ADS release
|
||||
? MigrationSourceAuthenticationType.Integrated
|
||||
: undefined!;
|
||||
this.migrationStateModel._authenticationType =
|
||||
connectionProfile.authenticationType === azdata.connection.AuthenticationType.SqlLogin
|
||||
? MigrationSourceAuthenticationType.Sql
|
||||
: connectionProfile.authenticationType === azdata.connection.AuthenticationType.Integrated
|
||||
? MigrationSourceAuthenticationType.Integrated
|
||||
: undefined!;
|
||||
this._sourceHelpText.value = constants.SQL_SOURCE_DETAILS(
|
||||
this.migrationStateModel._authenticationType,
|
||||
connectionProfile.serverName);
|
||||
|
||||
this._sqlSourceUsernameInput.value = username;
|
||||
this._sqlSourcePassword.value = (await azdata.connection.getCredentials(this.migrationStateModel.sourceConnectionId)).password;
|
||||
this._windowsUserAccountText.value = this.migrationStateModel.savedInfo?.networkShares
|
||||
? this.migrationStateModel.savedInfo?.networkShares[0]?.windowsUser
|
||||
: '';
|
||||
|
||||
this._windowsUserAccountText.value =
|
||||
this.migrationStateModel._databaseBackup.networkShares[0]?.windowsUser
|
||||
?? this.migrationStateModel.savedInfo?.networkShares[0]?.windowsUser
|
||||
?? '';
|
||||
this._passwordText.value =
|
||||
this.migrationStateModel._databaseBackup.networkShares[0]?.password
|
||||
?? this.migrationStateModel.savedInfo?.networkShares[0]?.password
|
||||
?? '';
|
||||
|
||||
this._networkShareTargetDatabaseNames = [];
|
||||
this._networkShareLocations = [];
|
||||
@@ -741,7 +817,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._blobContainerDropdowns = [];
|
||||
this._blobContainerLastBackupFileDropdowns = [];
|
||||
|
||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
|
||||
if (this.migrationStateModel.isSqlMiTarget) {
|
||||
this._existingDatabases = await this.migrationStateModel.getManagedDatabases();
|
||||
}
|
||||
|
||||
@@ -803,7 +879,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
return false;
|
||||
}
|
||||
// Making sure if database with same name is not present on the target Azure SQL
|
||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) {
|
||||
if (this.migrationStateModel.isSqlMiTarget && this._existingDatabases.includes(c.value!)) {
|
||||
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
||||
return false;
|
||||
}
|
||||
@@ -816,7 +892,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
targetDatabaseInput.onTextChanged(async (value) => {
|
||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||
await this.validateFields();
|
||||
this._resetValidationUI();
|
||||
await this.validateFields(targetDatabaseInput);
|
||||
}));
|
||||
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
||||
this._networkShareTargetDatabaseNames.push(targetDatabaseInput);
|
||||
@@ -828,7 +905,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION,
|
||||
width: '300px'
|
||||
}).withValidation(c => {
|
||||
if (this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if (this.migrationStateModel.isBackupContainerNetworkShare) {
|
||||
if (c.value) {
|
||||
if (!/^[\\\/]{2,}[^\\\/]+[\\\/]+[^\\\/]+/.test(c.value)) {
|
||||
return false;
|
||||
@@ -840,7 +917,8 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
networkShareLocationInput.onTextChanged(async (value) => {
|
||||
this.migrationStateModel._databaseBackup.networkShares[index].networkShareLocation = value.trim();
|
||||
await this.validateFields();
|
||||
this._resetValidationUI();
|
||||
await this.validateFields(networkShareLocationInput);
|
||||
}));
|
||||
networkShareLocationInput.value = this.migrationStateModel._databaseBackup.networkShares[index]?.networkShareLocation;
|
||||
this._networkShareLocations.push(networkShareLocationInput);
|
||||
@@ -856,7 +934,7 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
return false;
|
||||
}
|
||||
// Making sure if database with same name is not present on the target Azure SQL
|
||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI && this._existingDatabases.includes(c.value!)) {
|
||||
if (this.migrationStateModel.isSqlMiTarget && this._existingDatabases.includes(c.value!)) {
|
||||
c.validationErrorMessage = constants.DATABASE_ALREADY_EXISTS_MI(c.value!, this.migrationStateModel._targetServerInstance.name);
|
||||
return false;
|
||||
}
|
||||
@@ -868,7 +946,10 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
blobTargetDatabaseInput.onTextChanged(
|
||||
(value) => { this.migrationStateModel._targetDatabaseNames[index] = value.trim(); }));
|
||||
(value) => {
|
||||
this.migrationStateModel._targetDatabaseNames[index] = value.trim();
|
||||
this._resetValidationUI();
|
||||
}));
|
||||
|
||||
targetDatabaseInput.value = this.migrationStateModel._targetDatabaseNames[index];
|
||||
this._blobContainerTargetDatabaseNames.push(blobTargetDatabaseInput);
|
||||
@@ -987,13 +1068,17 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
{ value: this._blobContainerStorageAccountDropdowns[index] },
|
||||
{ value: this._blobContainerDropdowns[index] },
|
||||
{ value: this._blobContainerLastBackupFileDropdowns[index] }]);
|
||||
await this._blobContainerTargetDatabaseNamesTable.setDataValues([]);
|
||||
await this._blobContainerTargetDatabaseNamesTable.setDataValues(blobContainerTargetData);
|
||||
|
||||
await this.getSubscriptionValues();
|
||||
// clear change tracking flags
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = false;
|
||||
this.migrationStateModel._didUpdateDatabasesForMigration = false;
|
||||
this.migrationStateModel._didDatabaseMappingChange = false;
|
||||
|
||||
this.migrationStateModel._validateIrSqlDb = [];
|
||||
this.migrationStateModel._validateIrSqlMi = [];
|
||||
this.migrationStateModel._validateIrSqlVm = [];
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
let errorText = error?.message;
|
||||
@@ -1006,101 +1091,76 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
}
|
||||
|
||||
await this.validateFields();
|
||||
this.updateValidationResultUI(true);
|
||||
}
|
||||
}
|
||||
|
||||
private async _validateIr(): Promise<void> {
|
||||
this.wizard.message = { text: '' };
|
||||
const dialog = new ValidateIrDialog(
|
||||
this.migrationStateModel,
|
||||
() => this.updateValidationResultUI());
|
||||
|
||||
let results: ValidationResult[] = [];
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLDB:
|
||||
results = this.migrationStateModel._validateIrSqlDb;
|
||||
break;
|
||||
case MigrationTargetType.SQLMI:
|
||||
results = this.migrationStateModel._validateIrSqlMi;
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
results = this.migrationStateModel._validateIrSqlVm;
|
||||
break;
|
||||
}
|
||||
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
await dialog.openDialog(constants.VALIDATION_DIALOG_TITLE, results);
|
||||
}
|
||||
|
||||
public updateValidationResultUI(initializing?: boolean): void {
|
||||
if (this.migrationStateModel.isIrMigration) {
|
||||
const succeeded = this.migrationStateModel.isIrTargetValidated;
|
||||
if (succeeded) {
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Information,
|
||||
text: constants.VALIDATION_MESSAGE_SUCCESS,
|
||||
};
|
||||
} else {
|
||||
const results = this.migrationStateModel.validationTargetResults;
|
||||
const hasResults = results.length > 0;
|
||||
if (initializing && !hasResults) {
|
||||
return;
|
||||
}
|
||||
|
||||
const canceled = results.some(result => result.state === ValidateIrState.Canceled);
|
||||
const errors: string[] = results.flatMap(result => result.errors) ?? [];
|
||||
const errorsMessage: string = errors.join(EOL);
|
||||
const hasErrors = errors.length > 0;
|
||||
const msg = hasResults
|
||||
? hasErrors
|
||||
? canceled
|
||||
? constants.VALIDATION_MESSAGE_CANCELED_ERRORS(errorsMessage)
|
||||
: constants.VALIDATE_IR_VALIDATION_COMPLETED_ERRORS(errorsMessage)
|
||||
: constants.VALIDATION_MESSAGE_CANCELED
|
||||
: constants.VALIDATION_MESSAGE_NOT_RUN;
|
||||
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: msg,
|
||||
};
|
||||
}
|
||||
|
||||
this.wizard.message = { text: '' };
|
||||
const errors: string[] = [];
|
||||
switch (this.migrationStateModel._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
if ((<azdata.CategoryValue>this._networkShareStorageAccountResourceGroupDropdown.value)?.displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._networkShareContainerStorageAccountDropdown.value)?.displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
this._blobContainerResourceGroupDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.RESOURCE_GROUP_NOT_FOUND])) {
|
||||
errors.push(constants.INVALID_BLOB_RESOURCE_GROUP_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
this._blobContainerStorageAccountDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_STORAGE_ACCOUNT_FOUND, constants.SELECT_RESOURCE_GROUP_PROMPT])) {
|
||||
errors.push(constants.INVALID_BLOB_STORAGE_ACCOUNT_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
this._blobContainerDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_BLOBCONTAINERS_FOUND, constants.SELECT_STORAGE_ACCOUNT])) {
|
||||
errors.push(constants.INVALID_BLOB_CONTAINER_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
|
||||
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
||||
this._blobContainerLastBackupFileDropdowns.forEach((v, index) => {
|
||||
if (this.shouldDisplayBlobDropdownError(v, [constants.NO_BLOBFILES_FOUND, constants.SELECT_BLOB_CONTAINER])) {
|
||||
errors.push(constants.INVALID_BLOB_LAST_BACKUP_FILE_ERROR(this.migrationStateModel._databasesForMigration[index]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
const duplicates: Map<string, number[]> = new Map();
|
||||
for (let i = 0; i < this.migrationStateModel._targetDatabaseNames.length; i++) {
|
||||
const blobContainerId = this.migrationStateModel._databaseBackup.blobs[i].blobContainer?.id;
|
||||
if (duplicates.has(blobContainerId)) {
|
||||
duplicates.get(blobContainerId)?.push(i);
|
||||
} else {
|
||||
duplicates.set(blobContainerId, [i]);
|
||||
}
|
||||
}
|
||||
duplicates.forEach((d) => {
|
||||
if (d.length > 1) {
|
||||
const dupString = `${d.map(index => this.migrationStateModel._databasesForMigration[index]).join(', ')}`;
|
||||
errors.push(constants.PROVIDE_UNIQUE_CONTAINERS + dupString);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
default:
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
if (isSqlDbTarget) {
|
||||
if (!this._validateTableSelection()) {
|
||||
errors.push(constants.DATABASE_TABLE_VALIDATE_SELECTION_MESSAGE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.migrationStateModel._targetType === MigrationTargetType.SQLMI) {
|
||||
this.migrationStateModel._targetDatabaseNames.forEach(t => {
|
||||
// Making sure if database with same name is not present on the target Azure SQL
|
||||
if (this._existingDatabases.includes(t)) {
|
||||
errors.push(constants.DATABASE_ALREADY_EXISTS_MI(t, this.migrationStateModel._targetServerInstance.name));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.wizard.message = {
|
||||
text: errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
if (errors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
try {
|
||||
if (pageChangeInfo.newPage > pageChangeInfo.lastPage) {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
this.wizard.customButtons[VALIDATE_IR_CUSTOM_BUTTON_INDEX].hidden = true;
|
||||
|
||||
if (pageChangeInfo.newPage > pageChangeInfo.lastPage) {
|
||||
if (!this.migrationStateModel.isSqlDbTarget) {
|
||||
switch (this.migrationStateModel._databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
for (let i = 0; i < this.migrationStateModel._databaseBackup.blobs.length; i++) {
|
||||
@@ -1124,26 +1184,12 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => true);
|
||||
}
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
|
||||
private async switchNetworkContainerFields(containerType: NetworkContainerType): Promise<void> {
|
||||
this.wizard.message = {
|
||||
text: '',
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
|
||||
this.wizard.nextButton.enabled = true;
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = containerType;
|
||||
await this._updatePageControlsVisibility(containerType);
|
||||
await this.validateFields();
|
||||
}
|
||||
|
||||
private _validateTableSelection(): boolean {
|
||||
for (const targetDatabaseInfo of this.migrationStateModel._sourceTargetMapping) {
|
||||
const databaseInfo = targetDatabaseInfo[1];
|
||||
@@ -1159,27 +1205,36 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
return false;
|
||||
}
|
||||
|
||||
private async validateFields(): Promise<void> {
|
||||
await this._sqlSourceUsernameInput.validate();
|
||||
await this._sqlSourcePassword.validate();
|
||||
await this._windowsUserAccountText.validate();
|
||||
await this._passwordText.validate();
|
||||
await this._networkShareContainerSubscription.validate();
|
||||
await this._networkShareStorageAccountResourceGroupDropdown.validate();
|
||||
await this._networkShareContainerStorageAccountDropdown.validate();
|
||||
await this._blobContainerSubscription.validate();
|
||||
private async validateFields(component?: azdata.Component): Promise<void> {
|
||||
await this._sqlSourceUsernameInput?.validate();
|
||||
await this._sqlSourcePassword?.validate();
|
||||
await this._windowsUserAccountText?.validate();
|
||||
await this._passwordText?.validate();
|
||||
await this._networkShareContainerSubscription?.validate();
|
||||
await this._networkShareStorageAccountResourceGroupDropdown?.validate();
|
||||
await this._networkShareContainerStorageAccountDropdown?.validate();
|
||||
await this._blobContainerSubscription?.validate();
|
||||
for (let i = 0; i < this._networkShareTargetDatabaseNames.length; i++) {
|
||||
await this._networkShareTargetDatabaseNames[i].validate();
|
||||
await this._networkShareLocations[i].validate();
|
||||
await this._blobContainerTargetDatabaseNames[i].validate();
|
||||
await this._blobContainerResourceGroupDropdowns[i].validate();
|
||||
await this._blobContainerStorageAccountDropdowns[i].validate();
|
||||
await this._blobContainerDropdowns[i].validate();
|
||||
await this._networkShareTargetDatabaseNames[i]?.validate();
|
||||
await this._networkShareLocations[i]?.validate();
|
||||
await this._blobContainerTargetDatabaseNames[i]?.validate();
|
||||
await this._blobContainerResourceGroupDropdowns[i]?.validate();
|
||||
await this._blobContainerStorageAccountDropdowns[i]?.validate();
|
||||
await this._blobContainerDropdowns[i]?.validate();
|
||||
|
||||
if (this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
|
||||
await this._blobContainerLastBackupFileDropdowns[i]?.validate();
|
||||
}
|
||||
}
|
||||
if (this.migrationStateModel.isIrMigration) {
|
||||
if (this.migrationStateModel.isSqlDbTarget) {
|
||||
await this._databaseTable?.validate();
|
||||
}
|
||||
}
|
||||
if (this.migrationStateModel.isBackupContainerNetworkShare) {
|
||||
await this._networkShareTargetDatabaseNamesTable.validate();
|
||||
}
|
||||
await component?.validate();
|
||||
}
|
||||
|
||||
private async getSubscriptionValues(): Promise<void> {
|
||||
@@ -1265,66 +1320,75 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
private loadBlobStorageDropdown(index: number): void {
|
||||
try {
|
||||
this._blobContainerStorageAccountDropdowns[index].loading = true;
|
||||
this._blobContainerStorageAccountDropdowns[index].values = utils.getAzureResourceDropdownValues(
|
||||
this.migrationStateModel._storageAccounts,
|
||||
this.migrationStateModel._location,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.resourceGroup?.name,
|
||||
constants.NO_STORAGE_ACCOUNT_FOUND);
|
||||
const dropDown = this._blobContainerStorageAccountDropdowns[index];
|
||||
if (dropDown) {
|
||||
try {
|
||||
dropDown.loading = true;
|
||||
dropDown.values = utils.getAzureResourceDropdownValues(
|
||||
this.migrationStateModel._storageAccounts,
|
||||
this.migrationStateModel._location,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.resourceGroup?.name,
|
||||
constants.NO_STORAGE_ACCOUNT_FOUND);
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._blobContainerStorageAccountDropdowns[index],
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.storageAccount?.id,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobStorageDropdown', error);
|
||||
} finally {
|
||||
this._blobContainerStorageAccountDropdowns[index].loading = false;
|
||||
utils.selectDefaultDropdownValue(
|
||||
dropDown,
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.storageAccount?.id,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobStorageDropdown', error);
|
||||
} finally {
|
||||
dropDown.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async loadBlobContainerDropdown(index: number): Promise<void> {
|
||||
try {
|
||||
this._blobContainerDropdowns[index].loading = true;
|
||||
this.migrationStateModel._blobContainers = await utils.getBlobContainer(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount);
|
||||
const dropDown = this._blobContainerDropdowns[index];
|
||||
if (dropDown) {
|
||||
try {
|
||||
dropDown.loading = true;
|
||||
this.migrationStateModel._blobContainers = await utils.getBlobContainer(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount);
|
||||
|
||||
this._blobContainerDropdowns[index].values = utils.getResourceDropdownValues(
|
||||
this.migrationStateModel._blobContainers,
|
||||
constants.NO_BLOBCONTAINERS_FOUND);
|
||||
dropDown.values = utils.getResourceDropdownValues(
|
||||
this.migrationStateModel._blobContainers,
|
||||
constants.NO_BLOBCONTAINERS_FOUND);
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._blobContainerDropdowns[index],
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.blobContainer?.id,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobContainers', error);
|
||||
} finally {
|
||||
this._blobContainerDropdowns[index].loading = false;
|
||||
utils.selectDefaultDropdownValue(
|
||||
dropDown,
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.blobContainer?.id,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobContainers', error);
|
||||
} finally {
|
||||
dropDown.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async loadBlobLastBackupFileDropdown(index: number): Promise<void> {
|
||||
try {
|
||||
this._blobContainerLastBackupFileDropdowns[index].loading = true;
|
||||
this.migrationStateModel._lastFileNames = await utils.getBlobLastBackupFileNames(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.blobContainer);
|
||||
this._blobContainerLastBackupFileDropdowns[index].values = await utils.getBlobLastBackupFileNamesValues(
|
||||
this.migrationStateModel._lastFileNames);
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._blobContainerLastBackupFileDropdowns[index],
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.lastBackupFile,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobLastBackupFiles', error);
|
||||
} finally {
|
||||
this._blobContainerLastBackupFileDropdowns[index].loading = false;
|
||||
const dropDown = this._blobContainerLastBackupFileDropdowns[index];
|
||||
if (dropDown) {
|
||||
try {
|
||||
dropDown.loading = true;
|
||||
this.migrationStateModel._lastFileNames = await utils.getBlobLastBackupFileNames(
|
||||
this.migrationStateModel._azureAccount,
|
||||
this.migrationStateModel._databaseBackup.subscription,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.storageAccount,
|
||||
this.migrationStateModel._databaseBackup.blobs[index]?.blobContainer);
|
||||
dropDown.values = await utils.getBlobLastBackupFileNamesValues(
|
||||
this.migrationStateModel._lastFileNames);
|
||||
utils.selectDefaultDropdownValue(
|
||||
dropDown,
|
||||
this.migrationStateModel._databaseBackup?.blobs[index]?.lastBackupFile,
|
||||
false);
|
||||
} catch (error) {
|
||||
logError(TelemetryViews.DatabaseBackupPage, 'ErrorLoadingBlobLastBackupFiles', error);
|
||||
} finally {
|
||||
dropDown.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1409,6 +1473,12 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
},
|
||||
],
|
||||
})
|
||||
.withValidation(table => {
|
||||
if (this.migrationStateModel.isSqlDbTarget) {
|
||||
return this._validateTableSelection();
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.component();
|
||||
|
||||
this._disposables.push(
|
||||
@@ -1479,7 +1549,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
});
|
||||
|
||||
await this._databaseTable.updateProperty('data', data);
|
||||
|
||||
this._refreshLoading.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user