mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 01:25:38 -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +64,9 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
|
||||
const assessedDatabases = this.migrationStateModel._assessedDatabaseList ?? [];
|
||||
const selectedDatabases = this.migrationStateModel._databasesForAssessment;
|
||||
// run assessment if
|
||||
@@ -75,15 +78,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
|| assessedDatabases.length === 0
|
||||
|| assessedDatabases.length !== selectedDatabases.length
|
||||
|| assessedDatabases.some(db => selectedDatabases.indexOf(db) < 0);
|
||||
|
||||
this.wizard.message = {
|
||||
text: '',
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, MigrationTargetType, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import { MigrationMode, MigrationStateModel, NetworkContainerType, StateChangeEvent } from '../models/stateMachine';
|
||||
import { CreateSqlMigrationServiceDialog } from '../dialog/createSqlMigrationService/createSqlMigrationServiceDialog';
|
||||
import * as constants from '../constants/strings';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
@@ -32,10 +32,22 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
private _copy2!: azdata.ButtonComponent;
|
||||
private _refresh1!: azdata.ButtonComponent;
|
||||
private _refresh2!: azdata.ButtonComponent;
|
||||
private _onlineButton!: azdata.RadioButtonComponent;
|
||||
private _offlineButton!: azdata.RadioButtonComponent;
|
||||
private _modeContainer!: azdata.FlexContainer;
|
||||
private _radioButtonContainer!: azdata.FlexContainer;
|
||||
private _networkShareButton!: azdata.RadioButtonComponent;
|
||||
private _blobContainerButton!: azdata.RadioButtonComponent;
|
||||
private _originalMigrationMode!: MigrationMode;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel);
|
||||
this.migrationStateModel._databaseBackup.migrationMode =
|
||||
this.migrationStateModel._databaseBackup.migrationMode ||
|
||||
this.migrationStateModel.isSqlDbTarget
|
||||
? MigrationMode.OFFLINE
|
||||
: MigrationMode.ONLINE;
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
@@ -49,8 +61,13 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
.withItems([this._statusLoadingComponent])
|
||||
.component();
|
||||
|
||||
this._radioButtonContainer = this.createBackupLocationComponent();
|
||||
this._modeContainer = this.migrationModeContainer();
|
||||
|
||||
const form = view.modelBuilder.formContainer()
|
||||
.withFormItems([
|
||||
{ component: this._modeContainer },
|
||||
{ component: this._radioButtonContainer },
|
||||
{ component: this.migrationServiceDropdownContainer() },
|
||||
{ component: this._dmsInfoContainer }])
|
||||
.withProps({ CSSStyles: { 'padding-top': '0' } })
|
||||
@@ -64,26 +81,132 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
await view.initializeModel(form);
|
||||
}
|
||||
|
||||
private migrationModeContainer(): azdata.FlexContainer {
|
||||
const buttonGroup = 'migrationMode';
|
||||
this._onlineButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL,
|
||||
name: buttonGroup,
|
||||
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.ONLINE,
|
||||
CSSStyles: { ...styles.LABEL_CSS, },
|
||||
}).component();
|
||||
const onlineDescription = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._onlineButton.onDidChangeCheckedState(checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.ONLINE;
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
}));
|
||||
|
||||
this._offlineButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL,
|
||||
name: buttonGroup,
|
||||
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE,
|
||||
CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '12px' },
|
||||
}).component();
|
||||
const offlineDescription = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._offlineButton.onDidChangeCheckedState(checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
}));
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
this._onlineButton,
|
||||
onlineDescription,
|
||||
this._offlineButton,
|
||||
offlineDescription]
|
||||
).withLayout({ flexFlow: 'column' })
|
||||
.component();
|
||||
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
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.isBackupContainerNetworkShare,
|
||||
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
||||
}).component();
|
||||
|
||||
this._disposables.push(
|
||||
this._networkShareButton.onDidChangeCheckedState(async checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.NETWORK_SHARE;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, true);
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
}));
|
||||
|
||||
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) {
|
||||
this.migrationStateModel._databaseBackup.networkContainerType = NetworkContainerType.BLOB_CONTAINER;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, false);
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
}));
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
selectLocationText,
|
||||
this._blobContainerButton,
|
||||
this._networkShareButton,
|
||||
])
|
||||
.withLayout({ flexFlow: 'column' })
|
||||
.component();
|
||||
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
const isNetworkShare = this.migrationStateModel.isBackupContainerNetworkShare;
|
||||
|
||||
this._subscription.value = this.migrationStateModel._targetSubscription.name;
|
||||
this._location.value = await getLocationDisplayName(
|
||||
this.migrationStateModel._targetServerInstance.location);
|
||||
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
this.migrationStateModel._targetType === MigrationTargetType.SQLDB ||
|
||||
this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE);
|
||||
|
||||
await this.loadResourceGroupDropdown();
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
this.wizard.message = { text: '' };
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
}
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
if (!isSqlDbTarget && !this._networkShareButton.checked && !this._blobContainerButton.checked) {
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: constants.SERVICE_SELECTION_LOCATION_MESSAGE,
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
const state = this.migrationStateModel._sqlMigrationService?.properties?.integrationRuntimeState;
|
||||
if (!this.migrationStateModel._sqlMigrationService) {
|
||||
@@ -93,10 +216,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if ((this.migrationStateModel._targetType === MigrationTargetType.SQLDB ||
|
||||
this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE)
|
||||
&& state !== 'Online') {
|
||||
|
||||
if ((isSqlDbTarget || isNetworkShare) && state !== 'Online') {
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: constants.SERVICE_OFFLINE_ERROR
|
||||
@@ -105,10 +225,43 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
await utils.updateControlDisplay(this._modeContainer, !isSqlDbTarget);
|
||||
this._onlineButton.enabled = !isSqlDbTarget;
|
||||
|
||||
if (isSqlDbTarget) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
||||
this._offlineButton.checked = true;
|
||||
}
|
||||
this._originalMigrationMode = this.migrationStateModel._databaseBackup.migrationMode;
|
||||
|
||||
this._networkShareButton.checked = this.migrationStateModel.isBackupContainerNetworkShare;
|
||||
this._blobContainerButton.checked = this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER;
|
||||
await utils.updateControlDisplay(
|
||||
this._radioButtonContainer,
|
||||
!isSqlDbTarget);
|
||||
|
||||
this._subscription.value = this.migrationStateModel._targetSubscription.name;
|
||||
this._location.value = await getLocationDisplayName(
|
||||
this.migrationStateModel._targetServerInstance.location);
|
||||
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
isSqlDbTarget || isNetworkShare);
|
||||
|
||||
await this.loadResourceGroupDropdown();
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => true);
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
if (this._originalMigrationMode !== this.migrationStateModel._databaseBackup.migrationMode) {
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
@@ -195,18 +348,20 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
async (value) => {
|
||||
if (value && value !== 'undefined' && value !== constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR) {
|
||||
this.wizard.message = { text: '' };
|
||||
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
this.migrationStateModel._targetType === MigrationTargetType.SQLDB ||
|
||||
this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE);
|
||||
|
||||
const resourceGroupName = this.migrationStateModel._sqlMigrationServiceResourceGroup.name.toLowerCase();
|
||||
const selectedDms = this.migrationStateModel._sqlMigrationServices.find(
|
||||
dms => dms.name === value && dms.properties.resourceGroup.toLowerCase() === this.migrationStateModel._sqlMigrationServiceResourceGroup.name.toLowerCase());
|
||||
dms => dms.name === value
|
||||
&& dms.properties.resourceGroup.toLowerCase() === resourceGroupName);
|
||||
|
||||
if (selectedDms) {
|
||||
this.migrationStateModel._sqlMigrationService = selectedDms;
|
||||
await this.loadStatus();
|
||||
}
|
||||
|
||||
await utils.updateControlDisplay(
|
||||
this._dmsInfoContainer,
|
||||
this.migrationStateModel.isSqlDbTarget ||
|
||||
this.migrationStateModel.isBackupContainerNetworkShare);
|
||||
} else {
|
||||
this.migrationStateModel._sqlMigrationService = undefined;
|
||||
await utils.updateControlDisplay(this._dmsInfoContainer, false);
|
||||
@@ -276,7 +431,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
this._disposables.push(
|
||||
this._refreshButton.onDidClick(
|
||||
async (e) => this.loadStatus()));
|
||||
async (e) => await this.loadStatus()));
|
||||
|
||||
const connectionLabelContainer = this._view.modelBuilder.flexContainer()
|
||||
.component();
|
||||
@@ -398,7 +553,7 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this._dmsDropdown.values = utils.getAzureResourceDropdownValues(
|
||||
this.migrationStateModel._sqlMigrationServices,
|
||||
this.migrationStateModel._location,
|
||||
this.migrationStateModel._sqlMigrationServiceResourceGroup.name,
|
||||
this.migrationStateModel._sqlMigrationServiceResourceGroup?.name,
|
||||
constants.SQL_MIGRATION_SERVICE_NOT_FOUND_ERROR);
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* 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 { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationMode, MigrationStateModel, MigrationTargetType, StateChangeEvent } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import * as styles from '../constants/styles';
|
||||
|
||||
export class MigrationModePage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
private _onlineButton!: azdata.RadioButtonComponent;
|
||||
private _offlineButton!: azdata.RadioButtonComponent;
|
||||
private _originalMigrationMode!: MigrationMode;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(
|
||||
wizard,
|
||||
azdata.window.createWizardPage(constants.DATABASE_BACKUP_MIGRATION_MODE_LABEL, 'MigrationModePage'),
|
||||
migrationStateModel);
|
||||
this.migrationStateModel._databaseBackup.migrationMode = this.migrationStateModel._databaseBackup.migrationMode || MigrationMode.ONLINE;
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
this._view = view;
|
||||
|
||||
const pageDescription = {
|
||||
title: '',
|
||||
component: view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.BODY_CSS, 'margin': '0' }
|
||||
}).component()
|
||||
};
|
||||
|
||||
const form = view.modelBuilder.formContainer()
|
||||
.withFormItems([
|
||||
pageDescription,
|
||||
this.migrationModeContainer()])
|
||||
.withProps({ CSSStyles: { 'padding-top': '0' } })
|
||||
.component();
|
||||
|
||||
this._disposables.push(
|
||||
this._view.onClosed(
|
||||
e => this._disposables.forEach(
|
||||
d => { try { d.dispose(); } catch { } })));
|
||||
|
||||
await view.initializeModel(form);
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
this._onlineButton.enabled = !isSqlDbTarget;
|
||||
if (isSqlDbTarget) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
||||
this._offlineButton.checked = true;
|
||||
await this._offlineButton.focus();
|
||||
}
|
||||
this._originalMigrationMode = this.migrationStateModel._databaseBackup.migrationMode;
|
||||
this.wizard.registerNavigationValidator((e) => true);
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
if (this._originalMigrationMode !== this.migrationStateModel._databaseBackup.migrationMode) {
|
||||
this.migrationStateModel.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
this.wizard.registerNavigationValidator((e) => true);
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
|
||||
private migrationModeContainer(): azdata.FormComponent {
|
||||
const buttonGroup = 'migrationMode';
|
||||
this._onlineButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_LABEL,
|
||||
name: buttonGroup,
|
||||
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.ONLINE,
|
||||
CSSStyles: { ...styles.LABEL_CSS, },
|
||||
}).component();
|
||||
const onlineDescription = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_ONLINE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._onlineButton.onDidChangeCheckedState(checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.ONLINE;
|
||||
}
|
||||
}));
|
||||
|
||||
this._offlineButton = this._view.modelBuilder.radioButton()
|
||||
.withProps({
|
||||
label: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_LABEL,
|
||||
name: buttonGroup,
|
||||
checked: this.migrationStateModel._databaseBackup.migrationMode === MigrationMode.OFFLINE,
|
||||
CSSStyles: { ...styles.LABEL_CSS, 'margin-top': '12px' },
|
||||
}).component();
|
||||
const offlineDescription = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.DATABASE_BACKUP_MIGRATION_MODE_OFFLINE_DESCRIPTION,
|
||||
CSSStyles: { ...styles.NOTE_CSS, 'margin-left': '20px' }
|
||||
}).component();
|
||||
this._disposables.push(
|
||||
this._offlineButton.onDidChangeCheckedState(checked => {
|
||||
if (checked) {
|
||||
this.migrationStateModel._databaseBackup.migrationMode = MigrationMode.OFFLINE;
|
||||
}
|
||||
}));
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
this._onlineButton,
|
||||
onlineDescription,
|
||||
this._offlineButton,
|
||||
offlineDescription]
|
||||
).withLayout({ flexFlow: 'column' })
|
||||
.component();
|
||||
|
||||
return { component: flexContainer };
|
||||
}
|
||||
}
|
||||
@@ -589,8 +589,8 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => true);
|
||||
this.eventListener?.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,14 +39,11 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
return true;
|
||||
});
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
}
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
return true;
|
||||
});
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
@@ -59,9 +56,9 @@ export class SqlSourceConfigurationPage extends MigrationWizardPage {
|
||||
const query = 'select SUSER_NAME()';
|
||||
const results = await queryProvider.runQueryAndReturn(await (azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId)), query);
|
||||
const username = results.rows[0][0].displayValue;
|
||||
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === 'SqlLogin'
|
||||
this.migrationStateModel._authenticationType = connectionProfile.authenticationType === azdata.connection.AuthenticationType.SqlLogin
|
||||
? MigrationSourceAuthenticationType.Sql
|
||||
: connectionProfile.authenticationType === 'Integrated'
|
||||
: connectionProfile.authenticationType === azdata.connection.AuthenticationType.Integrated
|
||||
? MigrationSourceAuthenticationType.Integrated
|
||||
: undefined!;
|
||||
|
||||
|
||||
@@ -40,11 +40,13 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
|
||||
const targetDatabaseSummary = new TargetDatabaseSummaryDialog(this.migrationStateModel);
|
||||
const isSqlVmTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLVM;
|
||||
const isSqlMiTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLMI;
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
const isNetworkShare = this.migrationStateModel._databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE;
|
||||
const isSqlVmTarget = this.migrationStateModel.isSqlVmTarget;
|
||||
const isSqlMiTarget = this.migrationStateModel.isSqlMiTarget;
|
||||
const isSqlDbTarget = this.migrationStateModel.isSqlDbTarget;
|
||||
const isNetworkShare = this.migrationStateModel.isBackupContainerNetworkShare;
|
||||
|
||||
const targetDatabaseHyperlink = this._view.modelBuilder.hyperlink()
|
||||
.withProps({
|
||||
@@ -132,8 +134,6 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
this._flexContainer.addItems([
|
||||
|
||||
|
||||
await createHeadingTextComponent(
|
||||
this._view,
|
||||
constants.IR_PAGE_TITLE),
|
||||
@@ -166,8 +166,9 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
this._flexContainer.clearItems();
|
||||
this.wizard.registerNavigationValidator(async (pageChangeInfo) => true);
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as utils from '../api/utils';
|
||||
import { azureResource } from 'azurecore';
|
||||
import { AzureSqlDatabaseServer, SqlVMServer } from '../api/azure';
|
||||
import { collectTargetDatabaseInfo, TargetDatabaseInfo } from '../api/sqlUtils';
|
||||
import { MigrationLocalStorage, MigrationServiceContext } from '../models/migrationLocalStorage';
|
||||
|
||||
export class TargetSelectionPage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
@@ -38,6 +39,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
private _testConectionButton!: azdata.ButtonComponent;
|
||||
private _connectionResultsInfoBox!: azdata.InfoBoxComponent;
|
||||
private _migrationTargetPlatform!: MigrationTargetType;
|
||||
private _serviceContext!: MigrationServiceContext;
|
||||
|
||||
constructor(
|
||||
wizard: azdata.window.Wizard,
|
||||
@@ -51,6 +53,8 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
this._view = view;
|
||||
|
||||
this._serviceContext = await MigrationLocalStorage.getMigrationServiceContext();
|
||||
|
||||
this._pageDescription = this._view.modelBuilder.text()
|
||||
.withProps({
|
||||
value: constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(),
|
||||
@@ -76,51 +80,6 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_MI_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_VM_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||
break;
|
||||
case MigrationTargetType.SQLDB:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_SQLDB_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE;
|
||||
this._updateConnectionButtonState();
|
||||
if (this.migrationStateModel._didUpdateDatabasesForMigration) {
|
||||
await this._resetTargetMapping();
|
||||
this.migrationStateModel._didUpdateDatabasesForMigration = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
await this._targetUserNameInputBox.updateProperty('required', isSqlDbTarget);
|
||||
await this._targetPasswordInputBox.updateProperty('required', isSqlDbTarget);
|
||||
await utils.updateControlDisplay(this._resourceAuthenticationContainer, isSqlDbTarget);
|
||||
await this.populateAzureAccountsDropdown();
|
||||
|
||||
if (this._migrationTargetPlatform !== this.migrationStateModel._targetType) {
|
||||
// if the user had previously selected values on this page, then went back to change the migration target platform
|
||||
// and came back, forcibly reload the location/resource group/resource values since they will now be different
|
||||
this._migrationTargetPlatform = this.migrationStateModel._targetType;
|
||||
|
||||
this._targetPasswordInputBox.value = '';
|
||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||
this.migrationStateModel._targetServerInstance = undefined!;
|
||||
this.migrationStateModel._resourceGroup = undefined!;
|
||||
this.migrationStateModel._location = undefined!;
|
||||
await this.populateLocationDropdown();
|
||||
}
|
||||
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
this.wizard.message = { text: '' };
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
@@ -152,8 +111,8 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
if (!targetMi || resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
|
||||
errors.push(constants.INVALID_MANAGED_INSTANCE_ERROR);
|
||||
}
|
||||
if (targetMi?.properties?.state !== 'Ready') {
|
||||
errors.push(constants.MI_NOT_READY_ERROR(targetMi.name, targetMi.properties.state));
|
||||
if (targetMi && targetMi.properties?.state !== 'Ready') {
|
||||
errors.push(constants.MI_NOT_READY_ERROR(targetMi.name, targetMi.properties?.state));
|
||||
}
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
@@ -168,7 +127,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
errors.push(constants.INVALID_SQL_DATABASE_ERROR);
|
||||
}
|
||||
// TODO: verify what state check is needed/possible?
|
||||
if (targetSqlDB?.properties?.state !== 'Ready') {
|
||||
if (targetSqlDB && targetSqlDB.properties?.state !== 'Ready') {
|
||||
errors.push(constants.SQLDB_NOT_READY_ERROR(targetSqlDB.name, targetSqlDB.properties.state));
|
||||
}
|
||||
|
||||
@@ -201,10 +160,70 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return;
|
||||
}
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_MI_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_VM_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||
break;
|
||||
case MigrationTargetType.SQLDB:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_SQLDB_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE;
|
||||
this._azureResourceDropdown.ariaLabel = constants.AZURE_SQL_DATABASE;
|
||||
this._updateConnectionButtonState();
|
||||
if (this.migrationStateModel._didUpdateDatabasesForMigration) {
|
||||
await this._resetTargetMapping();
|
||||
this.migrationStateModel._didUpdateDatabasesForMigration = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const isSqlDbTarget = this.migrationStateModel._targetType === MigrationTargetType.SQLDB;
|
||||
await this._targetUserNameInputBox.updateProperties({ required: isSqlDbTarget });
|
||||
await this._targetPasswordInputBox.updateProperties({ required: isSqlDbTarget });
|
||||
await utils.updateControlDisplay(this._resourceAuthenticationContainer, isSqlDbTarget);
|
||||
|
||||
if (this._migrationTargetPlatform !== this.migrationStateModel._targetType) {
|
||||
// if the user had previously selected values on this page, then went back to change the migration target platform
|
||||
// and came back, forcibly reload the location/resource group/resource values since they will now be different
|
||||
this._migrationTargetPlatform = this.migrationStateModel._targetType;
|
||||
|
||||
this._targetPasswordInputBox.value = '';
|
||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||
this.migrationStateModel._azureAccount = undefined!;
|
||||
this.migrationStateModel._azureTenant = undefined!;
|
||||
this.migrationStateModel._targetSubscription = undefined!;
|
||||
this.migrationStateModel._location = undefined!;
|
||||
this.migrationStateModel._resourceGroup = undefined!;
|
||||
this.migrationStateModel._targetServerInstance = undefined!;
|
||||
|
||||
const clearDropDown = async (dropDown: azdata.DropDownComponent): Promise<void> => {
|
||||
dropDown.values = [];
|
||||
dropDown.value = undefined;
|
||||
};
|
||||
await clearDropDown(this._azureAccountsDropdown);
|
||||
await clearDropDown(this._accountTenantDropdown);
|
||||
await clearDropDown(this._azureSubscriptionDropdown);
|
||||
await clearDropDown(this._azureLocationDropdown);
|
||||
await clearDropDown(this._azureResourceGroupDropdown);
|
||||
await clearDropDown(this._azureResourceDropdown);
|
||||
}
|
||||
|
||||
await this.populateAzureAccountsDropdown();
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(async (pageChangeInfo) => true);
|
||||
this.wizard.registerNavigationValidator(pageChangeInfo => true);
|
||||
this.wizard.message = { text: '' };
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
@@ -231,7 +250,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
this._azureAccountsDropdown.onValueChanged(async (value) => {
|
||||
if (value && value !== 'undefined') {
|
||||
const selectedAccount = this.migrationStateModel._azureAccounts.find(account => account.displayInfo.displayName === value);
|
||||
const selectedAccount = this.migrationStateModel._azureAccounts?.find(account => account.displayInfo.displayName === value);
|
||||
this.migrationStateModel._azureAccount = (selectedAccount)
|
||||
? utils.deepClone(selectedAccount)!
|
||||
: undefined!;
|
||||
@@ -287,7 +306,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
* 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
|
||||
*/
|
||||
const selectedTenant = this.migrationStateModel._accountTenants.find(tenant => tenant.displayName === value);
|
||||
const selectedTenant = this.migrationStateModel._accountTenants?.find(tenant => tenant.displayName === value);
|
||||
if (selectedTenant) {
|
||||
this.migrationStateModel._azureTenant = utils.deepClone(selectedTenant)!;
|
||||
this.migrationStateModel._azureAccount.properties.tenants = [this.migrationStateModel._azureTenant];
|
||||
@@ -328,7 +347,8 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
this._azureSubscriptionDropdown.onValueChanged(async (value) => {
|
||||
if (value && value !== 'undefined' && value !== constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
const selectedSubscription = this.migrationStateModel._subscriptions.find(subscription => `${subscription.name} - ${subscription.id}` === value);
|
||||
const selectedSubscription = this.migrationStateModel._subscriptions?.find(
|
||||
subscription => `${subscription.name} - ${subscription.id}` === value);
|
||||
this.migrationStateModel._targetSubscription = (selectedSubscription)
|
||||
? utils.deepClone(selectedSubscription)!
|
||||
: undefined!;
|
||||
@@ -358,7 +378,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
this._azureLocationDropdown.onValueChanged(async (value) => {
|
||||
if (value && value !== 'undefined' && value !== constants.NO_LOCATION_FOUND) {
|
||||
const selectedLocation = this.migrationStateModel._locations.find(location => location.displayName === value);
|
||||
const selectedLocation = this.migrationStateModel._locations?.find(location => location.displayName === value);
|
||||
this.migrationStateModel._location = (selectedLocation)
|
||||
? utils.deepClone(selectedLocation)!
|
||||
: undefined!;
|
||||
@@ -585,7 +605,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
this._disposables.push(
|
||||
this._azureResourceGroupDropdown.onValueChanged(async (value) => {
|
||||
if (value && value !== 'undefined' && value !== constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
const selectedResourceGroup = this.migrationStateModel._resourceGroups.find(rg => rg.name === value);
|
||||
const selectedResourceGroup = this.migrationStateModel._resourceGroups?.find(rg => rg.name === value);
|
||||
this.migrationStateModel._resourceGroup = (selectedResourceGroup)
|
||||
? utils.deepClone(selectedResourceGroup)!
|
||||
: undefined!;
|
||||
@@ -622,15 +642,15 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLVM:
|
||||
const selectedVm = this.migrationStateModel._targetSqlVirtualMachines.find(vm => vm.name === value);
|
||||
const selectedVm = this.migrationStateModel._targetSqlVirtualMachines?.find(vm => vm.name === value);
|
||||
if (selectedVm) {
|
||||
this.migrationStateModel._targetServerInstance = utils.deepClone(selectedVm)! as SqlVMServer;
|
||||
}
|
||||
break;
|
||||
case MigrationTargetType.SQLMI:
|
||||
const selectedMi = this.migrationStateModel._targetManagedInstances
|
||||
.find(mi => mi.name === value
|
||||
|| constants.UNAVAILABLE_TARGET_PREFIX(mi.name) === value);
|
||||
const selectedMi = this.migrationStateModel._targetManagedInstances?.find(
|
||||
mi => mi.name === value ||
|
||||
constants.UNAVAILABLE_TARGET_PREFIX(mi.name) === value);
|
||||
|
||||
if (selectedMi) {
|
||||
this.migrationStateModel._targetServerInstance = utils.deepClone(selectedMi)! as azureResource.AzureSqlManagedInstance;
|
||||
@@ -647,7 +667,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
}
|
||||
break;
|
||||
case MigrationTargetType.SQLDB:
|
||||
const sqlDatabaseServer = this.migrationStateModel._targetSqlDatabaseServers.find(
|
||||
const sqlDatabaseServer = this.migrationStateModel._targetSqlDatabaseServers?.find(
|
||||
sqldb => sqldb.name === value || constants.UNAVAILABLE_TARGET_PREFIX(sqldb.name) === value);
|
||||
|
||||
if (sqlDatabaseServer) {
|
||||
@@ -703,8 +723,8 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
|
||||
private _updateConnectionButtonState(): void {
|
||||
const targetDatabaseServer = (this._azureResourceDropdown.value as azdata.CategoryValue)?.name ?? '';
|
||||
const userName = this._targetUserNameInputBox.value ?? '';
|
||||
const password = this._targetPasswordInputBox.value ?? '';
|
||||
const userName = this.migrationStateModel._targetUserName ?? '';
|
||||
const password = this.migrationStateModel._targetPassword ?? '';
|
||||
this._testConectionButton.enabled = targetDatabaseServer.length > 0
|
||||
&& userName.length > 0
|
||||
&& password.length > 0;
|
||||
@@ -764,12 +784,17 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
try {
|
||||
this._azureAccountsDropdown.loading = true;
|
||||
this.migrationStateModel._azureAccounts = await utils.getAzureAccounts();
|
||||
|
||||
this._azureAccountsDropdown.values = await utils.getAzureAccountsDropdownValues(this.migrationStateModel._azureAccounts);
|
||||
} finally {
|
||||
this._azureAccountsDropdown.loading = false;
|
||||
const accountId =
|
||||
this.migrationStateModel._azureAccount?.displayInfo?.userId ??
|
||||
this._serviceContext?.azureAccount?.displayInfo?.userId;
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._azureAccountsDropdown,
|
||||
this.migrationStateModel._azureAccount?.displayInfo?.userId,
|
||||
accountId,
|
||||
false);
|
||||
}
|
||||
}
|
||||
@@ -777,17 +802,24 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
private async populateTenantsDropdown(): Promise<void> {
|
||||
try {
|
||||
this._accountTenantDropdown.loading = true;
|
||||
if (this.migrationStateModel._azureAccount && this.migrationStateModel._azureAccount.isStale === false && this.migrationStateModel._azureAccount.properties.tenants.length > 0) {
|
||||
if (this.migrationStateModel._azureAccount?.isStale === false &&
|
||||
this.migrationStateModel._azureAccount?.properties?.tenants?.length > 0) {
|
||||
this.migrationStateModel._accountTenants = utils.getAzureTenants(this.migrationStateModel._azureAccount);
|
||||
|
||||
this._accountTenantDropdown.values = await utils.getAzureTenantsDropdownValues(this.migrationStateModel._accountTenants);
|
||||
}
|
||||
const tenantId =
|
||||
this.migrationStateModel._azureTenant?.id ??
|
||||
this._serviceContext?.tenant?.id;
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._accountTenantDropdown,
|
||||
this.migrationStateModel._azureTenant?.id,
|
||||
tenantId,
|
||||
true);
|
||||
await this._accountTenantFlexContainer.updateCssStyles(this.migrationStateModel._azureAccount.properties.tenants.length > 1
|
||||
? { 'display': 'inline' }
|
||||
: { 'display': 'none' }
|
||||
await this._accountTenantFlexContainer.updateCssStyles(
|
||||
this.migrationStateModel._azureAccount?.properties?.tenants?.length > 1
|
||||
? { 'display': 'inline' }
|
||||
: { 'display': 'none' }
|
||||
);
|
||||
await this._azureAccountsDropdown.validate();
|
||||
} finally {
|
||||
@@ -804,9 +836,13 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureSubscriptionDropdown.loading = false;
|
||||
const subscriptionId =
|
||||
this.migrationStateModel._targetSubscription?.id ??
|
||||
this._serviceContext?.subscription?.id;
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._azureSubscriptionDropdown,
|
||||
this.migrationStateModel._targetSubscription?.id,
|
||||
subscriptionId,
|
||||
false);
|
||||
}
|
||||
}
|
||||
@@ -848,9 +884,13 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureLocationDropdown.loading = false;
|
||||
const location =
|
||||
this.migrationStateModel._location?.displayName ??
|
||||
this._serviceContext?.location?.displayName;
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._azureLocationDropdown,
|
||||
this.migrationStateModel._location?.displayName,
|
||||
location,
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -882,6 +922,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureResourceGroupDropdown.loading = false;
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._azureResourceGroupDropdown,
|
||||
this.migrationStateModel._resourceGroup?.id,
|
||||
@@ -916,9 +957,22 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
}
|
||||
} finally {
|
||||
this._azureResourceDropdown.loading = false;
|
||||
let targetName = '';
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
targetName = (this.migrationStateModel._targetServerInstance as azureResource.AzureSqlManagedInstance)?.name;
|
||||
break;
|
||||
case MigrationTargetType.SQLVM:
|
||||
targetName = (this.migrationStateModel._targetServerInstance as SqlVMServer)?.name;
|
||||
break;
|
||||
case MigrationTargetType.SQLDB:
|
||||
targetName = (this.migrationStateModel._targetServerInstance as AzureSqlDatabaseServer)?.name;
|
||||
break;
|
||||
}
|
||||
|
||||
utils.selectDefaultDropdownValue(
|
||||
this._azureResourceDropdown,
|
||||
this.migrationStateModel._targetServerInstance?.name,
|
||||
targetName,
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -944,7 +998,7 @@ export class TargetSelectionPage extends MigrationWizardPage {
|
||||
.component();
|
||||
this._disposables.push(
|
||||
targetDatabaseDropDown.onValueChanged((targetDatabaseName: string) => {
|
||||
const targetDatabaseInfo = targetDatabases.find(
|
||||
const targetDatabaseInfo = targetDatabases?.find(
|
||||
targetDb => targetDb.databaseName === targetDatabaseName);
|
||||
this.migrationStateModel._sourceTargetMapping.set(
|
||||
sourceDatabase,
|
||||
|
||||
@@ -13,7 +13,6 @@ import { DatabaseBackupPage } from './databaseBackupPage';
|
||||
import { TargetSelectionPage } from './targetSelectionPage';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
import { SummaryPage } from './summaryPage';
|
||||
import { MigrationModePage } from './migrationModePage';
|
||||
import { DatabaseSelectorPage } from './databaseSelectorPage';
|
||||
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews, logError } from '../telemtery';
|
||||
import * as styles from '../constants/styles';
|
||||
@@ -48,23 +47,41 @@ export class WizardController {
|
||||
|
||||
this._wizardObject.generateScriptButton.enabled = false;
|
||||
this._wizardObject.generateScriptButton.hidden = true;
|
||||
const saveAndCloseButton = azdata.window.createButton(loc.SAVE_AND_CLOSE);
|
||||
this._wizardObject.customButtons = [saveAndCloseButton];
|
||||
this._wizardObject.nextButton.position = 'left';
|
||||
this._wizardObject.nextButton.secondary = false;
|
||||
this._wizardObject.doneButton.label = loc.START_MIGRATION_TEXT;
|
||||
this._wizardObject.doneButton.position = 'left';
|
||||
this._wizardObject.doneButton.secondary = false;
|
||||
this._wizardObject.backButton.position = 'left';
|
||||
this._wizardObject.backButton.secondary = true;
|
||||
this._wizardObject.cancelButton.position = 'left';
|
||||
this._wizardObject.cancelButton.secondary = true;
|
||||
|
||||
const saveAndCloseButton = azdata.window.createButton(
|
||||
loc.SAVE_AND_CLOSE,
|
||||
'right');
|
||||
saveAndCloseButton.secondary = true;
|
||||
|
||||
const validateButton = azdata.window.createButton(
|
||||
loc.RUN_VALIDATION,
|
||||
'left');
|
||||
validateButton.secondary = false;
|
||||
validateButton.hidden = true;
|
||||
|
||||
this._wizardObject.customButtons = [validateButton, saveAndCloseButton];
|
||||
const databaseSelectorPage = new DatabaseSelectorPage(this._wizardObject, stateModel);
|
||||
const skuRecommendationPage = new SKURecommendationPage(this._wizardObject, stateModel);
|
||||
const targetSelectionPage = new TargetSelectionPage(this._wizardObject, stateModel);
|
||||
const migrationModePage = new MigrationModePage(this._wizardObject, stateModel);
|
||||
const databaseBackupPage = new DatabaseBackupPage(this._wizardObject, stateModel);
|
||||
const integrationRuntimePage = new IntergrationRuntimePage(this._wizardObject, stateModel);
|
||||
const databaseBackupPage = new DatabaseBackupPage(this._wizardObject, stateModel);
|
||||
const summaryPage = new SummaryPage(this._wizardObject, stateModel);
|
||||
|
||||
const pages: MigrationWizardPage[] = [
|
||||
databaseSelectorPage,
|
||||
skuRecommendationPage,
|
||||
targetSelectionPage,
|
||||
migrationModePage,
|
||||
databaseBackupPage,
|
||||
integrationRuntimePage,
|
||||
databaseBackupPage,
|
||||
summaryPage];
|
||||
|
||||
this._wizardObject.pages = pages.map(p => p.getwizardPage());
|
||||
@@ -80,15 +97,15 @@ export class WizardController {
|
||||
wizardSetupPromises.push(...pages.map(p => p.registerWizardContent()));
|
||||
wizardSetupPromises.push(this._wizardObject.open());
|
||||
if (this._model.retryMigration || this._model.resumeAssessment) {
|
||||
if (this._model.savedInfo.closedPage >= Page.MigrationMode) {
|
||||
if (this._model.savedInfo.closedPage >= Page.IntegrationRuntime) {
|
||||
this._model.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
|
||||
// if the user selected network share and selected save & close afterwards, it should always return to the database backup page so that
|
||||
// the user can input their password again
|
||||
if (this._model.savedInfo.closedPage >= Page.DatabaseBackup &&
|
||||
if (this._model.savedInfo.closedPage >= Page.IntegrationRuntime &&
|
||||
this._model.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
wizardSetupPromises.push(this._wizardObject.setCurrentPage(Page.DatabaseBackup));
|
||||
wizardSetupPromises.push(this._wizardObject.setCurrentPage(Page.IntegrationRuntime));
|
||||
} else {
|
||||
wizardSetupPromises.push(this._wizardObject.setCurrentPage(this._model.savedInfo.closedPage));
|
||||
}
|
||||
@@ -107,15 +124,7 @@ export class WizardController {
|
||||
await pages[newPage]?.onPageEnter(pageChangeInfo);
|
||||
}));
|
||||
|
||||
this._wizardObject.registerNavigationValidator(async validator => {
|
||||
// const lastPage = validator.lastPage;
|
||||
|
||||
// const canLeave = await pages[lastPage]?.canLeave() ?? true;
|
||||
// const canEnter = await pages[lastPage]?.canEnter() ?? true;
|
||||
|
||||
// return canEnter && canLeave;
|
||||
return true;
|
||||
});
|
||||
this._wizardObject.registerNavigationValidator(async validator => true);
|
||||
|
||||
await Promise.all(wizardSetupPromises);
|
||||
this._model.extensionContext.subscriptions.push(
|
||||
@@ -146,8 +155,6 @@ export class WizardController {
|
||||
{});
|
||||
}));
|
||||
|
||||
this._wizardObject.doneButton.label = loc.START_MIGRATION_TEXT;
|
||||
|
||||
this._disposables.push(
|
||||
this._wizardObject.doneButton.onClick(async (e) => {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user