mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
SQL Assessment Database Selector (#16030)
* wip * wip * database selector table * fixed db icon * wip * fixed assessment results table * replaced large query * fix build error * updated spacing and other fixes * removed commented code * added search bar, fix margins, disable checkbox for offline db * change width of checkbox column * changed api to require databases * Revert "changed api to require databases" This reverts commit 20fe2d8bd223bae90dfb09609225a1781267a01d. * removed optional flag from databases parameter * removed icons on assessment dialog page * formatting changes, fixed search * bump STS * bumped extension version number * one excludeDbs
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "3.0.0-release.115",
|
"version": "3.0.0-release.116",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-net5.0.zip",
|
"Windows_86": "win-x86-net5.0.zip",
|
||||||
"Windows_64": "win-x64-net5.0.zip",
|
"Windows_64": "win-x64-net5.0.zip",
|
||||||
|
|||||||
@@ -702,7 +702,7 @@ export namespace SchemaCompareCancellationRequest {
|
|||||||
|
|
||||||
export interface SqlAssessmentParams {
|
export interface SqlAssessmentParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
targetType: azdata.sqlAssessment.SqlAssessmentTargetType
|
targetType: azdata.sqlAssessment.SqlAssessmentTargetType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GenerateSqlAssessmentScriptParams {
|
export interface GenerateSqlAssessmentScriptParams {
|
||||||
@@ -1016,10 +1016,11 @@ export namespace ProfilerSessionCreatedNotification {
|
|||||||
/// ------------------------------- <Sql Migration> -----------------------------
|
/// ------------------------------- <Sql Migration> -----------------------------
|
||||||
export interface SqlMigrationAssessmentParams {
|
export interface SqlMigrationAssessmentParams {
|
||||||
ownerUri: string;
|
ownerUri: string;
|
||||||
|
databases: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export namespace GetSqlMigrationAssessmentItemsRequest {
|
export namespace GetSqlMigrationAssessmentItemsRequest {
|
||||||
export const type = new RequestType<SqlAssessmentParams, mssql.AssessmentResult, void, void>('migration/getassessments');
|
export const type = new RequestType<SqlMigrationAssessmentParams, mssql.AssessmentResult, void, void>('migration/getassessments');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------- <Sql Migration> -----------------------------
|
// ------------------------------- <Sql Migration> -----------------------------
|
||||||
|
|||||||
2
extensions/mssql/src/mssql.d.ts
vendored
2
extensions/mssql/src/mssql.d.ts
vendored
@@ -599,5 +599,5 @@ export interface AssessmentResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ISqlMigrationService {
|
export interface ISqlMigrationService {
|
||||||
getAssessments(ownerUri: string): Promise<AssessmentResult | undefined>;
|
getAssessments(ownerUri: string, databases: string[]): Promise<AssessmentResult | undefined>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export class SqlMigrationService implements mssql.ISqlMigrationService {
|
|||||||
context.registerService(constants.SqlMigrationService, this);
|
context.registerService(constants.SqlMigrationService, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAssessments(ownerUri: string): Promise<mssql.AssessmentResult | undefined> {
|
async getAssessments(ownerUri: string, databases: string[]): Promise<mssql.AssessmentResult | undefined> {
|
||||||
let params: contracts.SqlMigrationAssessmentParams = { ownerUri: ownerUri };
|
let params: contracts.SqlMigrationAssessmentParams = { ownerUri: ownerUri, databases: databases };
|
||||||
try {
|
try {
|
||||||
return this.client.sendRequest(contracts.GetSqlMigrationAssessmentItemsRequest.type, params);
|
return this.client.sendRequest(contracts.GetSqlMigrationAssessmentItemsRequest.type, params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "sql-migration",
|
"name": "sql-migration",
|
||||||
"displayName": "%displayName%",
|
"displayName": "%displayName%",
|
||||||
"description": "%description%",
|
"description": "%description%",
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"publisher": "Microsoft",
|
"publisher": "Microsoft",
|
||||||
"preview": true,
|
"preview": true,
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt",
|
||||||
|
|||||||
@@ -464,6 +464,11 @@ export const SOURCE_CREDENTIALS = localize('sql.migration.source.credentials', "
|
|||||||
export const ENTER_YOUR_SQL_CREDS = localize('sql.migration.enter.your.sql.cred', "Enter the credential for source SQL Server instance. This credential will be used while migrating database(s) to Azure SQL.");
|
export const ENTER_YOUR_SQL_CREDS = localize('sql.migration.enter.your.sql.cred', "Enter the credential for source SQL Server instance. This credential will be used while migrating database(s) to Azure SQL.");
|
||||||
export const SERVER = localize('sql.migration.server', "Server");
|
export const SERVER = localize('sql.migration.server', "Server");
|
||||||
export const USERNAME = localize('sql.migration.username', "Username");
|
export const USERNAME = localize('sql.migration.username', "Username");
|
||||||
|
export const SIZE = localize('sql.migration.size', "Size (MB)");
|
||||||
|
export const LAST_BACKUP = localize('sql.migration.last.backup', "Last backup");
|
||||||
|
export const DATABASE_FOR_MIGRATION = localize('sql.migration.database.migration', "Databases for migration");
|
||||||
|
export const DATABASE_MIGRATE_TEXT = localize('sql.migrate.text', "Select database(s) that you want to migrate to Azure SQL");
|
||||||
|
export const OFFLINE_CAPS = localize('sql.migration.offline.caps', "OFFLINE");
|
||||||
|
|
||||||
//Assessment Dialog
|
//Assessment Dialog
|
||||||
export const ISSUES = localize('sql.migration.issues', "Issues");
|
export const ISSUES = localize('sql.migration.issues', "Issues");
|
||||||
@@ -493,6 +498,9 @@ export function IMPACT_OBJECT_NAME(objectName?: string): string {
|
|||||||
export function DATABASES(selectedCount: number, totalCount: number): string {
|
export function DATABASES(selectedCount: number, totalCount: number): string {
|
||||||
return localize('sql.migration.databases', "Databases ({0}/{1})", selectedCount, totalCount);
|
return localize('sql.migration.databases', "Databases ({0}/{1})", selectedCount, totalCount);
|
||||||
}
|
}
|
||||||
|
export function DATABASES_SELECTED(selectedCount: number, totalCount: number): string {
|
||||||
|
return localize('sql.migration.databases.selected', "{0}/{1} Databases Selected", selectedCount, totalCount);
|
||||||
|
}
|
||||||
export function ISSUES_COUNT(totalCount: number): string {
|
export function ISSUES_COUNT(totalCount: number): string {
|
||||||
return localize('sql.migration.issues.count', "Issues ({0})", totalCount);
|
return localize('sql.migration.issues.count', "Issues ({0})", totalCount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql';
|
||||||
import { IconPath, IconPathHelper } from '../../constants/iconPathHelper';
|
|
||||||
import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
|
import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine';
|
||||||
import * as constants from '../../constants/strings';
|
import * as constants from '../../constants/strings';
|
||||||
import { debounce } from '../../api/utils';
|
import { debounce } from '../../api/utils';
|
||||||
@@ -128,7 +127,7 @@ export class SqlDatabaseTree {
|
|||||||
'font-weight': 'bold',
|
'font-weight': 'bold',
|
||||||
'margin': '0px 8px 0px 36px'
|
'margin': '0px 8px 0px 36px'
|
||||||
},
|
},
|
||||||
value: constants.DATABASES(this.selectedDbs.length, this._model._serverDatabases.length)
|
value: constants.DATABASES(0, this._model._databaseAssessment.length)
|
||||||
}).component();
|
}).component();
|
||||||
return this._databaseCount;
|
return this._databaseCount;
|
||||||
}
|
}
|
||||||
@@ -146,22 +145,22 @@ export class SqlDatabaseTree {
|
|||||||
{
|
{
|
||||||
displayName: '',
|
displayName: '',
|
||||||
valueType: azdata.DeclarativeDataType.boolean,
|
valueType: azdata.DeclarativeDataType.boolean,
|
||||||
width: 20,
|
width: 10,
|
||||||
isReadOnly: false,
|
isReadOnly: false,
|
||||||
showCheckAll: true,
|
showCheckAll: true,
|
||||||
headerCssStyles: headerLeft,
|
headerCssStyles: headerLeft,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: constants.DATABASE,
|
displayName: constants.DATABASE,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: 100,
|
width: 95,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerLeft
|
headerCssStyles: headerLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: constants.ISSUES,
|
displayName: constants.ISSUES,
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: 30,
|
width: 45,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerRight,
|
headerCssStyles: headerRight,
|
||||||
}
|
}
|
||||||
@@ -170,7 +169,7 @@ export class SqlDatabaseTree {
|
|||||||
).component();
|
).component();
|
||||||
this._databaseTable.onDataChanged(() => {
|
this._databaseTable.onDataChanged(() => {
|
||||||
this._databaseCount.updateProperties({
|
this._databaseCount.updateProperties({
|
||||||
'value': constants.DATABASES(this.selectedDbs().length, this._model._serverDatabases.length)
|
'value': constants.DATABASES(this.selectedDbs().length, this._model._databaseAssessment.length)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
this._databaseTable.onRowSelected(async (e) => {
|
this._databaseTable.onRowSelected(async (e) => {
|
||||||
@@ -224,8 +223,7 @@ export class SqlDatabaseTree {
|
|||||||
if (this._databaseTableValues && value?.length > 0) {
|
if (this._databaseTableValues && value?.length > 0) {
|
||||||
const filter: number[] = [];
|
const filter: number[] = [];
|
||||||
this._databaseTableValues.forEach((row, index) => {
|
this._databaseTableValues.forEach((row, index) => {
|
||||||
const flexContainer: azdata.FlexContainer = row[1]?.value as azdata.FlexContainer;
|
const textComponent: azdata.TextComponent = row[1] as azdata.TextComponent;
|
||||||
const textComponent: azdata.TextComponent = flexContainer.items[1] as azdata.TextComponent;
|
|
||||||
const cellText = textComponent.value?.toLowerCase();
|
const cellText = textComponent.value?.toLowerCase();
|
||||||
const searchText: string = value.toLowerCase();
|
const searchText: string = value.toLowerCase();
|
||||||
if (cellText?.includes(searchText)) {
|
if (cellText?.includes(searchText)) {
|
||||||
@@ -242,20 +240,23 @@ export class SqlDatabaseTree {
|
|||||||
private createInstanceComponent(): azdata.DivContainer {
|
private createInstanceComponent(): azdata.DivContainer {
|
||||||
this._instanceTable = this._view.modelBuilder.declarativeTable().withProps(
|
this._instanceTable = this._view.modelBuilder.declarativeTable().withProps(
|
||||||
{
|
{
|
||||||
|
CSSStyles: {
|
||||||
|
'table-layout': 'fixed'
|
||||||
|
},
|
||||||
|
width: 200,
|
||||||
enableRowSelection: true,
|
enableRowSelection: true,
|
||||||
width: 170,
|
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
displayName: constants.INSTANCE,
|
displayName: constants.INSTANCE,
|
||||||
valueType: azdata.DeclarativeDataType.component,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: 130,
|
width: 105,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerLeft
|
headerCssStyles: headerLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: constants.WARNINGS,
|
displayName: constants.WARNINGS,
|
||||||
valueType: azdata.DeclarativeDataType.string,
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
width: 30,
|
width: 45,
|
||||||
isReadOnly: true,
|
isReadOnly: true,
|
||||||
headerCssStyles: headerRight
|
headerCssStyles: headerRight
|
||||||
}
|
}
|
||||||
@@ -264,6 +265,7 @@ export class SqlDatabaseTree {
|
|||||||
|
|
||||||
const instanceContainer = this._view.modelBuilder.divContainer().withItems([this._instanceTable]).withProps({
|
const instanceContainer = this._view.modelBuilder.divContainer().withItems([this._instanceTable]).withProps({
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
|
'width': '200px',
|
||||||
'margin': '19px 8px 0px 34px'
|
'margin': '19px 8px 0px 34px'
|
||||||
}
|
}
|
||||||
}).component();
|
}).component();
|
||||||
@@ -279,7 +281,7 @@ export class SqlDatabaseTree {
|
|||||||
});
|
});
|
||||||
this._recommendation.value = constants.WARNINGS_DETAILS;
|
this._recommendation.value = constants.WARNINGS_DETAILS;
|
||||||
this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues.length);
|
this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues.length);
|
||||||
if (this._model._targetType === MigrationTargetType.SQLMI) {
|
if (this._targetType === MigrationTargetType.SQLMI) {
|
||||||
await this.refreshResults();
|
await this.refreshResults();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -362,7 +364,7 @@ export class SqlDatabaseTree {
|
|||||||
|
|
||||||
private createNoIssuesText(): azdata.FlexContainer {
|
private createNoIssuesText(): azdata.FlexContainer {
|
||||||
let message: azdata.TextComponent;
|
let message: azdata.TextComponent;
|
||||||
if (this._model._targetType === MigrationTargetType.SQLVM) {
|
if (this._targetType === MigrationTargetType.SQLVM) {
|
||||||
message = this._view.modelBuilder.text().withProps({
|
message = this._view.modelBuilder.text().withProps({
|
||||||
value: constants.NO_ISSUES_FOUND_VM,
|
value: constants.NO_ISSUES_FOUND_VM,
|
||||||
CSSStyles: {
|
CSSStyles: {
|
||||||
@@ -740,7 +742,7 @@ export class SqlDatabaseTree {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async refreshResults(): Promise<void> {
|
public async refreshResults(): Promise<void> {
|
||||||
if (this._model._targetType === MigrationTargetType.SQLMI) {
|
if (this._targetType === MigrationTargetType.SQLMI) {
|
||||||
if (this._activeIssues.length === 0) {
|
if (this._activeIssues.length === 0) {
|
||||||
/// show no issues here
|
/// show no issues here
|
||||||
this._assessmentsTable.updateCssStyles({
|
this._assessmentsTable.updateCssStyles({
|
||||||
@@ -810,8 +812,7 @@ export class SqlDatabaseTree {
|
|||||||
public async initialize(): Promise<void> {
|
public async initialize(): Promise<void> {
|
||||||
let instanceTableValues: azdata.DeclarativeTableCellValue[][] = [];
|
let instanceTableValues: azdata.DeclarativeTableCellValue[][] = [];
|
||||||
this._databaseTableValues = [];
|
this._databaseTableValues = [];
|
||||||
const excludedDatabases = ['master', 'msdb', 'tempdb', 'model'];
|
this._dbNames = this._model._databaseAssessment;
|
||||||
this._dbNames = (await azdata.connection.listDatabases(this._model.sourceConnectionId)).filter(db => !excludedDatabases.includes(db));
|
|
||||||
const selectedDbs = (this._targetType === MigrationTargetType.SQLVM) ? this._model._vmDbs : this._model._miDbs;
|
const selectedDbs = (this._targetType === MigrationTargetType.SQLVM) ? this._model._vmDbs : this._model._miDbs;
|
||||||
this._serverName = (await this._model.getSourceConnectionProfile()).serverName;
|
this._serverName = (await this._model.getSourceConnectionProfile()).serverName;
|
||||||
|
|
||||||
@@ -819,7 +820,8 @@ export class SqlDatabaseTree {
|
|||||||
instanceTableValues = [
|
instanceTableValues = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName),
|
// value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName),
|
||||||
|
value: this._serverName,
|
||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -836,7 +838,8 @@ export class SqlDatabaseTree {
|
|||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, db),
|
// value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, db),
|
||||||
|
value: db,
|
||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -850,7 +853,8 @@ export class SqlDatabaseTree {
|
|||||||
instanceTableValues = [
|
instanceTableValues = [
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName),
|
// value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName),
|
||||||
|
value: this._serverName,
|
||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -877,7 +881,8 @@ export class SqlDatabaseTree {
|
|||||||
enabled: selectable
|
enabled: selectable
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.createIconTextCell((selectable) ? IconPathHelper.sqlDatabaseLogo : IconPathHelper.sqlDatabaseWarningLogo, db.name),
|
// value: this.createIconTextCell((selectable) ? IconPathHelper.sqlDatabaseLogo : IconPathHelper.sqlDatabaseWarningLogo, db.name),
|
||||||
|
value: db.name,
|
||||||
style: styleLeft
|
style: styleLeft
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -892,41 +897,4 @@ export class SqlDatabaseTree {
|
|||||||
await this._databaseTable.setDataValues(this._databaseTableValues);
|
await this._databaseTable.setDataValues(this._databaseTableValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer {
|
|
||||||
|
|
||||||
const iconComponent = this._view.modelBuilder.image().withProps({
|
|
||||||
iconPath: icon,
|
|
||||||
iconWidth: '16px',
|
|
||||||
iconHeight: '16px',
|
|
||||||
width: '20px',
|
|
||||||
height: '20px'
|
|
||||||
}).component();
|
|
||||||
const textComponent = this._view.modelBuilder.text().withProps({
|
|
||||||
value: text,
|
|
||||||
title: text,
|
|
||||||
CSSStyles: {
|
|
||||||
'margin': '0px',
|
|
||||||
'width': '110px'
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
|
|
||||||
const cellContainer = this._view.modelBuilder.flexContainer().withProps({
|
|
||||||
CSSStyles: {
|
|
||||||
'justify-content': 'left'
|
|
||||||
}
|
|
||||||
}).component();
|
|
||||||
cellContainer.addItem(iconComponent, {
|
|
||||||
flex: '0',
|
|
||||||
CSSStyles: {
|
|
||||||
'width': '32px'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cellContainer.addItem(textComponent, {
|
|
||||||
CSSStyles: {
|
|
||||||
'width': 'auto'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return cellContainer;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _authenticationType!: MigrationSourceAuthenticationType;
|
public _authenticationType!: MigrationSourceAuthenticationType;
|
||||||
public _sqlServerUsername!: string;
|
public _sqlServerUsername!: string;
|
||||||
public _sqlServerPassword!: string;
|
public _sqlServerPassword!: string;
|
||||||
|
public _databaseAssessment!: string[];
|
||||||
|
|
||||||
public _subscriptions!: azureResource.AzureResourceSubscription[];
|
public _subscriptions!: azureResource.AzureResourceSubscription[];
|
||||||
|
|
||||||
@@ -120,7 +121,6 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _blobContainers!: azureResource.BlobContainer[];
|
public _blobContainers!: azureResource.BlobContainer[];
|
||||||
public _refreshNetworkShareLocation!: azureResource.BlobContainer[];
|
public _refreshNetworkShareLocation!: azureResource.BlobContainer[];
|
||||||
public _targetDatabaseNames!: string[];
|
public _targetDatabaseNames!: string[];
|
||||||
public _serverDatabases!: string[];
|
|
||||||
|
|
||||||
public _sqlMigrationServiceResourceGroup!: string;
|
public _sqlMigrationServiceResourceGroup!: string;
|
||||||
public _sqlMigrationService!: SqlMigrationService;
|
public _sqlMigrationService!: SqlMigrationService;
|
||||||
@@ -138,6 +138,13 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
public _targetType!: MigrationTargetType;
|
public _targetType!: MigrationTargetType;
|
||||||
public refreshDatabaseBackupPage!: boolean;
|
public refreshDatabaseBackupPage!: boolean;
|
||||||
|
|
||||||
|
public excludeDbs: string[] = [
|
||||||
|
'master',
|
||||||
|
'tempdb',
|
||||||
|
'msdb',
|
||||||
|
'model'
|
||||||
|
];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _extensionContext: vscode.ExtensionContext,
|
private readonly _extensionContext: vscode.ExtensionContext,
|
||||||
private readonly _sourceConnectionId: string,
|
private readonly _sourceConnectionId: string,
|
||||||
@@ -162,28 +169,25 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
this._currentState = newState;
|
this._currentState = newState;
|
||||||
this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState });
|
this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState });
|
||||||
}
|
}
|
||||||
|
public async getDatabases(): Promise<string[]> {
|
||||||
|
let temp = await azdata.connection.listDatabases(this.sourceConnectionId);
|
||||||
|
let finalResult = temp.filter((name) => !this.excludeDbs.includes(name));
|
||||||
|
return finalResult;
|
||||||
|
}
|
||||||
|
|
||||||
public async getServerAssessments(): Promise<ServerAssessement> {
|
public async getDatabaseAssessments(): Promise<ServerAssessement> {
|
||||||
const excludeDbs: string[] = [
|
|
||||||
'master',
|
|
||||||
'tempdb',
|
|
||||||
'msdb',
|
|
||||||
'model'
|
|
||||||
];
|
|
||||||
|
|
||||||
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId);
|
||||||
|
// stress test backend & dialog component
|
||||||
const assessmentResults = await this.migrationService.getAssessments(
|
const assessmentResults = await this.migrationService.getAssessments(
|
||||||
ownerUri
|
ownerUri,
|
||||||
|
this._databaseAssessment
|
||||||
);
|
);
|
||||||
this._serverDatabases = await (await azdata.connection.listDatabases(this.sourceConnectionId)).filter((name) => !excludeDbs.includes(name));
|
const dbAssessments = assessmentResults?.assessmentResult.databases.filter(d => !this.excludeDbs.includes(d.name)).map(d => {
|
||||||
const dbAssessments = assessmentResults?.assessmentResult.databases.filter(d => !excludeDbs.includes(d.name)).map(d => {
|
|
||||||
return {
|
return {
|
||||||
name: d.name,
|
name: d.name,
|
||||||
issues: d.items.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? []
|
issues: d.items.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? []
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
this._assessmentResults = {
|
this._assessmentResults = {
|
||||||
issues: assessmentResults?.assessmentResult.items?.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? [],
|
issues: assessmentResults?.assessmentResult.items?.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? [],
|
||||||
databaseAssessments: dbAssessments! ?? []
|
databaseAssessments: dbAssessments! ?? []
|
||||||
@@ -192,7 +196,7 @@ export class MigrationStateModel implements Model, vscode.Disposable {
|
|||||||
return this._assessmentResults;
|
return this._assessmentResults;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDatabaseAssessments(databaseName: string): mssql.SqlMigrationAssessmentResultItem[] | undefined {
|
public findDatabaseAssessments(databaseName: string): mssql.SqlMigrationAssessmentResultItem[] | undefined {
|
||||||
return this._assessmentResults.databaseAssessments.find(databaseAsssessment => databaseAsssessment.name === databaseName)?.issues;
|
return this._assessmentResults.databaseAssessments.find(databaseAsssessment => databaseAsssessment.name === databaseName)?.issues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
312
extensions/sql-migration/src/wizard/databaseSelectorPage.ts
Normal file
312
extensions/sql-migration/src/wizard/databaseSelectorPage.ts
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||||
|
import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine';
|
||||||
|
import * as constants from '../constants/strings';
|
||||||
|
import { IconPath, IconPathHelper } from '../constants/iconPathHelper';
|
||||||
|
import { debounce } from '../api/utils';
|
||||||
|
|
||||||
|
const headerLeft: azdata.CssStyles = {
|
||||||
|
'border': 'none',
|
||||||
|
'text-align': 'left',
|
||||||
|
'white-space': 'nowrap',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
'overflow': 'hidden',
|
||||||
|
'border-bottom': '1px solid'
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleLeft: azdata.CssStyles = {
|
||||||
|
'border': 'none',
|
||||||
|
'text-align': 'left',
|
||||||
|
'white-space': 'nowrap',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
'overflow': 'hidden',
|
||||||
|
};
|
||||||
|
|
||||||
|
const styleCenter: azdata.CssStyles = {
|
||||||
|
'border': 'none',
|
||||||
|
'text-align': 'center',
|
||||||
|
'white-space': 'nowrap',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
'overflow': 'hidden',
|
||||||
|
};
|
||||||
|
|
||||||
|
export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||||
|
private _view!: azdata.ModelView;
|
||||||
|
private _databaseSelectorTable!: azdata.DeclarativeTableComponent;
|
||||||
|
private _dbNames!: string[];
|
||||||
|
private _dbCount!: azdata.TextComponent;
|
||||||
|
private _databaseTableValues!: azdata.DeclarativeTableCellValue[][];
|
||||||
|
|
||||||
|
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||||
|
super(wizard, azdata.window.createWizardPage(constants.SOURCE_CONFIGURATION, 'MigrationModePage'), migrationStateModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||||
|
this._view = view;
|
||||||
|
|
||||||
|
const flex = view.modelBuilder.flexContainer().withLayout({
|
||||||
|
flexFlow: 'row',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%'
|
||||||
|
}).component();
|
||||||
|
flex.addItem(await this.createRootContainer(view), { flex: '1 1 auto' });
|
||||||
|
|
||||||
|
await view.initializeModel(flex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async onPageEnter(): Promise<void> {
|
||||||
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public async onPageLeave(): Promise<void> {
|
||||||
|
this.migrationStateModel._databaseAssessment = this.selectedDbs();
|
||||||
|
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private createSearchComponent(): azdata.DivContainer {
|
||||||
|
let resourceSearchBox = this._view.modelBuilder.inputBox().withProps({
|
||||||
|
stopEnterPropagation: true,
|
||||||
|
placeHolder: constants.SEARCH,
|
||||||
|
width: 200
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
resourceSearchBox.onTextChanged(value => this._filterTableList(value));
|
||||||
|
|
||||||
|
const searchContainer = this._view.modelBuilder.divContainer().withItems([resourceSearchBox]).withProps({
|
||||||
|
CSSStyles: {
|
||||||
|
'width': '200px',
|
||||||
|
'margin': '10px 8px 0px 0px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
return searchContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@debounce(500)
|
||||||
|
private _filterTableList(value: string): void {
|
||||||
|
if (this._databaseTableValues && value?.length > 0) {
|
||||||
|
const filter: number[] = [];
|
||||||
|
this._databaseTableValues.forEach((row, index) => {
|
||||||
|
const flexContainer: azdata.FlexContainer = row[1]?.value as azdata.FlexContainer;
|
||||||
|
const textComponent: azdata.TextComponent = flexContainer.items[1] as azdata.TextComponent;
|
||||||
|
const cellText = textComponent.value?.toLowerCase();
|
||||||
|
const searchText: string = value.toLowerCase();
|
||||||
|
if (cellText?.includes(searchText)) {
|
||||||
|
filter.push(index);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this._databaseSelectorTable.setFilter(filter);
|
||||||
|
} else {
|
||||||
|
this._databaseSelectorTable.setFilter(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async createRootContainer(view: azdata.ModelView): Promise<azdata.FlexContainer> {
|
||||||
|
const providerId = (await this.migrationStateModel.getSourceConnectionProfile()).providerId;
|
||||||
|
const metaDataService = azdata.dataprotocol.getProvider<azdata.MetadataProvider>(providerId, azdata.DataProviderType.MetadataProvider);
|
||||||
|
const ownerUri = await azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId);
|
||||||
|
const results = <azdata.DatabaseInfo[]>await metaDataService.getDatabases(ownerUri);
|
||||||
|
const excludeDbs: string[] = [
|
||||||
|
'master',
|
||||||
|
'tempdb',
|
||||||
|
'msdb',
|
||||||
|
'model'
|
||||||
|
];
|
||||||
|
this._dbNames = [];
|
||||||
|
let finalResult = results.filter((db) => !excludeDbs.includes(db.options.name));
|
||||||
|
finalResult.sort((a, b) => a.options.name.localeCompare(b.options.name));
|
||||||
|
this._databaseTableValues = [];
|
||||||
|
for (let index in finalResult) {
|
||||||
|
let selectable = true;
|
||||||
|
if (constants.OFFLINE_CAPS.includes(finalResult[index].options.state)) {
|
||||||
|
selectable = false;
|
||||||
|
}
|
||||||
|
this._databaseTableValues.push([
|
||||||
|
{
|
||||||
|
value: false,
|
||||||
|
style: styleCenter,
|
||||||
|
enabled: selectable
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, finalResult[index].options.name),
|
||||||
|
style: styleLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${finalResult[index].options.state}`,
|
||||||
|
style: styleLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${finalResult[index].options.sizeInMB}`,
|
||||||
|
style: styleLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: `${finalResult[index].options.lastBackup}`,
|
||||||
|
style: styleLeft
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
this._dbNames.push(finalResult[index].options.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = this._view.modelBuilder.text().withProps({
|
||||||
|
value: constants.DATABASE_FOR_MIGRATION,
|
||||||
|
CSSStyles: {
|
||||||
|
'font-size': '28px',
|
||||||
|
'line-size': '19px',
|
||||||
|
'margin': '16px 0px 20px 0px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const text = this._view.modelBuilder.text().withProps({
|
||||||
|
value: constants.DATABASE_MIGRATE_TEXT,
|
||||||
|
CSSStyles: {
|
||||||
|
'font-size': '13px',
|
||||||
|
'line-size': '19px',
|
||||||
|
'margin': '10px 0px 0px 0px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._dbCount = this._view.modelBuilder.text().withProps({
|
||||||
|
value: constants.DATABASES_SELECTED(this.selectedDbs.length, this._databaseTableValues.length),
|
||||||
|
CSSStyles: {
|
||||||
|
'font-size': '13px',
|
||||||
|
'line-size': '19px',
|
||||||
|
'margin': '10px 0px 0px 0px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
this._databaseSelectorTable = this._view.modelBuilder.declarativeTable().withProps(
|
||||||
|
{
|
||||||
|
enableRowSelection: true,
|
||||||
|
width: '800px',
|
||||||
|
CSSStyles: {
|
||||||
|
'table-layout': 'fixed',
|
||||||
|
'border': 'none'
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
displayName: '',
|
||||||
|
valueType: azdata.DeclarativeDataType.boolean,
|
||||||
|
width: 1,
|
||||||
|
isReadOnly: false,
|
||||||
|
showCheckAll: true,
|
||||||
|
headerCssStyles: headerLeft,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: constants.DATABASE,
|
||||||
|
valueType: azdata.DeclarativeDataType.component,
|
||||||
|
width: 100,
|
||||||
|
isReadOnly: true,
|
||||||
|
headerCssStyles: headerLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: constants.STATUS,
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
width: 20,
|
||||||
|
isReadOnly: true,
|
||||||
|
headerCssStyles: headerLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: constants.SIZE,
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
width: 30,
|
||||||
|
isReadOnly: true,
|
||||||
|
headerCssStyles: headerLeft
|
||||||
|
},
|
||||||
|
{
|
||||||
|
displayName: constants.LAST_BACKUP,
|
||||||
|
valueType: azdata.DeclarativeDataType.string,
|
||||||
|
width: 50,
|
||||||
|
isReadOnly: true,
|
||||||
|
headerCssStyles: headerLeft
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
).component();
|
||||||
|
|
||||||
|
await this._databaseSelectorTable.setDataValues(this._databaseTableValues);
|
||||||
|
this._databaseSelectorTable.onDataChanged(() => {
|
||||||
|
this._dbCount.updateProperties({
|
||||||
|
'value': constants.DATABASES_SELECTED(this.selectedDbs().length, this._databaseTableValues.length)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const flex = view.modelBuilder.flexContainer().withLayout({
|
||||||
|
flexFlow: 'column',
|
||||||
|
height: '100%',
|
||||||
|
width: '100%'
|
||||||
|
}).withProps({
|
||||||
|
CSSStyles: {
|
||||||
|
'margin': '0px 0px 0px 28px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
flex.addItem(title, { flex: '0 0 auto' });
|
||||||
|
flex.addItem(text, { flex: '0 0 auto' });
|
||||||
|
flex.addItem(this.createSearchComponent(), { flex: '0 0 auto' });
|
||||||
|
flex.addItem(this._dbCount, { flex: '0 0 auto' });
|
||||||
|
flex.addItem(this._databaseSelectorTable);
|
||||||
|
return flex;
|
||||||
|
// insert names of databases into table
|
||||||
|
}
|
||||||
|
|
||||||
|
public selectedDbs(): string[] {
|
||||||
|
let result: string[] = [];
|
||||||
|
this._databaseSelectorTable.dataValues?.forEach((arr, index) => {
|
||||||
|
if (arr[0].value === true) {
|
||||||
|
result.push(this._dbNames[index]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private createIconTextCell(icon: IconPath, text: string): azdata.FlexContainer {
|
||||||
|
|
||||||
|
const iconComponent = this._view.modelBuilder.image().withProps({
|
||||||
|
iconPath: icon,
|
||||||
|
iconWidth: '16px',
|
||||||
|
iconHeight: '16px',
|
||||||
|
width: '20px',
|
||||||
|
height: '20px'
|
||||||
|
}).component();
|
||||||
|
const textComponent = this._view.modelBuilder.text().withProps({
|
||||||
|
value: text,
|
||||||
|
title: text,
|
||||||
|
CSSStyles: {
|
||||||
|
'margin': '0px',
|
||||||
|
'width': '110px'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
|
||||||
|
const cellContainer = this._view.modelBuilder.flexContainer().withProps({
|
||||||
|
CSSStyles: {
|
||||||
|
'justify-content': 'left'
|
||||||
|
}
|
||||||
|
}).component();
|
||||||
|
cellContainer.addItem(iconComponent, {
|
||||||
|
flex: '0',
|
||||||
|
CSSStyles: {
|
||||||
|
'width': '32px'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cellContainer.addItem(textComponent, {
|
||||||
|
CSSStyles: {
|
||||||
|
'width': 'auto'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return cellContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -403,13 +403,13 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
private changeTargetType(newTargetType: string) {
|
private changeTargetType(newTargetType: string) {
|
||||||
if (newTargetType === MigrationTargetType.SQLMI) {
|
if (newTargetType === MigrationTargetType.SQLMI) {
|
||||||
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI;
|
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_MI;
|
||||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._miDbs.length, this.migrationStateModel._serverDatabases.length);
|
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._miDbs.length, this.migrationStateModel._databaseAssessment.length);
|
||||||
this.migrationStateModel._targetType = MigrationTargetType.SQLMI;
|
this.migrationStateModel._targetType = MigrationTargetType.SQLMI;
|
||||||
this._azureSubscriptionText.value = constants.SELECT_AZURE_MI;
|
this._azureSubscriptionText.value = constants.SELECT_AZURE_MI;
|
||||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs;
|
this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs;
|
||||||
} else {
|
} else {
|
||||||
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
|
this._viewAssessmentsHelperText.value = constants.SKU_RECOMMENDATION_VIEW_ASSESSMENT_VM;
|
||||||
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._vmDbs.length, this.migrationStateModel._serverDatabases.length);
|
this._databaseSelectedHelperText.value = constants.TOTAL_DATABASES_SELECTED(this.migrationStateModel._vmDbs.length, this.migrationStateModel._databaseAssessment.length);
|
||||||
this.migrationStateModel._targetType = MigrationTargetType.SQLVM;
|
this.migrationStateModel._targetType = MigrationTargetType.SQLVM;
|
||||||
this._azureSubscriptionText.value = constants.SELECT_AZURE_VM;
|
this._azureSubscriptionText.value = constants.SELECT_AZURE_VM;
|
||||||
this.migrationStateModel._migrationDbs = this.migrationStateModel._vmDbs;
|
this.migrationStateModel._migrationDbs = this.migrationStateModel._vmDbs;
|
||||||
@@ -424,7 +424,7 @@ export class SKURecommendationPage extends MigrationWizardPage {
|
|||||||
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName;
|
||||||
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
|
this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName);
|
||||||
try {
|
try {
|
||||||
await this.migrationStateModel.getServerAssessments();
|
await this.migrationStateModel.getDatabaseAssessments();
|
||||||
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length);
|
this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { AccountsSelectionPage } from './accountsSelectionPage';
|
|||||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||||
import { SummaryPage } from './summaryPage';
|
import { SummaryPage } from './summaryPage';
|
||||||
import { MigrationModePage } from './migrationModePage';
|
import { MigrationModePage } from './migrationModePage';
|
||||||
|
import { DatabaseSelectorPage } from './databaseSelectorPage';
|
||||||
|
|
||||||
export const WIZARD_INPUT_COMPONENT_WIDTH = '600px';
|
export const WIZARD_INPUT_COMPONENT_WIDTH = '600px';
|
||||||
export class WizardController {
|
export class WizardController {
|
||||||
@@ -38,6 +39,7 @@ export class WizardController {
|
|||||||
wizard.generateScriptButton.hidden = true;
|
wizard.generateScriptButton.hidden = true;
|
||||||
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
|
const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel);
|
||||||
const migrationModePage = new MigrationModePage(wizard, stateModel);
|
const migrationModePage = new MigrationModePage(wizard, stateModel);
|
||||||
|
const databaseSelectorPage = new DatabaseSelectorPage(wizard, stateModel);
|
||||||
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
||||||
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
||||||
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
||||||
@@ -45,6 +47,7 @@ export class WizardController {
|
|||||||
|
|
||||||
const pages: MigrationWizardPage[] = [
|
const pages: MigrationWizardPage[] = [
|
||||||
azureAccountsPage,
|
azureAccountsPage,
|
||||||
|
databaseSelectorPage,
|
||||||
skuRecommendationPage,
|
skuRecommendationPage,
|
||||||
migrationModePage,
|
migrationModePage,
|
||||||
databaseBackupPage,
|
databaseBackupPage,
|
||||||
|
|||||||
Reference in New Issue
Block a user