Add SQL DB offline migration wizard experience (#20403)

* sql db wizard with target selection

* add database table selection

* add sqldb to service and IR page

* Code complete

* navigation bug fixes

* fix target db selection

* improve sqldb error and status reporting

* fix error count bug

* remove table status inference

* address review feedback

* update resource strings and content

* fix migraton status string, use localized value

* fix ux navigation issues

* fix back/fwd w/o changes from changing data
This commit is contained in:
brian-harris
2022-08-19 18:12:34 -07:00
committed by GitHub
parent c0b09dcedd
commit 7a736b76fa
42 changed files with 5716 additions and 4209 deletions

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as azdata from 'azdata';
import { MigrationMode, MigrationStateModel, NetworkContainerType } from '../../models/stateMachine';
import { MigrationMode, MigrationStateModel, MigrationTargetType, NetworkContainerType } from '../../models/stateMachine';
import * as constants from '../../constants/strings';
import * as styles from '../../constants/styles';
@@ -25,22 +25,20 @@ export class TargetDatabaseSummaryDialog {
this._dialogObject = azdata.window.createModelViewDialog(
constants.DATABASE_TO_BE_MIGRATED,
'TargetDatabaseSummaryDialog',
dialogWidth
);
dialogWidth);
}
async initialize(): Promise<void> {
let tab = azdata.window.createTab('sql.migration.CreateResourceGroupDialog');
const tab = azdata.window.createTab('sql.migration.CreateResourceGroupDialog');
tab.registerContent(async (view: azdata.ModelView) => {
this._view = view;
const databaseCount = this._view.modelBuilder.text().withProps({
value: constants.COUNT_DATABASES(this._model._databasesForMigration.length),
CSSStyles: {
...styles.BODY_CSS,
'margin-bottom': '20px'
}
}).component();
const isSqlDbMigration = this._model._targetType === MigrationTargetType.SQLDB;
const databaseCount = this._view.modelBuilder.text()
.withProps({
value: constants.COUNT_DATABASES(this._model._databasesForMigration.length),
CSSStyles: { ...styles.BODY_CSS, 'margin-bottom': '20px' }
}).component();
const headerCssStyle = {
'border': 'none',
@@ -61,7 +59,7 @@ export class TargetDatabaseSummaryDialog {
const columnWidth = 150;
let columns: azdata.DeclarativeTableColumn[] = [
const columns: azdata.DeclarativeTableColumn[] = [
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.SOURCE_DATABASE,
@@ -70,7 +68,6 @@ export class TargetDatabaseSummaryDialog {
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
},
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.TARGET_DATABASE_NAME,
@@ -78,46 +75,59 @@ export class TargetDatabaseSummaryDialog {
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
}
];
}];
if (this._model._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
columns.push(
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.LOCATION,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
},
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.RESOURCE_GROUP,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
},
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.SUMMARY_AZURE_STORAGE,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
},
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.BLOB_CONTAINER,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
},
{
valueType: azdata.DeclarativeDataType.string,
displayName: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle,
hidden: this._model._databaseBackup.migrationMode === MigrationMode.ONLINE
});
} else if (isSqlDbMigration) {
columns.push({
valueType: azdata.DeclarativeDataType.string,
displayName: constants.LOCATION,
displayName: constants.TARGET_TABLE_COUNT_NAME,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
}, {
valueType: azdata.DeclarativeDataType.string,
displayName: constants.RESOURCE_GROUP,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
}, {
valueType: azdata.DeclarativeDataType.string,
displayName: constants.SUMMARY_AZURE_STORAGE,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
}, {
valueType: azdata.DeclarativeDataType.string,
displayName: constants.BLOB_CONTAINER,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle
}, {
valueType: azdata.DeclarativeDataType.string,
displayName: constants.BLOB_CONTAINER_LAST_BACKUP_FILE,
isReadOnly: true,
width: columnWidth,
rowCssStyles: rowCssStyle,
headerCssStyles: headerCssStyle,
hidden: this._model._databaseBackup.migrationMode === MigrationMode.ONLINE
});
} else {
columns.push({
@@ -134,59 +144,54 @@ export class TargetDatabaseSummaryDialog {
this._model._databasesForMigration.forEach((db, index) => {
const tableRow: azdata.DeclarativeTableCellValue[] = [];
tableRow.push({
value: db
}, {
value: this._model._targetDatabaseNames[index]
});
tableRow.push(
{ value: db },
{ value: this._model._targetDatabaseNames[index] });
if (this._model._databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
tableRow.push({
value: this._model._databaseBackup.blobs[index].storageAccount.location
}, {
value: this._model._databaseBackup.blobs[index].storageAccount.resourceGroup!
}, {
value: this._model._databaseBackup.blobs[index].storageAccount.name
}, {
value: this._model._databaseBackup.blobs[index].blobContainer.name
});
tableRow.push(
{ value: this._model._databaseBackup.blobs[index].storageAccount.location },
{ value: this._model._databaseBackup.blobs[index].storageAccount.resourceGroup! },
{ value: this._model._databaseBackup.blobs[index].storageAccount.name },
{ value: this._model._databaseBackup.blobs[index].blobContainer.name });
if (this._model._databaseBackup.migrationMode === MigrationMode.OFFLINE) {
tableRow.push({
value: this._model._databaseBackup.blobs[index].lastBackupFile!
});
tableRow.push(
{ value: this._model._databaseBackup.blobs[index].lastBackupFile! });
}
} else if (isSqlDbMigration) {
const totalTables = this._model._sourceTargetMapping.get(db)?.sourceTables.size ?? 0;
let selectedTables = 0;
this._model._sourceTargetMapping.get(db)?.sourceTables.forEach(
tableInfo => selectedTables += tableInfo.selectedForMigration ? 1 : 0);
tableRow.push(
{ value: constants.TOTAL_TABLES_SELECTED(selectedTables, totalTables) });
} else {
tableRow.push({
value: this._model._databaseBackup.networkShares[index].networkShareLocation
});
tableRow.push(
{ value: this._model._databaseBackup.networkShares[index].networkShareLocation });
}
tableRows.push(tableRow);
});
const databaseTable: azdata.DeclarativeTableComponent = this._view.modelBuilder.declarativeTable().withProps({
ariaLabel: constants.DATABASE_TO_BE_MIGRATED,
columns: columns,
dataValues: tableRows,
width: this._tableLength
}).component();
const databaseTable: azdata.DeclarativeTableComponent = this._view.modelBuilder.declarativeTable()
.withProps({
ariaLabel: constants.DATABASE_TO_BE_MIGRATED,
columns: columns,
dataValues: tableRows,
width: this._tableLength
}).component();
const container = this._view.modelBuilder.flexContainer()
.withLayout({ flexFlow: 'column' })
.withItems([databaseCount, databaseTable])
.component();
const form = this._view.modelBuilder.formContainer()
.withFormItems(
[{ component: container }],
{ horizontal: false })
.withLayout({ width: '100%' })
.component();
const container = this._view.modelBuilder.flexContainer().withLayout({
flexFlow: 'column',
}).withItems([
databaseCount,
databaseTable
]).component();
const formBuilder = this._view.modelBuilder.formContainer().withFormItems(
[
{
component: container
}
],
{
horizontal: false
}
);
const form = formBuilder.withLayout({ width: '100%' }).component();
return view.initializeModel(form);
});
this._dialogObject.content = [tab];