diff --git a/extensions/mssql/config.json b/extensions/mssql/config.json index be936268ef..5f453c6f63 100644 --- a/extensions/mssql/config.json +++ b/extensions/mssql/config.json @@ -1,6 +1,6 @@ { "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": { "Windows_86": "win-x86-net5.0.zip", "Windows_64": "win-x64-net5.0.zip", diff --git a/extensions/mssql/src/contracts.ts b/extensions/mssql/src/contracts.ts index 70ea5d5ac6..0f2d423173 100644 --- a/extensions/mssql/src/contracts.ts +++ b/extensions/mssql/src/contracts.ts @@ -702,7 +702,7 @@ export namespace SchemaCompareCancellationRequest { export interface SqlAssessmentParams { ownerUri: string; - targetType: azdata.sqlAssessment.SqlAssessmentTargetType + targetType: azdata.sqlAssessment.SqlAssessmentTargetType; } export interface GenerateSqlAssessmentScriptParams { @@ -1016,10 +1016,11 @@ export namespace ProfilerSessionCreatedNotification { /// ------------------------------- ----------------------------- export interface SqlMigrationAssessmentParams { ownerUri: string; + databases: string[]; } export namespace GetSqlMigrationAssessmentItemsRequest { - export const type = new RequestType('migration/getassessments'); + export const type = new RequestType('migration/getassessments'); } // ------------------------------- ----------------------------- diff --git a/extensions/mssql/src/mssql.d.ts b/extensions/mssql/src/mssql.d.ts index 8a3a082868..12bd0c683a 100644 --- a/extensions/mssql/src/mssql.d.ts +++ b/extensions/mssql/src/mssql.d.ts @@ -599,5 +599,5 @@ export interface AssessmentResult { } export interface ISqlMigrationService { - getAssessments(ownerUri: string): Promise; + getAssessments(ownerUri: string, databases: string[]): Promise; } diff --git a/extensions/mssql/src/sqlMigration/sqlMigrationService.ts b/extensions/mssql/src/sqlMigration/sqlMigrationService.ts index fb2ffce155..294a21a1a3 100644 --- a/extensions/mssql/src/sqlMigration/sqlMigrationService.ts +++ b/extensions/mssql/src/sqlMigration/sqlMigrationService.ts @@ -29,8 +29,8 @@ export class SqlMigrationService implements mssql.ISqlMigrationService { context.registerService(constants.SqlMigrationService, this); } - async getAssessments(ownerUri: string): Promise { - let params: contracts.SqlMigrationAssessmentParams = { ownerUri: ownerUri }; + async getAssessments(ownerUri: string, databases: string[]): Promise { + let params: contracts.SqlMigrationAssessmentParams = { ownerUri: ownerUri, databases: databases }; try { return this.client.sendRequest(contracts.GetSqlMigrationAssessmentItemsRequest.type, params); } diff --git a/extensions/sql-migration/package.json b/extensions/sql-migration/package.json index 3fe7eb809a..5789294172 100644 --- a/extensions/sql-migration/package.json +++ b/extensions/sql-migration/package.json @@ -2,7 +2,7 @@ "name": "sql-migration", "displayName": "%displayName%", "description": "%description%", - "version": "0.1.3", + "version": "0.1.4", "publisher": "Microsoft", "preview": true, "license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/main/LICENSE.txt", diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 87b19e13f4..2d210dc5e3 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -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 SERVER = localize('sql.migration.server', "Server"); 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 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 { 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 { return localize('sql.migration.issues.count', "Issues ({0})", totalCount); } diff --git a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts index 822443c472..dd9984a9c3 100644 --- a/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts +++ b/extensions/sql-migration/src/dialog/assessmentResults/sqlDatabasesTree.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../../../mssql/src/mssql'; -import { IconPath, IconPathHelper } from '../../constants/iconPathHelper'; import { MigrationStateModel, MigrationTargetType } from '../../models/stateMachine'; import * as constants from '../../constants/strings'; import { debounce } from '../../api/utils'; @@ -128,7 +127,7 @@ export class SqlDatabaseTree { 'font-weight': 'bold', 'margin': '0px 8px 0px 36px' }, - value: constants.DATABASES(this.selectedDbs.length, this._model._serverDatabases.length) + value: constants.DATABASES(0, this._model._databaseAssessment.length) }).component(); return this._databaseCount; } @@ -146,22 +145,22 @@ export class SqlDatabaseTree { { displayName: '', valueType: azdata.DeclarativeDataType.boolean, - width: 20, + width: 10, isReadOnly: false, showCheckAll: true, headerCssStyles: headerLeft, }, { displayName: constants.DATABASE, - valueType: azdata.DeclarativeDataType.component, - width: 100, + valueType: azdata.DeclarativeDataType.string, + width: 95, isReadOnly: true, headerCssStyles: headerLeft }, { displayName: constants.ISSUES, valueType: azdata.DeclarativeDataType.string, - width: 30, + width: 45, isReadOnly: true, headerCssStyles: headerRight, } @@ -170,7 +169,7 @@ export class SqlDatabaseTree { ).component(); this._databaseTable.onDataChanged(() => { 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) => { @@ -224,8 +223,7 @@ export class SqlDatabaseTree { 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 textComponent: azdata.TextComponent = row[1] as azdata.TextComponent; const cellText = textComponent.value?.toLowerCase(); const searchText: string = value.toLowerCase(); if (cellText?.includes(searchText)) { @@ -242,20 +240,23 @@ export class SqlDatabaseTree { private createInstanceComponent(): azdata.DivContainer { this._instanceTable = this._view.modelBuilder.declarativeTable().withProps( { + CSSStyles: { + 'table-layout': 'fixed' + }, + width: 200, enableRowSelection: true, - width: 170, columns: [ { displayName: constants.INSTANCE, - valueType: azdata.DeclarativeDataType.component, - width: 130, + valueType: azdata.DeclarativeDataType.string, + width: 105, isReadOnly: true, headerCssStyles: headerLeft }, { displayName: constants.WARNINGS, valueType: azdata.DeclarativeDataType.string, - width: 30, + width: 45, isReadOnly: true, headerCssStyles: headerRight } @@ -264,6 +265,7 @@ export class SqlDatabaseTree { const instanceContainer = this._view.modelBuilder.divContainer().withItems([this._instanceTable]).withProps({ CSSStyles: { + 'width': '200px', 'margin': '19px 8px 0px 34px' } }).component(); @@ -279,7 +281,7 @@ export class SqlDatabaseTree { }); this._recommendation.value = constants.WARNINGS_DETAILS; this._recommendationTitle.value = constants.WARNINGS_COUNT(this._activeIssues.length); - if (this._model._targetType === MigrationTargetType.SQLMI) { + if (this._targetType === MigrationTargetType.SQLMI) { await this.refreshResults(); } }); @@ -362,7 +364,7 @@ export class SqlDatabaseTree { private createNoIssuesText(): azdata.FlexContainer { let message: azdata.TextComponent; - if (this._model._targetType === MigrationTargetType.SQLVM) { + if (this._targetType === MigrationTargetType.SQLVM) { message = this._view.modelBuilder.text().withProps({ value: constants.NO_ISSUES_FOUND_VM, CSSStyles: { @@ -740,7 +742,7 @@ export class SqlDatabaseTree { } public async refreshResults(): Promise { - if (this._model._targetType === MigrationTargetType.SQLMI) { + if (this._targetType === MigrationTargetType.SQLMI) { if (this._activeIssues.length === 0) { /// show no issues here this._assessmentsTable.updateCssStyles({ @@ -810,8 +812,7 @@ export class SqlDatabaseTree { public async initialize(): Promise { let instanceTableValues: azdata.DeclarativeTableCellValue[][] = []; this._databaseTableValues = []; - const excludedDatabases = ['master', 'msdb', 'tempdb', 'model']; - this._dbNames = (await azdata.connection.listDatabases(this._model.sourceConnectionId)).filter(db => !excludedDatabases.includes(db)); + this._dbNames = this._model._databaseAssessment; const selectedDbs = (this._targetType === MigrationTargetType.SQLVM) ? this._model._vmDbs : this._model._miDbs; this._serverName = (await this._model.getSourceConnectionProfile()).serverName; @@ -819,7 +820,8 @@ export class SqlDatabaseTree { instanceTableValues = [ [ { - value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName), + // value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName), + value: this._serverName, style: styleLeft }, { @@ -836,7 +838,8 @@ export class SqlDatabaseTree { style: styleLeft }, { - value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, db), + // value: this.createIconTextCell(IconPathHelper.sqlDatabaseLogo, db), + value: db, style: styleLeft }, { @@ -850,7 +853,8 @@ export class SqlDatabaseTree { instanceTableValues = [ [ { - value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName), + // value: this.createIconTextCell(IconPathHelper.sqlServerLogo, this._serverName), + value: this._serverName, style: styleLeft }, { @@ -877,7 +881,8 @@ export class SqlDatabaseTree { 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 }, { @@ -892,41 +897,4 @@ export class SqlDatabaseTree { 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; - } } diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 186fcfeac8..2ef7b0a03b 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -102,6 +102,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { public _authenticationType!: MigrationSourceAuthenticationType; public _sqlServerUsername!: string; public _sqlServerPassword!: string; + public _databaseAssessment!: string[]; public _subscriptions!: azureResource.AzureResourceSubscription[]; @@ -120,7 +121,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { public _blobContainers!: azureResource.BlobContainer[]; public _refreshNetworkShareLocation!: azureResource.BlobContainer[]; public _targetDatabaseNames!: string[]; - public _serverDatabases!: string[]; public _sqlMigrationServiceResourceGroup!: string; public _sqlMigrationService!: SqlMigrationService; @@ -138,6 +138,13 @@ export class MigrationStateModel implements Model, vscode.Disposable { public _targetType!: MigrationTargetType; public refreshDatabaseBackupPage!: boolean; + public excludeDbs: string[] = [ + 'master', + 'tempdb', + 'msdb', + 'model' + ]; + constructor( private readonly _extensionContext: vscode.ExtensionContext, private readonly _sourceConnectionId: string, @@ -162,28 +169,25 @@ export class MigrationStateModel implements Model, vscode.Disposable { this._currentState = newState; this._stateChangeEventEmitter.fire({ oldState, newState: this.currentState }); } + public async getDatabases(): Promise { + let temp = await azdata.connection.listDatabases(this.sourceConnectionId); + let finalResult = temp.filter((name) => !this.excludeDbs.includes(name)); + return finalResult; + } - public async getServerAssessments(): Promise { - const excludeDbs: string[] = [ - 'master', - 'tempdb', - 'msdb', - 'model' - ]; - + public async getDatabaseAssessments(): Promise { const ownerUri = await azdata.connection.getUriForConnection(this.sourceConnectionId); - + // stress test backend & dialog component 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 => !excludeDbs.includes(d.name)).map(d => { + const dbAssessments = assessmentResults?.assessmentResult.databases.filter(d => !this.excludeDbs.includes(d.name)).map(d => { return { name: d.name, issues: d.items.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? [] }; }); - this._assessmentResults = { issues: assessmentResults?.assessmentResult.items?.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLMI) ?? [], databaseAssessments: dbAssessments! ?? [] @@ -192,7 +196,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { 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; } diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts new file mode 100644 index 0000000000..f1587efa52 --- /dev/null +++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts @@ -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 { + 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 { + this.wizard.registerNavigationValidator((pageChangeInfo) => { + return true; + }); + } + public async onPageLeave(): Promise { + this.migrationStateModel._databaseAssessment = this.selectedDbs(); + this.wizard.registerNavigationValidator((pageChangeInfo) => { + return true; + }); + } + + protected async handleStateChange(e: StateChangeEvent): Promise { + } + + + 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 { + const providerId = (await this.migrationStateModel.getSourceConnectionProfile()).providerId; + const metaDataService = azdata.dataprotocol.getProvider(providerId, azdata.DataProviderType.MetadataProvider); + const ownerUri = await azdata.connection.getUriForConnection(this.migrationStateModel.sourceConnectionId); + const results = 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; + } + +} diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 113fe428c7..2ba1be0339 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -403,13 +403,13 @@ export class SKURecommendationPage extends MigrationWizardPage { private changeTargetType(newTargetType: string) { if (newTargetType === MigrationTargetType.SQLMI) { 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._azureSubscriptionText.value = constants.SELECT_AZURE_MI; this.migrationStateModel._migrationDbs = this.migrationStateModel._miDbs; } else { 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._azureSubscriptionText.value = constants.SELECT_AZURE_VM; this.migrationStateModel._migrationDbs = this.migrationStateModel._vmDbs; @@ -424,7 +424,7 @@ export class SKURecommendationPage extends MigrationWizardPage { const serverName = (await this.migrationStateModel.getSourceConnectionProfile()).serverName; this._igComponent.value = constants.ASSESSMENT_COMPLETED(serverName); try { - await this.migrationStateModel.getServerAssessments(); + await this.migrationStateModel.getDatabaseAssessments(); this._detailsComponent.value = constants.SKU_RECOMMENDATION_ALL_SUCCESSFUL(this.migrationStateModel._assessmentResults.databaseAssessments.length); } catch (e) { console.log(e); diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts index 8861204790..86071fa0e5 100644 --- a/extensions/sql-migration/src/wizard/wizardController.ts +++ b/extensions/sql-migration/src/wizard/wizardController.ts @@ -15,6 +15,7 @@ import { AccountsSelectionPage } from './accountsSelectionPage'; import { IntergrationRuntimePage } from './integrationRuntimePage'; import { SummaryPage } from './summaryPage'; import { MigrationModePage } from './migrationModePage'; +import { DatabaseSelectorPage } from './databaseSelectorPage'; export const WIZARD_INPUT_COMPONENT_WIDTH = '600px'; export class WizardController { @@ -38,6 +39,7 @@ export class WizardController { wizard.generateScriptButton.hidden = true; const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel); const migrationModePage = new MigrationModePage(wizard, stateModel); + const databaseSelectorPage = new DatabaseSelectorPage(wizard, stateModel); const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel); const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel); const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel); @@ -45,6 +47,7 @@ export class WizardController { const pages: MigrationWizardPage[] = [ azureAccountsPage, + databaseSelectorPage, skuRecommendationPage, migrationModePage, databaseBackupPage,