diff --git a/extensions/sql-migration/src/api/azure.ts b/extensions/sql-migration/src/api/azure.ts index d538420012..37d24b0b8d 100644 --- a/extensions/sql-migration/src/api/azure.ts +++ b/extensions/sql-migration/src/api/azure.ts @@ -867,7 +867,6 @@ export function getMigrationErrors(migration: DatabaseMigration): string { errors.push(migration.properties.migrationStatusWarnings?.completeRestoreErrorMessage); errors.push(migration.properties.migrationStatusWarnings?.restoreBlockingReason); errors.push(...migration.properties.migrationStatusDetails?.listOfCopyProgressDetails?.flatMap(cp => cp.errors) ?? []); - errors.push(...migration.properties.migrationStatusDetails?.sqlSchemaMigrationStatus?.sqlSchemaCopyErrors ?? []); } // remove undefined and duplicate error entries @@ -957,8 +956,6 @@ export interface StartDatabaseMigrationRequest { tableList?: string[], scope: string, offlineConfiguration?: OfflineConfiguration, - sqlSchemaMigrationConfiguration?: SqlSchemaMigrationConfiguration, - sqlDataMigrationConfiguration?: SqlDataMigrationConfiguration } } @@ -1085,8 +1082,6 @@ export interface DatabaseMigrationProperties { migrationOperationId: string; backupConfiguration: BackupConfiguration; offlineConfiguration: OfflineConfiguration; - sqlSchemaMigrationConfiguration: SqlSchemaMigrationConfiguration; - sqlDataMigrationConfiguration: SqlDataMigrationConfiguration; migrationFailureError: ErrorInfo; tableList: string[]; } @@ -1108,7 +1103,6 @@ export interface MigrationStatusDetails { invalidFiles: string[]; listOfCopyProgressDetails: CopyProgressDetail[]; sqlDataCopyErrors: string[]; - sqlSchemaMigrationStatus: SqlSchemaMigrationStatus; // new fields pendingDiffBackupsCount: number; @@ -1148,38 +1142,6 @@ export interface CopyProgressDetail { errors: string[]; } -export interface SqlSchemaMigrationStatus { - sqlSchemaCopyErrors: string[]; - status: 'CollectionCompleted' | 'PrefetchObjects' | 'GetDependency' | 'ScriptObjects' | 'ScriptViewIndexes' | 'ScriptOwnership' | 'GeneratingScript' | 'GeneratingScriptCompleted' | 'DeployingSchema' | 'DeploymentCompleted' | 'Completed' | 'CompletedWithError'; - objectsCollection: ObjectsCollection; - scriptGeneration: ScriptGeneration; - scriptDeployment: ScriptDeployment; -} - -export interface ObjectsCollection { - totalCountOfObjectsCollected: number; - startedOn: string; - endedOn: string; -} - -export interface ScriptGeneration { - progressInPercentage: string; - scriptedObjectsFailedCount: number; - scriptedObjectsCount: number; - startedOn: string; - endedOn: string; - errors: string[]; -} - -export interface ScriptDeployment { - progressInPercentage: string; - failedDeploymentCount: number; - succeededDeploymentCount: number; - startedOn: string; - endedOn: string; - errors: string[]; -} - export interface BackupConfiguration { sourceLocation?: SourceLocation; targetLocation?: TargetLocation; @@ -1190,14 +1152,6 @@ export interface OfflineConfiguration { lastBackupName?: string; } -export interface SqlSchemaMigrationConfiguration { - enableSchemaMigration: boolean; -} - -export interface SqlDataMigrationConfiguration { - enableDataMigration: boolean; -} - export interface ErrorInfo { code: string; message: string; diff --git a/extensions/sql-migration/src/api/sqlUtils.ts b/extensions/sql-migration/src/api/sqlUtils.ts index ec1b2ef063..8676d708ea 100644 --- a/extensions/sql-migration/src/api/sqlUtils.ts +++ b/extensions/sql-migration/src/api/sqlUtils.ts @@ -123,7 +123,6 @@ export interface TargetDatabaseInfo { isReadOnly: boolean; sourceTables: Map; targetTables: Map; - enableSchemaMigration: boolean; } export interface LoginTableInfo { @@ -371,7 +370,6 @@ export async function collectTargetDatabaseInfo( isReadOnly: getSqlBoolean(row[7]), sourceTables: new Map(), targetTables: new Map(), - enableSchemaMigration: false, }; }) ?? []; } diff --git a/extensions/sql-migration/src/api/utils.ts b/extensions/sql-migration/src/api/utils.ts index b2d073b313..ac5a63e6b9 100644 --- a/extensions/sql-migration/src/api/utils.ts +++ b/extensions/sql-migration/src/api/utils.ts @@ -12,7 +12,7 @@ import { azureResource, Tenant } from 'azurecore'; import * as constants from '../constants/strings'; import { logError, TelemetryViews } from '../telemetry'; import { AdsMigrationStatus } from '../dashboard/tabBase'; -import { getMigrationMode, getMigrationStatus, getSchemaMigrationStatus, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes, getMigrationType } from '../constants/helper'; +import { getMigrationMode, getMigrationStatus, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes } from '../constants/helper'; import * as os from 'os'; import * as styles from '../constants/styles'; import { SqlMigrationService, getSqlMigrationServiceAuthKeys, regenerateSqlMigrationServiceAuthKey } from './azure'; @@ -178,7 +178,6 @@ export function filterMigrations(databaseMigrations: azure.DatabaseMigration[], || migration.properties.sourceDatabaseName?.toLowerCase().includes(filter) || getMigrationStatus(migration)?.toLowerCase().includes(filter) || getMigrationMode(migration)?.toLowerCase().includes(filter) - || getMigrationType(migration)?.toLocaleLowerCase().includes(filter) || getMigrationTargetType(migration)?.toLowerCase().includes(filter) || azure.getResourceName(migration.properties.scope)?.toLowerCase().includes(filter) || azure.getResourceName(migration.id)?.toLowerCase().includes(filter) @@ -400,93 +399,6 @@ export function getMigrationStatusImage(migration: azure.DatabaseMigration): Ico } } -export function getSchemaMigrationStatusImage(migration: azure.DatabaseMigration): IconPath { - const status = getSchemaMigrationStatus(migration); - switch (status) { - case constants.MigrationState.CollectionCompleted: - case constants.MigrationState.PrefetchObjects: - case constants.MigrationState.GetDependency: - case constants.MigrationState.ScriptObjects: - case constants.MigrationState.ScriptViewIndexes: - case constants.MigrationState.ScriptOwnership: - case constants.MigrationState.GeneratingScript: - case constants.MigrationState.DeployingSchema: - case constants.MigrationState.GeneratingScriptCompleted: - case constants.MigrationState.DeploymentCompleted: - return IconPathHelper.inProgressMigration; - case constants.MigrationState.Completed: - return IconPathHelper.completedMigration; - case constants.MigrationState.Failed: - case constants.MigrationState.CompletedWithError: - return IconPathHelper.error; - default: - return IconPathHelper.notStartedMigration; - } -} - -export function getObjectsCollectionStatusImage(migration: azure.DatabaseMigration): IconPath { - const status = getSchemaMigrationStatus(migration); - switch (status) { - case constants.MigrationState.CollectionCompleted: - case constants.MigrationState.PrefetchObjects: - case constants.MigrationState.GetDependency: - case constants.MigrationState.ScriptObjects: - case constants.MigrationState.ScriptViewIndexes: - case constants.MigrationState.ScriptOwnership: - case constants.MigrationState.GeneratingScript: - case constants.MigrationState.GeneratingScriptCompleted: - case constants.MigrationState.DeployingSchema: - case constants.MigrationState.DeploymentCompleted: - case constants.MigrationState.Completed: - case constants.MigrationState.CompletedWithError: - return IconPathHelper.completedMigration; - default: - return IconPathHelper.notStartedMigration; - } -} - -export function getScriptGenerationStatusImage(migration: azure.DatabaseMigration): IconPath { - var scriptGeneration = migration?.properties.migrationStatusDetails?.sqlSchemaMigrationStatus?.scriptGeneration ?? undefined; - var errors = scriptGeneration === undefined ? [] : scriptGeneration.errors ?? []; - - const status = getSchemaMigrationStatus(migration); - switch (status) { - case constants.MigrationState.PrefetchObjects: - case constants.MigrationState.GetDependency: - case constants.MigrationState.ScriptObjects: - case constants.MigrationState.ScriptViewIndexes: - case constants.MigrationState.ScriptOwnership: - case constants.MigrationState.GeneratingScript: - return IconPathHelper.inProgressMigration; - case constants.MigrationState.Completed: - case constants.MigrationState.CompletedWithError: - case constants.MigrationState.GeneratingScriptCompleted: - case constants.MigrationState.DeployingSchema: - case constants.MigrationState.DeploymentCompleted: - return errors.length > 0 ? IconPathHelper.error : IconPathHelper.completedMigration; - default: - return IconPathHelper.notStartedMigration; - } -} - -export function getScriptDeploymentStatusImage(migration: azure.DatabaseMigration): IconPath { - var scriptDeployment = migration?.properties.migrationStatusDetails?.sqlSchemaMigrationStatus?.scriptDeployment ?? undefined; - var errors = scriptDeployment === undefined ? [] : scriptDeployment.errors ?? []; - - const status = getSchemaMigrationStatus(migration); - switch (status) { - case constants.MigrationState.DeployingSchema: - return IconPathHelper.inProgressMigration; - case constants.MigrationState.Completed: - case constants.MigrationState.DeploymentCompleted: - return IconPathHelper.completedMigration; - case constants.MigrationState.CompletedWithError: - return errors.length > 0 ? IconPathHelper.error : IconPathHelper.completedMigration; - default: - return IconPathHelper.notStartedMigration; - } -} - export function get12HourTime(date: Date | undefined): string { const localeTimeStringOptions: Intl.DateTimeFormatOptions = { hour: '2-digit', @@ -1257,9 +1169,3 @@ export async function clearDropDown(dropDown: DropDownComponent): Promise await dropDown.updateProperty('value', undefined); await dropDown.updateProperty('values', []); } - -export async function clearDropDownWithLoading(dropDown: DropDownComponent, placeHolder: string | undefined = undefined): Promise { - dropDown.loading = true; - await dropDown.updateProperties({ 'values': [], 'value': undefined, 'placeholder': placeHolder }); - -} diff --git a/extensions/sql-migration/src/constants/helper.ts b/extensions/sql-migration/src/constants/helper.ts index 09a5c209e1..0036e7e9b7 100644 --- a/extensions/sql-migration/src/constants/helper.ts +++ b/extensions/sql-migration/src/constants/helper.ts @@ -205,20 +205,6 @@ export function getMigrationMode(migration: DatabaseMigration | undefined): stri : loc.ONLINE; } -export function getMigrationType(migration: DatabaseMigration | undefined): string { - // If MI or VM migration, the data type is schema + data - var targetType = getMigrationTargetTypeEnum(migration); - if (targetType === MigrationTargetType.SQLMI || targetType === MigrationTargetType.SQLVM) { - return loc.BACKUP_AND_RESTORE; - } - - var enableSchema = migration?.properties?.sqlSchemaMigrationConfiguration?.enableSchemaMigration ?? false; - var enableData = migration?.properties?.sqlDataMigrationConfiguration?.enableDataMigration ?? false; - return enableSchema && enableData - ? loc.SCHEMA_AND_DATA - : enableSchema ? loc.SCHEMA_ONLY : loc.DATA_ONLY -} - export function getMigrationModeEnum(migration: DatabaseMigration | undefined): MigrationMode { return isOfflineMigation(migration) ? MigrationMode.OFFLINE @@ -256,20 +242,11 @@ export function getMigrationStatus(migration: DatabaseMigration | undefined): st ?? migration?.properties.provisioningState; } -export function getSchemaMigrationStatus(migration: DatabaseMigration | undefined): string | undefined { - return migration?.properties?.migrationStatusDetails?.sqlSchemaMigrationStatus?.status; -} - export function getMigrationStatusString(migration: DatabaseMigration | undefined): string { const migrationStatus = getMigrationStatus(migration) ?? DefaultSettingValue; return loc.StatusLookup[migrationStatus] ?? migrationStatus; } -export function getSchemaMigrationStatusString(migration: DatabaseMigration | undefined): string { - const schemaMigrationStatus = getSchemaMigrationStatus(migration) ?? DefaultSettingValue; - return loc.SchemaMigrationStatusLookup[schemaMigrationStatus] ?? schemaMigrationStatus; -} - export function hasMigrationOperationId(migration: DatabaseMigration | undefined): boolean { const migrationId = migration?.id ?? ''; const migationOperationId = migration?.properties?.migrationOperationId ?? ''; diff --git a/extensions/sql-migration/src/constants/strings.ts b/extensions/sql-migration/src/constants/strings.ts index 82d5b5cbac..f0967bc776 100644 --- a/extensions/sql-migration/src/constants/strings.ts +++ b/extensions/sql-migration/src/constants/strings.ts @@ -39,18 +39,6 @@ export enum MigrationState { Succeeded = 'Succeeded', UploadingFullBackup = 'UploadingFullBackup', UploadingLogBackup = 'UploadingLogBackup', - CollectionCompleted = 'CollectionCompleted', - GeneratingScript = 'GeneratingScript', - PrefetchObjects = 'PrefetchObjects', - GetDependency = 'GetDependency', - ScriptObjects = 'ScriptObjects', - ScriptViewIndexes = 'ScriptViewIndexes', - ScriptOwnership = 'ScriptOwnership', - GeneratingScriptCompleted = 'GeneratingScriptCompleted', - DeployingSchema = 'DeployingSchema', - DeploymentCompleted = 'DeploymentCompleted', - Completed = 'Completed', - CompletedWithError = 'CompletedWithError' } export enum ProvisioningState { @@ -559,7 +547,7 @@ export function ACCOUNT_CREDENTIALS_REFRESH(accountName: string): string { "{0} (requires credentials refresh)", accountName); } -export const SELECT_SERVICE_PLACEHOLDER = localize('sql.migration.select.service.select.migration.target', "Select a target server"); +export const SELECT_SERVICE_PLACEHOLDER = localize('sql.migration.select.service.select.migration.target', "Select a target server."); // database backup page export const DATA_SOURCE_CONFIGURATION_PAGE_TITLE = localize('sql.migration.data.source.configuration.page.title', "Data source configuration"); @@ -595,14 +583,13 @@ export const DATABASE_BACKUP_MIGRATION_MODE_LABEL = localize('sql.migration.data export const DATABASE_BACKUP_MIGRATION_MODE_DESCRIPTION = localize('sql.migration.database.migration.mode.description', "To migrate to the Azure SQL target, choose a migration mode based on your downtime requirements."); export const DATABASE_TABLE_SELECTION_LABEL = localize('sql.migration.database.table.selection.label', "Table selection"); export const DATABASE_TABLE_SELECTION_DESCRIPTION = localize('sql.migration.database.table.selection.description', "For each database below, click Edit to select the tables to migrate from source to target. Then, before clicking Next, validate the provided configuration by clicking 'Run validation'."); -export const DATABASE_SCHEMA_MIGRATION_HELP = localize('sql.migration.database.schema.migration.help', "Ensure to migrate the database schema from source to target before starting the migration by using the Database Schema Migration feature (Public Preview) or {0} or the {1} in Azure Data Studio before selecting the list of tables to migrate."); +export const DATABASE_SCHEMA_MIGRATION_HELP = localize('sql.migration.database.schema.migration.help', "Make sure to migrate the database schema from source to target by using the {0} or the {1} in Azure Data Studio before selecting the list of tables to migrate."); export const DATABASE_SCHEMA_MIGRATION_DACPAC_EXTENSION = localize('sql.migration.database.schema.migration.dacpac', "SQL Server dacpac extension"); export const DATABASE_SCHEMA_MIGRATION_PROJECTS_EXTENSION = localize('sql.migration.database.schema.migration.project', "SQL Database Projects extension"); export const DATABASE_TABLE_REFRESH_LABEL = localize('sql.migration.database.table.refresh.label', "Refresh"); export const DATABASE_TABLE_SOURCE_DATABASE_COLUMN_LABEL = localize('sql.migration.database.table.source.column.label', "Source database"); export const DATABASE_TABLE_TARGET_DATABASE_COLUMN_LABEL = localize('sql.migration.database.table.target.column.label', "Target database"); -export const SCHEMA_MIGRATION_COLUMN_LABLE = localize('sql.migration.schema.migration.column.label', "Schema migration"); export const DATABASE_TABLE_SELECTED_TABLES_COLUMN_LABEL = localize('sql.migration.database.table.tables.column.label', "Select tables"); export const DATABASE_TABLE_CONNECTION_ERROR = localize('sql.migration.database.connection.error', "An error occurred while connecting to target migration database."); export function DATABASE_TABLE_CONNECTION_ERROR_MESSAGE(message: string): string { @@ -701,17 +688,14 @@ export const TABLE_SELECTION_EDIT = localize('sql.migration.table.selection.edit export function TABLE_SELECTION_COUNT(selectedCount: number, rowCount: number): string { return localize('sql.migration.table.selection.count', "{0} of {1}", formatNumber(selectedCount), formatNumber(rowCount)); } -export function TABLE_SELECTION_COUNT_TO_TARGET(selectedCount: number, rowCount: number): string { - return localize('sql.migration.table.selection.count.to.target', "{0} of {1} tables selected for migration", formatNumber(selectedCount), formatNumber(rowCount)); +export function TABLE_SELECTED_COUNT(selectedCount: number, rowCount: number): string { + return localize('sql.migration.table.selected.count', "{0} of {1} tables selected", formatNumber(selectedCount), formatNumber(rowCount)); } -export function AVAILABLE_TABLE_COUNT_ON_TARGET(rowCount: number): string { - return localize('sql.migration.available.table.count.on.target', "Available on target ({0})", formatNumber(rowCount)); -} -export function MISSING_TARGET_TABLES_COUNT(missingCount: number): string { - return localize('sql.migration.table.missing.count', "Missing on target ({0})", formatNumber(missingCount)); +export function MISSING_TARGET_TABLES_COUNT(tables: number): string { + return localize('sql.migration.table.missing.count', "Tables missing on target: {0}", formatNumber(tables)); } export const SELECT_TABLES_FOR_MIGRATION = localize('sql.migration.select.migration.tables', "Select tables for migration"); -export const DATABASE_MISSING_TABLES = localize('sql.migration.database.missing.tables', "0 tables on source database found in target database. To migrate the data in the tables select the migrate schema option above."); +export const DATABASE_MISSING_TABLES = localize('sql.migration.database.missing.tables', "0 tables found on source database."); export const DATABASE_LOADING_TABLES = localize('sql.migration.database.loading.tables', "Loading tables list..."); export const TABLE_SELECTION_FILTER = localize('sql.migration.table.selection.filter', "Filter tables"); export const TABLE_SELECTION_UPDATE_BUTTON = localize('sql.migration.table.selection.update.button', "Update"); @@ -934,50 +918,6 @@ export function VALIDATE_IR_SQLDB_VALIDATION_RESULT_ERROR(sourceDatabaseName: st error.message); } -// Schema migration -export const FULL_SCHEMA_MISSING_ON_TARGET = localize('sql.migration.schema.full.missing', "No schema was found on target. This option must be selected to migrate this database."); -export const PARTIAL_SCHEMA_ON_TARGET = localize('sql.migration.schema.partial.missing', "Missing schemas on the target. Some tables are disabled and cannot be migrated unless this option is selected."); -export const FULL_SCHEMA_ON_TARGET = localize('sql.migration.schema.no.missing', "Schema was found on target. Schema migration is not required."); -export const SCHEMA_MIGRATION_INFO = localize('sql.migration.schema.migration.info', "Select this option to migrate missing tables on your Azure SQL target"); -export const DATA_MIGRATION_INFO = localize('sql.migration.data.migration.info', "Select tables to migrate data to your Azure SQL target"); -export const SCHEMA_MIGRATION_HEADER = localize('sql.migration.schema.migration.header', "Schema migration"); -export const DATA_MIGRATION_HEADER = localize('sql.migration.data.migration.header', "Data migration"); -export const SCHEMA_MIGRATION_CHECKBOX_INFO = localize('sql.migration.schema.migration.checkbox.info', "Migrate schema to target"); -export const SCHEMA_ONLY = localize('sql.migration.schema.only', "Schema only"); -export const DATA_ONLY = localize('sql.migration.data.only', "Data only"); -export const SCHEMA_AND_DATA = localize('sql.migration.schema.data', "Schema and data"); -export const BACKUP_AND_RESTORE = localize('sql.migration.backup.restore', "Backup and restore"); -export const SCHEMA_MIGRATION_STATUS = localize('sql.migration.schema.status', "Schema migration status"); -export const OBJECTS_COLLECTED = localize('sql.migration.schema.objects.collection', "Objects collected"); -export const COLLECTION_STARTED = localize('sql.migration.schema.collection.started', "Collection started"); -export const COLLECTION_ENDED = localize('sql.migration.schema.collection.ended', "Collection ended"); -export const SCRIPT_GENERATION = localize('sql.migration.schema.script.generation', "Script generation"); -export const SCRIPTING_STARTED = localize('sql.migration.schema.script.started', "Scripting started"); -export const SCRIPTING_ENDED = localize('sql.migration.schema.script.ended', "Scripting ended"); -export const SCRIPTED_OBJECTS_COUNT = localize('sql.migration.schema.script.count', "Scripted objects count"); -export const SCRIPTING_ERROR_COUNT = localize('sql.migration.schema.script.fail.count', "Scripting error count"); -export const MIGRATION_TYPE = localize('sql.migration.schema.type', "Migration type"); -export const SCRIPT_DEPLOYMENT = localize('sql.migration.schema.script.deployment', "Script deployment"); -export const DEPLOYMENT_STARTED = localize('sql.migration.schema.script.deployment.started', "Deployment started"); -export const DEPLOYMENT_ENDED = localize('sql.migration.schema.script.deployment.ended', "Deployment ended"); -export const DEPLOYMENT_COUNT = localize('sql.migration.schema.script.deployment.count', "Deployment count"); -export const DEPLOYMENT_ERROR_COUNT = localize('sql.migration.schema.script.deployment.error.count', "Deployment error count"); -export const SCHEMA_MIGRATION_ASSESSMENT_WARNING_MESSAGE = localize('sql.migration.schema.assessment.warning.message', "The detected issues shown below might fail the schema migration. Some of them might be entirely unsupported and the others might be partially supported in Azure SQL Database. \nTherefore, please review the assessment results and make sure all of the issues will not fail the schema migration.\nHowever, it is allowed to proceed the schema migration and DMS will migrate the objects as possible as it can."); - -export const SchemaMigrationFailedRulesLookup: LookupTable = { - ["ComputeClause"]: localize('sql.migration.schema.rule.compute', 'COMPUTE'), - ["CrossDatabaseReferences"]: localize('sql.migration.schema.rule.crossdatabasereferences', 'CROSS DATABASE REFERENCE'), - ["FileStream"]: localize('sql.migration.schema.filestream', 'FILESTREAM'), - ["OpenRowsetWithNonBlobDataSourceBulk"]: localize('sql.migration.schema.openrowset.nonazureblob', 'OPENROWSET WITH NON-AZURE BLOB'), - ["OpenRowsetWithSQLAndNonSQLProvider"]: localize('sql.migration.schema.openrowset.provider', 'OPENROWSET WITH PROVIDER'), - ["BulkInsert"]: localize('sql.migration.schema.bulkinsert', 'BULK INSERT'), - ["CryptographicProvider"]: localize('sql.migration.schema.cryptographicprovider', 'CRYPTOGRAPHIC PROVIDER'), - ["MSDTCTransactSQL"]: localize('sql.migration.schema.msdtctransactsql', 'BEGIN DISTRIBUTED TRANSACTION'), - ["DisableDefCNSTCHK"]: localize('sql.migration.schema.disabledefcnstchk', 'DISABLE_DEF_CNST_CHK'), - ["FastFirstRowHint"]: localize('sql.migration.schema.fastfirstrow', 'FASTFIRSTROW'), - default: undefined -}; - // common strings export const WARNING = localize('sql.migration.warning', "Warning"); export const ERROR = localize('sql.migration.error', "Error"); @@ -1236,12 +1176,10 @@ export const AZURE_SQL_TARGET_TOOL_TIP = localize('sql.migration.database.migrat export const TARGET_SERVER_TOOL_TIP = localize('sql.migration.database.migration.target.instance.server.name.tool.tip', "The target server name"); export const TARGET_DATABASE_TOOL_TIP = localize('sql.migration.database.migration.target.instance.database.name.tool.tip', "The target database name"); export const MIGRATION_MODE_TOOL_TIP = localize('sql.migration.database.migration.migration.mode.tool.tip', "In Azure Database Migration Service, you can migrate your databases offline or while they are online. In an offline migration, application downtime starts when the migration starts. To limit downtime to the time it takes you to cut over to the new environment after the migration, use an online migration."); -export const MIGRATION_TYPE_TOOL_TIP = localize('sql.migration.database.migration.migration.type.tool.tip', "Migration type includes: Schema only migration, Data only migration, Schema and data migration."); export const START_TIME_TOOL_TIP = localize('sql.migration.database.migration.start.time.tool.tip', "The start time for the migration"); export const FINISH_TIME_TOOL_TIP = localize('sql.migration.database.migration.finish.time.tool.tip', "The fininish time for the migration"); export const CONTEXT_MENU_TOOL_TIP = localize('sql.migration.database.migration.context.menu.tool.tip', "Click this column to activate the context command menu"); - export function STATUS_VALUE(status: string): string { return localize('sql.migration.status.value', "{0}", StatusLookup[status] ?? status); } @@ -1339,22 +1277,6 @@ export const InternalManagedDatabaseRestoreDetailsStatusLookup: LookupTable = { - [MigrationState.CollectionCompleted]: localize('sql.migration.status.collectioncompleted', 'Objects collection completed'), - [MigrationState.PrefetchObjects]: localize('sql.migration.status.prefetchobjects', 'Prefetch objects'), - [MigrationState.GetDependency]: localize('sql.migration.status.getdependency', 'Get dependency'), - [MigrationState.ScriptObjects]: localize('sql.migration.status.scriptobjects', 'Scripting objects'), - [MigrationState.ScriptViewIndexes]: localize('sql.migration.status.scriptindexes', 'Scripting indexes'), - [MigrationState.ScriptOwnership]: localize('sql.migration.status.scriptownership', 'Scripting ownerships'), - [MigrationState.GeneratingScript]: localize('sql.migration.status.generatingscript', 'Generating script'), - [MigrationState.GeneratingScriptCompleted]: localize('sql.migration.status.generatingcompleted', 'Script generation completed'), - [MigrationState.DeployingSchema]: localize('sql.migration.status.deployingschema', 'Deploying script'), - [MigrationState.DeploymentCompleted]: localize('sql.migration.status.deploymentcompleted', 'Deployment completed'), - [MigrationState.Completed]: localize('sql.migration.status.completed', 'Completed'), - [MigrationState.CompletedWithError]: localize('sql.migration.status.completedwitherrors', 'Completed with errors'), - default: undefined -}; - export function STATUS_WARNING_COUNT(status: string, count: number): string | undefined { if (status === MigrationState.InProgress || status === MigrationState.ReadyForCutover || diff --git a/extensions/sql-migration/src/dashboard/migrationDetailsTableTab.ts b/extensions/sql-migration/src/dashboard/migrationDetailsTableTab.ts index 4577980f84..53332f7e3d 100644 --- a/extensions/sql-migration/src/dashboard/migrationDetailsTableTab.ts +++ b/extensions/sql-migration/src/dashboard/migrationDetailsTableTab.ts @@ -6,16 +6,15 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import * as loc from '../constants/strings'; -import { getMigrationStatusImage, getObjectsCollectionStatusImage, getPipelineStatusImage, getSchemaMigrationStatusImage, getScriptDeploymentStatusImage, getScriptGenerationStatusImage } from '../api/utils'; +import { getMigrationStatusImage, getPipelineStatusImage } from '../api/utils'; import { logError, TelemetryViews } from '../telemetry'; -import { canCancelMigration, canCutoverMigration, canDeleteMigration, canRestartMigrationWizard, canRetryMigration, formatDateTimeString, formatNumber, formatSecondsIntoReadableTime, formatSizeBytes, formatSizeKb, getMigrationStatusString, getMigrationTargetTypeEnum, getSchemaMigrationStatusString, isOfflineMigation, PipelineStatusCodes } from '../constants/helper'; +import { canCancelMigration, canCutoverMigration, canDeleteMigration, canRestartMigrationWizard, canRetryMigration, formatDateTimeString, formatNumber, formatSecondsIntoReadableTime, formatSizeBytes, formatSizeKb, getMigrationStatusString, getMigrationTargetTypeEnum, isOfflineMigation, PipelineStatusCodes } from '../constants/helper'; import { CopyProgressDetail, getResourceName } from '../api/azure'; import { InfoFieldSchema, MigrationDetailsTabBase, MigrationTargetTypeName } from './migrationDetailsTabBase'; import { IconPathHelper } from '../constants/iconPathHelper'; import { EOL } from 'os'; import { DashboardStatusBar } from './DashboardStatusBar'; import { EmptySettingValue } from './tabBase'; -import * as utils from '../api/utils'; const MigrationDetailsTableTabId = 'MigrationDetailsTableTab'; @@ -48,23 +47,7 @@ export class MigrationDetailsTableTab extends MigrationDetailsTabBase { - const migration = this.model?.migration; - const isSchemaMigration = migration.properties.sqlSchemaMigrationConfiguration.enableSchemaMigration; - - await utils.updateControlDisplay(this._schemaMigrationStatusInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._objectsCollectionCountInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._objectsCollectionStartedInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._objectsCollectionEndedInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._scriptGenerationProgressInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._scriptGenerationStartedInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._scriptGenerationEndedInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._succeededScriptCountInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._failedScriptCountInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._scriptDeploymentProgressInfoField.flexContainer, isSchemaMigration, 'inline-flex'); - await utils.updateControlDisplay(this._scriptDeploymentStartedField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._scriptDeploymentEndedInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._succeededDeploymentCountInfoField.flexContainer, isSchemaMigration); - await utils.updateControlDisplay(this._failedDeploymentCountInfoField.flexContainer, isSchemaMigration); - } - private async _loadData(): Promise { const migration = this.model?.migration; await this.showMigrationErrors(this.model?.migration); @@ -353,10 +285,6 @@ export class MigrationDetailsTableTab extends MigrationDetailsTabBase = {}; await this._populateTableData(hashSet); @@ -389,36 +317,12 @@ export class MigrationDetailsTableTab extends MigrationDetailsTabBase { { name: TableColumns.sourceServer, displayName: loc.SRC_SERVER }, { name: TableColumns.status, displayName: loc.STATUS_COLUMN }, { name: TableColumns.mode, displayName: loc.MIGRATION_MODE }, - { name: TableColumns.type, displayName: loc.MIGRATION_TYPE }, { name: TableColumns.targetType, displayName: loc.AZURE_SQL_TARGET }, { name: TableColumns.targetDatabse, displayName: loc.TARGET_DATABASE_COLUMN }, { name: TableColumns.targetServer, displayName: loc.TARGET_SERVER_COLUMN }, @@ -348,13 +346,6 @@ export class MigrationsListTab extends TabBase { getMigrationMode(m2), sortDir)); return; - case TableColumns.type: - migrations.sort( - (m1, m2) => this.stringCompare( - getMigrationType(m1), - getMigrationType(m2), - sortDir)); - return; case TableColumns.targetType: migrations.sort( (m1, m2) => this.stringCompare( @@ -436,7 +427,6 @@ export class MigrationsListTab extends TabBase { role: 'button' }, // statue getMigrationMode(migration), // mode - getMigrationType(migration), // type getMigrationTargetType(migration), // targetType getResourceName(migration.id), // targetDatabase getResourceName(migration.properties.scope), // targetServer @@ -515,15 +505,6 @@ export class MigrationsListTab extends TabBase { type: azdata.ColumnType.text, toolTip: loc.MIGRATION_MODE_TOOL_TIP, }, - { - cssClass: rowCssStyles, - headerCssClass: headerCssStyles, - name: loc.MIGRATION_TYPE, - value: 'type', - width: 120, - type: azdata.ColumnType.text, - toolTip: loc.MIGRATION_TYPE_TOOL_TIP, - }, { cssClass: rowCssStyles, headerCssClass: headerCssStyles, diff --git a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts index 1dc752cce2..6fa013fdbb 100644 --- a/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts +++ b/extensions/sql-migration/src/dashboard/sqlServerDashboard.ts @@ -22,7 +22,7 @@ import { MigrationStateModel, SavedInfo } from '../models/stateMachine'; import { logError, TelemetryViews } from '../telemetry'; import { WizardController } from '../wizard/wizardController'; import { DashboardStatusBar, ErrorEvent } from './DashboardStatusBar'; -import { DashboardTab, DashboardTabId } from './dashboardTab'; +import { DashboardTab } from './dashboardTab'; import { MigrationsTab, MigrationsTabId } from './migrationsTab'; import { AdsMigrationStatus, MigrationDetailsEvent, ServiceContextChangeEvent } from './tabBase'; import { migrationServiceProvider } from '../service/provider'; @@ -137,10 +137,9 @@ export class DashboardWidget { disposables.push( tabs.onTabChanged(async tabId => { await this.clearError(await getSourceConnectionId()); - if (tabId === MigrationsTabId) { + if (tabId === MigrationsTabId && !migrationsTabInitialized) { + migrationsTabInitialized = true; await this._migrationsTab.refresh(); - } else if (tabId === DashboardTabId) { - await dashboardTab.refresh(); } })); diff --git a/extensions/sql-migration/src/dialog/restartMigration/restartMigrationDialog.ts b/extensions/sql-migration/src/dialog/restartMigration/restartMigrationDialog.ts index b792af2294..2ab03461fc 100644 --- a/extensions/sql-migration/src/dialog/restartMigration/restartMigrationDialog.ts +++ b/extensions/sql-migration/src/dialog/restartMigration/restartMigrationDialog.ts @@ -75,8 +75,7 @@ export class RestartMigrationDialog { // Integration Runtime sqlMigrationService: serviceContext.migrationService, serviceSubscription: null, - serviceResourceGroup: null, - xEventsFilesFolderPath: null + serviceResourceGroup: null }; const getStorageAccountResourceGroup = (storageAccountResourceId: string): azureResource.AzureResourceResourceGroup => { diff --git a/extensions/sql-migration/src/dialog/tableMigrationSelection/schemaMigrationAssessmentDialog.ts b/extensions/sql-migration/src/dialog/tableMigrationSelection/schemaMigrationAssessmentDialog.ts deleted file mode 100644 index 6de45ba6e1..0000000000 --- a/extensions/sql-migration/src/dialog/tableMigrationSelection/schemaMigrationAssessmentDialog.ts +++ /dev/null @@ -1,548 +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 * as constants from '../../constants/strings'; -import * as styles from '../../constants/styles'; -import { MigrationStateModel } from '../../models/stateMachine'; -import { IconPathHelper } from '../../constants/iconPathHelper'; -import { MigrationTargetType } from '../../api/utils'; -import { SqlMigrationAssessmentResultItem, SqlMigrationImpactedObjectInfo } from '../../service/contracts'; - - -const DialogName = 'SchemaMigrationAssessment'; -const headerLeft: azdata.CssStyles = { - 'border': 'none', - 'text-align': 'left', - 'white-space': 'nowrap', - 'text-overflow': 'ellipsis', - 'overflow': 'hidden', - 'border-bottom': '1px solid' -}; - -export class SchemaMigrationAssessmentDialog { - private _dialog: azdata.window.Dialog | undefined; - private _view!: azdata.ModelView; - private _disposables: vscode.Disposable[] = []; - private _isOpen: boolean = false; - private _model: MigrationStateModel; - private _sourceDatabaseName: string; - private _assessmentsTable!: azdata.FlexContainer; - private _assessmentContainer!: azdata.FlexContainer; - private _assessmentTitle!: azdata.TextComponent; - private _descriptionText!: azdata.TextComponent; - private _impactedObjectsTable!: azdata.DeclarativeTableComponent; - private _impactedObjects!: SqlMigrationImpactedObjectInfo[]; - private _objectDetailsType!: azdata.TextComponent; - private _objectDetailsName!: azdata.TextComponent; - private _objectDetailsSample!: azdata.TextComponent; - private _recommendationText!: azdata.TextComponent; - private _recommendationTitle!: azdata.TextComponent; - private _moreInfoTitle!: azdata.TextComponent; - private _moreInfoText!: azdata.HyperlinkComponent; - private _recommendation!: azdata.TextComponent; - private _dbName!: azdata.TextComponent; - private _assessmentResultsList!: azdata.ListViewComponent; - private _activeIssues!: SqlMigrationAssessmentResultItem[]; - private _resultComponent!: azdata.Component; - private _rootContainer!: azdata.FlexContainer; - private _onSaveCallback: () => Promise; - - constructor( - model: MigrationStateModel, - sourceDatabaseName: string, - onSaveCallback: () => Promise - ) { - this._model = model; - this._sourceDatabaseName = sourceDatabaseName; - this._onSaveCallback = onSaveCallback; - } - - private async _initializeDialog(dialog: azdata.window.Dialog): Promise { - dialog.registerContent(async (view: azdata.ModelView) => { - dialog.message = { - level: azdata.window.MessageLevel.Warning, - text: constants.SCHEMA_MIGRATION_ASSESSMENT_WARNING_MESSAGE, - }; - dialog.okButton.hidden = true; - dialog.cancelButton.label = "Close"; - dialog.cancelButton.position = 'left'; - this._disposables.push( - dialog.cancelButton.onClick( - async () => { await this._onSaveCallback(); this._isOpen = false; })); - const flex = view.modelBuilder.flexContainer().withLayout({ - flexFlow: 'row', - height: '100%', - width: '100%' - }).component(); - flex.addItem(await this._createRootContainer(view), { flex: '1 1 auto' }); - - this._disposables.push(view.onClosed(e => { - this._disposables.forEach( - d => { try { d.dispose(); } catch { } }); - })); - - await view.initializeModel(flex); - await this._loadData(); - }); - } - - public async openDialog(dialogTitle: string) { - if (!this._isOpen) { - this._isOpen = true; - this._dialog = azdata.window.createModelViewDialog( - dialogTitle, - DialogName, - 600, undefined, undefined, false); - - const promise = this._initializeDialog(this._dialog); - azdata.window.openDialog(this._dialog); - await promise; - } - } - - private async _loadData(): Promise { - this._activeIssues = this._model._assessmentResults?.databaseAssessments - .find(r => r.name === this._sourceDatabaseName)?.issues - .filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLDB && constants.SchemaMigrationFailedRulesLookup[i.ruleId] !== undefined) ?? []; - this._dbName.value = this._sourceDatabaseName; - this._recommendationTitle.value = constants.ISSUES_COUNT(this._activeIssues?.length); - this._recommendation.value = constants.ISSUES_DETAILS; - await this._resultComponent.updateCssStyles({ - 'display': 'block' - }); - - await this.refreshResults(); - } - - private async _createRootContainer(view: azdata.ModelView): Promise { - this._view = view; - - this._resultComponent = await this.createComponentResult(view); - this._rootContainer = view.modelBuilder.flexContainer().withLayout({ - flexFlow: 'row', - height: '100%', - width: '100%' - }).component(); - this._rootContainer.addItem(this._resultComponent, { flex: '0 0 auto' }); - - this._disposables.push(this._view.onClosed(e => { - this._disposables.forEach( - d => { try { d.dispose(); } catch { } }); - })); - - return this._rootContainer; - } - - private async refreshResults(): Promise { - if (this._activeIssues?.length === 0) { - /// show no issues here - await this._assessmentsTable.updateCssStyles({ 'display': 'none', 'border-right': 'none' }); - await this._assessmentContainer.updateCssStyles({ 'display': 'none' }); - } else { - await this._assessmentContainer.updateCssStyles({ 'display': 'flex' }); - await this._assessmentsTable.updateCssStyles({ 'display': 'flex', 'border-right': 'solid 1px' }); - } - let assessmentResults: azdata.ListViewOption[] = this._activeIssues - .map((v, index) => { - return { - id: index.toString(), - label: constants.SchemaMigrationFailedRulesLookup[v.ruleId] ?? '', - icon: v.databaseRestoreFails ? IconPathHelper.error : undefined, - ariaLabel: v.databaseRestoreFails ? constants.BLOCKING_ISSUE_ARIA_LABEL(v.checkId) : v.checkId, - }; - }); - - this._assessmentResultsList.options = assessmentResults; - if (this._assessmentResultsList.options.length) { - this._assessmentResultsList.selectedOptionId = '0'; - } - } - - private async createComponentResult(view: azdata.ModelView): Promise { - this._view = view; - const topContainer = this.createTopContainer(); - const bottomContainer = this.createBottomContainer(); - - const container = this._view.modelBuilder.flexContainer().withLayout({ - flexFlow: 'column', - height: '100%' - }).withProps({ - CSSStyles: { - 'margin': '32px 0px 0px 18px', - 'overflow-y': 'hidden', - 'display': 'none' - } - }).component(); - - container.addItem(topContainer, { flex: '0 0 auto' }); - container.addItem(bottomContainer, { flex: '1 1 auto', CSSStyles: { 'overflow-y': 'hidden' } }); - - return container; - } - - private createTopContainer(): azdata.FlexContainer { - const title = this.createTitleComponent(); - const impact = this.createPlatformComponent(); - const recommendation = this.createRecommendationComponent(); - const assessmentResultsTitle = this.createAssessmentResultsTitle(); - const assessmentDetailsTitle = this.createAssessmentDetailsTitle(); - - const titleContainer = this._view.modelBuilder.flexContainer().withItems([ - ]).withProps({ - CSSStyles: { - 'border-bottom': 'solid 1px', - 'width': '800px' - } - }).component(); - - titleContainer.addItem(assessmentResultsTitle, { - flex: '0 0 auto' - }); - - titleContainer.addItem(assessmentDetailsTitle, { - flex: '0 0 auto' - }); - - const container = this._view.modelBuilder.flexContainer().withItems([title, impact, recommendation, titleContainer]).withLayout({ - flexFlow: 'column' - }).component(); - - return container; - } - - private createBottomContainer(): azdata.FlexContainer { - - this._assessmentsTable = this.createImpactedObjectsTable(); - this._assessmentContainer = this.createAssessmentContainer(); - - const container = this._view.modelBuilder.flexContainer().withLayout({ - flexFlow: 'row', - height: '100%' - }).withProps({ - CSSStyles: { - 'height': '100%' - } - }).component(); - - container.addItem(this._assessmentsTable, { flex: '1 1 auto', CSSStyles: { 'overflow-y': 'auto' } }); - container.addItem(this._assessmentContainer, { flex: '1 1 auto', CSSStyles: { 'overflow-y': 'auto' } }); - return container; - } - - private createAssessmentContainer(): azdata.FlexContainer { - const title = this.createAssessmentTitle(); - const bottomContainer = this.createDescriptionContainer(); - const container = this._view.modelBuilder.flexContainer() - .withItems([title, bottomContainer]) - .withLayout({ flexFlow: 'column' }) - .withProps({ CSSStyles: { 'margin-left': '24px' } }) - .component(); - - return container; - } - - private createAssessmentTitle(): azdata.TextComponent { - this._assessmentTitle = this._view.modelBuilder.text() - .withProps({ - value: '', - CSSStyles: { - ...styles.LABEL_CSS, - 'margin-top': '12px', - 'height': '48px', - 'width': '540px', - 'border-bottom': 'solid 1px' - } - }).component(); - - return this._assessmentTitle; - } - - private createDescriptionContainer(): azdata.FlexContainer { - const description = this.createDescription(); - const impactedObjects = this.createImpactedObjectsDescription(); - const container = this._view.modelBuilder.flexContainer() - .withLayout({ flexFlow: 'row' }) - .withProps({ CSSStyles: { 'height': '100%' } }) - .component(); - container.addItem(description, { flex: '0 0 auto', CSSStyles: { 'width': '200px', 'margin-right': '35px' } }); - container.addItem(impactedObjects, { flex: '0 0 auto', CSSStyles: { 'width': '280px' } }); - - return container; - } - - private createImpactedObjectsDescription(): azdata.FlexContainer { - const impactedObjectsTitle = this._view.modelBuilder.text().withProps({ - value: constants.IMPACTED_OBJECTS, - CSSStyles: { - ...styles.LIGHT_LABEL_CSS, - 'width': '280px', - 'margin': '10px 0px 0px 0px', - } - }).component(); - - const rowStyle: azdata.CssStyles = { - 'border': 'none', - 'text-align': 'left', - 'border-bottom': '1px solid' - }; - - this._impactedObjectsTable = this._view.modelBuilder.declarativeTable().withProps( - { - ariaLabel: constants.IMPACTED_OBJECTS, - enableRowSelection: true, - width: '100%', - columns: [ - { - displayName: constants.TYPE, - valueType: azdata.DeclarativeDataType.string, - width: '120px', - isReadOnly: true, - headerCssStyles: headerLeft, - rowCssStyles: rowStyle - }, - { - displayName: constants.NAME, - valueType: azdata.DeclarativeDataType.string, - width: '130px', - isReadOnly: true, - headerCssStyles: headerLeft, - rowCssStyles: rowStyle - }, - ], - dataValues: [[{ value: '' }, { value: '' }]], - CSSStyles: { 'margin-top': '12px' } - } - ).component(); - - this._disposables.push(this._impactedObjectsTable.onRowSelected((e) => { - const impactedObject = e.row > -1 ? this._impactedObjects[e.row] : undefined; - this.refreshImpactedObject(impactedObject); - })); - - const objectDetailsTitle = this._view.modelBuilder.text() - .withProps({ - value: constants.OBJECT_DETAILS, - CSSStyles: { - ...styles.LIGHT_LABEL_CSS, - 'margin': '12px 0px 0px 0px', - } - }).component(); - const objectDescriptionStyle = { - ...styles.BODY_CSS, - 'margin': '5px 0px 0px 0px', - 'word-wrap': 'break-word' - }; - this._objectDetailsType = this._view.modelBuilder.text() - .withProps({ - value: constants.TYPES_LABEL, - CSSStyles: objectDescriptionStyle - }).component(); - - this._objectDetailsName = this._view.modelBuilder.text() - .withProps({ - value: constants.NAMES_LABEL, - CSSStyles: objectDescriptionStyle - }).component(); - - this._objectDetailsSample = this._view.modelBuilder.text() - .withProps({ - value: '', - CSSStyles: objectDescriptionStyle - }).component(); - - const container = this._view.modelBuilder.flexContainer() - .withItems([ - impactedObjectsTitle, - this._impactedObjectsTable, - objectDetailsTitle, - this._objectDetailsType, - this._objectDetailsName, - this._objectDetailsSample]) - .withLayout({ flexFlow: 'column' }) - .component(); - - return container; - } - - private createDescription(): azdata.FlexContainer { - const LABEL_CSS = { - ...styles.LIGHT_LABEL_CSS, - 'width': '200px', - 'margin': '12px 0 0' - }; - const textStyle = { - ...styles.BODY_CSS, - 'width': '200px', - 'word-wrap': 'break-word' - }; - const descriptionTitle = this._view.modelBuilder.text() - .withProps({ - value: constants.DESCRIPTION, - CSSStyles: LABEL_CSS - }).component(); - this._descriptionText = this._view.modelBuilder.text() - .withProps({ - CSSStyles: textStyle - }).component(); - - const recommendationTitle = this._view.modelBuilder.text() - .withProps({ - value: constants.RECOMMENDATION, - CSSStyles: LABEL_CSS - }).component(); - this._recommendationText = this._view.modelBuilder.text() - .withProps({ - CSSStyles: textStyle - }).component(); - - this._moreInfoTitle = this._view.modelBuilder.text() - .withProps({ - value: constants.MORE_INFO, - CSSStyles: LABEL_CSS - }).component(); - this._moreInfoText = this._view.modelBuilder.hyperlink() - .withProps({ - label: '', - url: '', - CSSStyles: textStyle, - ariaLabel: constants.MORE_INFO, - showLinkIcon: true - }).component(); - - const container = this._view.modelBuilder.flexContainer() - .withItems([descriptionTitle, - this._descriptionText, - recommendationTitle, - this._recommendationText, - this._moreInfoTitle, - this._moreInfoText]) - .withLayout({ flexFlow: 'column' }) - .component(); - - return container; - } - - private createTitleComponent(): azdata.TextComponent { - return this._view.modelBuilder.text() - .withProps({ - value: constants.TARGET_PLATFORM, - CSSStyles: { - ...styles.BODY_CSS, - 'margin': '0 0 4px 0' - } - }).component(); - } - - private createPlatformComponent(): azdata.TextComponent { - return this._view.modelBuilder.text() - .withProps({ - value: constants.SUMMARY_SQLDB_TYPE, - CSSStyles: { ...styles.PAGE_SUBTITLE_CSS } - }).component(); - } - - private createRecommendationComponent(): azdata.TextComponent { - this._dbName = this._view.modelBuilder.text().withProps({ - CSSStyles: { - ...styles.LABEL_CSS, - 'margin-bottom': '8px', - 'font-weight': '700' - } - }).component(); - - return this._dbName; - } - - private createAssessmentResultsTitle(): azdata.TextComponent { - this._recommendationTitle = this._view.modelBuilder.text().withProps({ - value: constants.WARNINGS, - CSSStyles: { - ...styles.LABEL_CSS, - 'margin': '0 8px 4px 0', - 'width': '220px', - } - }).component(); - - return this._recommendationTitle; - } - - private createAssessmentDetailsTitle(): azdata.TextComponent { - this._recommendation = this._view.modelBuilder.text().withProps({ - value: constants.WARNINGS_DETAILS, - CSSStyles: { - ...styles.LABEL_CSS, - 'margin': '0 0 4px 24px', - 'width': '200px', - } - }).component(); - - return this._recommendation; - } - - private createImpactedObjectsTable(): azdata.FlexContainer { - - this._assessmentResultsList = this._view.modelBuilder.listView().withProps({ - width: '200px', - options: [] - }).component(); - - this._disposables.push(this._assessmentResultsList.onDidClick(async (e: azdata.ListViewClickEvent) => { - const selectedIssue = this._activeIssues[parseInt(this._assessmentResultsList.selectedOptionId!)]; - await this.refreshAssessmentDetails(selectedIssue); - })); - - const container = this._view.modelBuilder.flexContainer() - .withItems([this._assessmentResultsList]) - .withLayout({ - flexFlow: 'column', - height: '100%' - }) - .withProps({ CSSStyles: { 'border-right': 'solid 1px' } }) - .component(); - - return container; - } - - public refreshImpactedObject(impactedObject?: SqlMigrationImpactedObjectInfo): void { - this._objectDetailsType.value = constants.IMPACT_OBJECT_TYPE(impactedObject?.objectType); - this._objectDetailsName.value = constants.IMPACT_OBJECT_NAME(impactedObject?.name); - this._objectDetailsSample.value = impactedObject?.impactDetail || ''; - } - - public async refreshAssessmentDetails(selectedIssue?: SqlMigrationAssessmentResultItem): Promise { - await this._assessmentTitle.updateProperty('value', selectedIssue?.checkId || ''); - await this._descriptionText.updateProperty('value', selectedIssue?.description || ''); - await this._recommendationText.updateProperty('value', selectedIssue?.message || constants.NA); - - if (selectedIssue?.helpLink) { - await this._moreInfoTitle.updateProperty('display', 'flex'); - await this._moreInfoText.updateProperties({ - 'display': 'flex', - 'url': selectedIssue?.helpLink || '', - 'label': selectedIssue?.displayName || '', - 'ariaLabel': selectedIssue?.displayName || '', - 'showLinkIcon': true - }); - } else { - await this._moreInfoTitle.updateProperty('display', 'none'); - await this._moreInfoText.updateProperties({ - 'display': 'none', - 'url': '', - 'label': '', - 'ariaLabel': '', - 'showLinkIcon': false - }); - } - - this._impactedObjects = selectedIssue?.impactedObjects || []; - await this._impactedObjectsTable.setDataValues( - this._impactedObjects.map( - (object) => [{ value: object.objectType }, { value: object.name }])); - - this._impactedObjectsTable.selectedRow = this._impactedObjects?.length > 0 ? 0 : -1; - } -} diff --git a/extensions/sql-migration/src/dialog/tableMigrationSelection/tableMigrationSelectionDialog.ts b/extensions/sql-migration/src/dialog/tableMigrationSelection/tableMigrationSelectionDialog.ts index c4c2a70376..bd4f67d80d 100644 --- a/extensions/sql-migration/src/dialog/tableMigrationSelection/tableMigrationSelectionDialog.ts +++ b/extensions/sql-migration/src/dialog/tableMigrationSelection/tableMigrationSelectionDialog.ts @@ -6,7 +6,6 @@ import * as azdata from 'azdata'; import * as vscode from 'vscode'; import * as constants from '../../constants/strings'; -import * as styles from '../../constants/styles'; import { AzureSqlDatabaseServer } from '../../api/azure'; import { collectSourceDatabaseTableInfo, collectTargetDatabaseTableInfo, TableInfo } from '../../api/sqlUtils'; import { MigrationStateModel } from '../../models/stateMachine'; @@ -20,8 +19,6 @@ export class TableMigrationSelectionDialog { private _dialog: azdata.window.Dialog | undefined; private _headingText!: azdata.TextComponent; private _refreshButton!: azdata.ButtonComponent; - private _schemaMigrationCheckBox!: azdata.CheckBoxComponent; - private _schemaMigrationInfoBox!: azdata.InfoBoxComponent; private _filterInputBox!: azdata.InputBoxComponent; private _tableSelectionTable!: azdata.TableComponent; private _missingTargetTablesTable!: azdata.TableComponent; @@ -31,7 +28,6 @@ export class TableMigrationSelectionDialog { private _model: MigrationStateModel; private _sourceDatabaseName: string; private _tableSelectionMap!: Map; - private _missingTableSelectionMap!: Map; private _targetTableMap!: Map; private _onSaveCallback: () => Promise; private _missingTableCount: number = 0; @@ -59,7 +55,6 @@ export class TableMigrationSelectionDialog { const targetDatabaseInfo = this._model._sourceTargetMapping.get(this._sourceDatabaseName); if (targetDatabaseInfo) { - targetDatabaseInfo.enableSchemaMigration = false; const sourceTableList: TableInfo[] = await collectSourceDatabaseTableInfo( this._sourceDatabaseName); @@ -81,67 +76,27 @@ export class TableMigrationSelectionDialog { })); this._tableSelectionMap = new Map(); - this._missingTableSelectionMap = new Map(); sourceTableList.forEach(table => { // If the source table doesn't exist in the target, set isSelected to false. // Otherwise, set it to true as default. var isSelected = false; - var sourceTable = this._targetTableMap.get(table.tableName); + var sourceTable = targetDatabaseInfo.sourceTables.get(table.tableName); if (sourceTable === null || sourceTable === undefined) { - isSelected = false; - const tableInfo: TableInfo = { - databaseName: table.databaseName, - rowCount: table.rowCount, - selectedForMigration: isSelected, - tableName: table.tableName, - }; - this._missingTableSelectionMap.set(table.tableName, tableInfo); + sourceTable = this._targetTableMap.get(table.tableName); + isSelected = sourceTable === null || sourceTable === undefined ? false : true; } else { - var savedSourceTable = targetDatabaseInfo.sourceTables.get(table.tableName); - isSelected = savedSourceTable === null || savedSourceTable === undefined ? true : savedSourceTable.selectedForMigration; - const tableInfo: TableInfo = { - databaseName: table.databaseName, - rowCount: table.rowCount, - selectedForMigration: isSelected, - tableName: table.tableName, - }; - this._tableSelectionMap.set(table.tableName, tableInfo); + isSelected = sourceTable.selectedForMigration; } + + const tableInfo: TableInfo = { + databaseName: table.databaseName, + rowCount: table.rowCount, + selectedForMigration: isSelected, + tableName: table.tableName, + }; + this._tableSelectionMap.set(table.tableName, tableInfo); }); - - this._missingTableCount = this._missingTableSelectionMap.size; - if (this._tableSelectionMap.size === 0) { - // Full schema missing on the target - await this._schemaMigrationInfoBox.updateProperties({ - text: constants.FULL_SCHEMA_MISSING_ON_TARGET, - style: "warning", - width: '600px', - CSSStyles: { ...styles.BODY_CSS, 'margin': '5px 0 0 0' }, - isClickable: true - }); - } else if (this._missingTableSelectionMap.size > 0) { - // Partial schema found on the target - await this._schemaMigrationInfoBox.updateProperties({ - text: constants.PARTIAL_SCHEMA_ON_TARGET, - style: "warning", - width: '600px', - CSSStyles: { ...styles.BODY_CSS, 'margin': '5px 0 0 0' }, - isClickable: true - }); - } else { - // Full schema found on the target - this._schemaMigrationCheckBox.checked = false; - this._schemaMigrationCheckBox.enabled = false; - await this._schemaMigrationInfoBox.updateProperties({ - text: constants.FULL_SCHEMA_ON_TARGET, - style: "information", - width: '600px', - CSSStyles: { ...styles.BODY_CSS, 'margin': '5px 0 0 0' }, - isClickable: false - }); - } } - this._dialog!.message = { text: '', level: azdata.window.MessageLevel.Information }; } catch (error) { this._dialog!.message = { @@ -157,144 +112,43 @@ export class TableMigrationSelectionDialog { } } - private async _createMigrationContainer(view: azdata.ModelView): Promise { - // Schema migration components - const schemaMigrationHeader = view.modelBuilder.text() - .withProps({ - value: constants.SCHEMA_MIGRATION_HEADER, - requiredIndicator: true, - CSSStyles: { ...styles.SECTION_HEADER_CSS, 'margin-top': '4px' } - }).component(); - - this._schemaMigrationCheckBox = view.modelBuilder.checkBox() - .withProps({ - checked: false, - label: constants.SCHEMA_MIGRATION_CHECKBOX_INFO, - }).component(); - - this._schemaMigrationInfoBox = view.modelBuilder.infoBox() - .withProps({ - text: '', - style: "information", - width: '40px', - CSSStyles: { ...styles.BODY_CSS, 'margin': '5px 0 0 0' }, - }).component(); - - this._disposables.push( - this._schemaMigrationCheckBox.onChanged( - async checked => { - const targetDatabaseInfo = this._model._sourceTargetMapping.get(this._sourceDatabaseName); - if (targetDatabaseInfo) { - targetDatabaseInfo.enableSchemaMigration = checked; - const missingData: any[][] = []; - const selectedItems: number[] = []; - let tableRow = 0; - this._missingTableSelectionMap.forEach(sourceTable => { - sourceTable.selectedForMigration = checked; - const tableRowCount = sourceTable?.rowCount ?? 0; - const tableStatus = tableRowCount > 0 - ? constants.TARGET_TABLE_NOT_EMPTY - : '--'; - missingData.push([ - sourceTable.selectedForMigration, - sourceTable.tableName, - tableStatus - ]); - if (sourceTable.selectedForMigration) { - selectedItems.push(tableRow); - } - tableRow++; - }); - - await this._missingTargetTablesTable.updateProperty('data', missingData); - this._missingTargetTablesTable.selectedRows = selectedItems; - this._updateRowSelection(); - } - } - ) - ) - - // Data migration component - const dataMigrationHeader = view.modelBuilder.text() - .withProps({ - description: constants.DATA_MIGRATION_INFO, - value: constants.DATA_MIGRATION_HEADER, - requiredIndicator: true, - CSSStyles: { ...styles.SECTION_HEADER_CSS, 'margin-top': '4px' } - }).component(); - - this._tabs = view.modelBuilder.tabbedPanel() - .withTabs([]) - .component(); - - await this._createSelectableTablesTab(view); - await this._createMissingTablesTab(view); - - this._tabs.updateTabs([this._selectableTablesTab]); - - this._disposables.push( - view.onClosed(e => - this._disposables.forEach( - d => { try { d.dispose(); } catch { } }))); - - return view.modelBuilder - .flexContainer() - .withLayout({ flexFlow: 'column' }) - .withItems([schemaMigrationHeader, this._schemaMigrationCheckBox, this._schemaMigrationInfoBox, dataMigrationHeader, this._tabs]) - .component(); - } - private async _loadControls(): Promise { const data: any[][] = []; const missingData: any[][] = []; const filterText = this._filterInputBox.value ?? ''; const selectedItems: number[] = []; - const missingSelectedItems: number[] = []; let tableRow = 0; - let missingTableRow = 0; - + this._missingTableCount = 0; this._tableSelectionMap.forEach(sourceTable => { const tableName = sourceTable.tableName.toLocaleLowerCase(); const searchText = filterText.toLocaleLowerCase(); if (filterText?.length === 0 || tableName.indexOf(searchText) > -1) { const targetTable = this._targetTableMap.get(sourceTable.tableName); - const targetTableRowCount = targetTable?.rowCount ?? 0; - const tableStatus = targetTableRowCount > 0 - ? constants.TARGET_TABLE_NOT_EMPTY - : '--'; + if (targetTable) { + const targetTableRowCount = targetTable?.rowCount ?? 0; + const tableStatus = targetTableRowCount > 0 + ? constants.TARGET_TABLE_NOT_EMPTY + : '--'; - data.push([ - sourceTable.selectedForMigration, - sourceTable.tableName, - tableStatus]); + data.push([ + sourceTable.selectedForMigration, + sourceTable.tableName, + tableStatus]); - if (sourceTable.selectedForMigration) { - selectedItems.push(tableRow); + if (sourceTable.selectedForMigration) { + selectedItems.push(tableRow); + } + + tableRow++; + } else { + this._missingTableCount++; + missingData.push([sourceTable.tableName]); } - - tableRow++; } }); - - this._missingTableSelectionMap.forEach(sourceTable => { - const tableRowCount = sourceTable?.rowCount ?? 0; - const tableStatus = tableRowCount > 0 - ? constants.TARGET_TABLE_NOT_EMPTY - : '--'; - missingData.push([ - sourceTable.selectedForMigration, - sourceTable.tableName, - tableStatus]); - if (sourceTable.selectedForMigration) { - missingSelectedItems.push(missingTableRow); - } - missingTableRow++; - }); - await this._tableSelectionTable.updateProperty('data', data); this._tableSelectionTable.selectedRows = selectedItems; await this._missingTargetTablesTable.updateProperty('data', missingData); - this._missingTargetTablesTable.selectedRows = missingSelectedItems; this._updateRowSelection(); if (this._missingTableCount > 0 && this._tabs.items.length === 1) { @@ -303,27 +157,28 @@ export class TableMigrationSelectionDialog { } private async _initializeDialog(dialog: azdata.window.Dialog): Promise { - dialog.registerContent(async (view: azdata.ModelView) => { - dialog.okButton.label = constants.TABLE_SELECTION_UPDATE_BUTTON; - dialog.okButton.position = 'left'; - this._disposables.push( - dialog.okButton.onClick( - async () => this._save())); + const tab = azdata.window.createTab(''); + tab.registerContent(async (view) => { - dialog.cancelButton.label = constants.TABLE_SELECTION_CANCEL_BUTTON; - dialog.cancelButton.position = 'left'; - this._disposables.push( - dialog.cancelButton.onClick( - async () => this._isOpen = false)); - const flexContainer = view.modelBuilder - .flexContainer() - .withItems([ - await this._createMigrationContainer(view)]) - .withProps({ CSSStyles: { 'padding': '20px' } }) + this._tabs = view.modelBuilder.tabbedPanel() + .withTabs([]) .component(); - await view.initializeModel(flexContainer); + + await this._createSelectableTablesTab(view); + await this._createMissingTablesTab(view); + + this._tabs.updateTabs([this._selectableTablesTab]); + + this._disposables.push( + view.onClosed(e => + this._disposables.forEach( + d => { try { d.dispose(); } catch { } }))); + + await view.initializeModel(this._tabs); await this._loadData(); }); + + dialog.content = [tab]; } public async openDialog(dialogTitle: string) { @@ -334,6 +189,18 @@ export class TableMigrationSelectionDialog { DialogName, 600, undefined, undefined, false); + this._dialog.okButton.label = constants.TABLE_SELECTION_UPDATE_BUTTON; + this._dialog.okButton.position = 'left'; + this._disposables.push( + this._dialog.okButton.onClick( + async () => this._save())); + + this._dialog.cancelButton.label = constants.TABLE_SELECTION_CANCEL_BUTTON; + this._dialog.cancelButton.position = 'left'; + this._disposables.push( + this._dialog.cancelButton.onClick( + async () => this._isOpen = false)); + const promise = this._initializeDialog(this._dialog); azdata.window.openDialog(this._dialog); await promise; @@ -406,7 +273,7 @@ export class TableMigrationSelectionDialog { this._selectableTablesTab = { content: flex, id: 'tableSelectionTab', - title: constants.AVAILABLE_TABLE_COUNT_ON_TARGET(0), + title: constants.SELECT_TABLES_FOR_MIGRATION, }; } @@ -473,6 +340,26 @@ export class TableMigrationSelectionDialog { this._disposables.push( table.onRowSelected( async e => { + // collect table list selected for migration + const selectedRows = this._tableSelectionTable.selectedRows ?? []; + selectedRows.forEach(rowIndex => { + // get selected source table name + const rowData = this._tableSelectionTable.data[rowIndex]; + const sourceTableName = rowData.length > 1 + ? rowData[1] as string + : ''; + // get source table info + const sourceTableInfo = this._tableSelectionMap.get(sourceTableName); + if (sourceTableInfo) { + // see if source table exists on target database + const targetTableInfo = this._targetTableMap.get(sourceTableName); + // keep source table selected + sourceTableInfo.selectedForMigration = targetTableInfo !== undefined; + // update table selection map with new selectedForMigration value + this._tableSelectionMap.set(sourceTableName, sourceTableInfo); + } + }); + this._updateRowSelection(); })); @@ -488,42 +375,17 @@ export class TableMigrationSelectionDialog { height: '600px', display: 'flex', forceFitColumns: azdata.ColumnSizingMode.ForceFit, - columns: [ - { - value: '', - width: 10, - type: azdata.ColumnType.checkBox, - action: azdata.ActionOnCellCheckboxCheck.selectRow, - resizable: false, - cssClass: cssClass, - headerCssClass: cssClass, - }, - { - name: constants.TABLE_SELECTION_TABLENAME_COLUMN, - value: 'tableName', - type: azdata.ColumnType.text, - width: 285, - cssClass: cssClass, - headerCssClass: cssClass, - }, - { - name: constants.TABLE_SELECTION_HASROWS_COLUMN, - value: 'hasRows', - type: azdata.ColumnType.text, - width: 255, - cssClass: cssClass, - headerCssClass: cssClass, - }] + columns: [{ + name: constants.MISSING_TABLE_NAME_COLUMN, + value: 'tableName', + type: azdata.ColumnType.text, + cssClass: cssClass, + headerCssClass: cssClass, + }], }) .withValidation(() => true) .component(); - this._disposables.push( - table.onRowSelected( - async e => { - this._updateRowSelection(); - })); - return table; } @@ -531,66 +393,40 @@ export class TableMigrationSelectionDialog { this._headingText.value = this._refreshLoader.loading ? constants.DATABASE_LOADING_TABLES : this._tableSelectionTable.data?.length > 0 - ? constants.TABLE_SELECTION_COUNT_TO_TARGET( + ? constants.TABLE_SELECTED_COUNT( this._tableSelectionTable.selectedRows?.length ?? 0, this._tableSelectionTable.data?.length ?? 0) : constants.DATABASE_MISSING_TABLES; - this._selectableTablesTab.title = constants.AVAILABLE_TABLE_COUNT_ON_TARGET(this._tableSelectionMap?.size ?? 0); this._missingTablesTab.title = constants.MISSING_TARGET_TABLES_COUNT(this._missingTableCount); } private async _save(): Promise { const targetDatabaseInfo = this._model._sourceTargetMapping.get(this._sourceDatabaseName); if (targetDatabaseInfo) { - // Reset selectedForMigration as false - this._tableSelectionMap.forEach(sourceTable => { - sourceTable.selectedForMigration = false; - targetDatabaseInfo.sourceTables.set(sourceTable.tableName, sourceTable); - }) - - // Set selectedForMigration from selectedRows + // collect table list selected for migration const selectedRows = this._tableSelectionTable.selectedRows ?? []; + const selectedTables = new Map(); selectedRows.forEach(rowIndex => { - // get selected source table name - const rowData = this._tableSelectionTable.data[rowIndex]; - const sourceTableName = rowData.length > 1 - ? rowData[1] as string + const tableRow = this._tableSelectionTable.data[rowIndex]; + const tableName = tableRow.length > 1 + ? tableRow[1] as string : ''; - // get source table info - const sourceTableInfo = this._tableSelectionMap.get(sourceTableName); - if (sourceTableInfo) { - // keep source table selected - sourceTableInfo.selectedForMigration = true; - // update table selection map with new selectedForMigration value - targetDatabaseInfo.sourceTables.set(sourceTableInfo.tableName, sourceTableInfo); + const tableInfo = this._tableSelectionMap.get(tableName); + if (tableInfo) { + selectedTables.set(tableName, tableInfo); } }); - // Reset selectedForMigration as false - this._missingTableSelectionMap.forEach(sourceTable => { - sourceTable.selectedForMigration = false; - targetDatabaseInfo.sourceTables.set(sourceTable.tableName, sourceTable); - }) - - //Set selectedForMigration from selectedRows - const selectedRowsFromMissingTable = this._missingTargetTablesTable.selectedRows ?? []; - selectedRowsFromMissingTable.forEach(rowIndex => { - // get selected source table name - const rowData = this._missingTargetTablesTable.data[rowIndex]; - const sourceTableName = rowData.length > 1 - ? rowData[1] as string - : ''; - // get source table info - const sourceTableInfo = this._missingTableSelectionMap.get(sourceTableName); - if (sourceTableInfo) { - // keep source table selected - sourceTableInfo.selectedForMigration = true; - // update table selection map with new selectedForMigration value - targetDatabaseInfo.sourceTables.set(sourceTableInfo.tableName, sourceTableInfo); - } + // copy table map selection status from grid + this._tableSelectionMap.forEach(tableInfo => { + const selectedTableInfo = selectedTables.get(tableInfo.tableName); + tableInfo.selectedForMigration = selectedTableInfo?.selectedForMigration === true; + this._tableSelectionMap.set(tableInfo.tableName, tableInfo); }); + // save table selection changes to migration source target map + targetDatabaseInfo.sourceTables = this._tableSelectionMap; this._model._sourceTargetMapping.set(this._sourceDatabaseName, targetDatabaseInfo); } await this._onSaveCallback(); diff --git a/extensions/sql-migration/src/models/stateMachine.ts b/extensions/sql-migration/src/models/stateMachine.ts index 1fde965fa2..4fec86f3de 100644 --- a/extensions/sql-migration/src/models/stateMachine.ts +++ b/extensions/sql-migration/src/models/stateMachine.ts @@ -153,7 +153,6 @@ export interface SavedInfo { serviceSubscription: azurecore.azureResource.AzureResourceSubscription | null; serviceResourceGroup: azurecore.azureResource.AzureResourceResourceGroup | null; serverAssessment: ServerAssessment | null; - xEventsFilesFolderPath: string | null; skuRecommendation: SkuRecommendationSavedInfo | null; } @@ -1097,7 +1096,7 @@ export class MigrationStateModel implements Model, vscode.Disposable { }); // skip databases that don't have tables selected - if (selectedTables === 0 && !targetDatabaseInfo?.enableSchemaMigration) { + if (selectedTables === 0) { continue; } @@ -1121,15 +1120,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { trustServerCertificate: false, }; - // Schema + data configuration - requestBody.properties.sqlSchemaMigrationConfiguration = { - enableSchemaMigration: targetDatabaseInfo?.enableSchemaMigration ?? false - }; - - requestBody.properties.sqlDataMigrationConfiguration = { - enableDataMigration: selectedTables > 0 - }; - // send an empty array when 'all' tables are selected for migration requestBody.properties.tableList = selectedTables === totalTables ? [] @@ -1188,8 +1178,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { response.databaseMigration.properties.sourceDatabaseName = this._databasesForMigration[i]; response.databaseMigration.properties.backupConfiguration = requestBody.properties.backupConfiguration!; response.databaseMigration.properties.offlineConfiguration = requestBody.properties.offlineConfiguration!; - response.databaseMigration.properties.sqlSchemaMigrationConfiguration = requestBody.properties.sqlSchemaMigrationConfiguration!; - response.databaseMigration.properties.sqlDataMigrationConfiguration = requestBody.properties.sqlDataMigrationConfiguration!; let wizardEntryPoint = WizardEntryPoint.Default; if (this.resumeAssessment) { @@ -1269,7 +1257,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { targetDatabaseNames: [], sqlMigrationService: undefined, serverAssessment: null, - xEventsFilesFolderPath: null, skuRecommendation: null, serviceResourceGroup: null, serviceSubscription: null, @@ -1301,7 +1288,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { saveInfo.migrationTargetType = this._targetType; saveInfo.databaseList = this._databasesForMigration; saveInfo.serverAssessment = this._assessmentResults; - saveInfo.xEventsFilesFolderPath = this._xEventsFilesFolderPath; if (this._skuRecommendationPerformanceDataSource) { const skuRecommendation: SkuRecommendationSavedInfo = { @@ -1318,7 +1304,6 @@ export class MigrationStateModel implements Model, vscode.Disposable { case Page.DatabaseSelector: saveInfo.databaseAssessment = this._databasesForAssessment; - saveInfo.xEventsFilesFolderPath = this._xEventsFilesFolderPath; await this.extensionContext.globalState.update(`${this.mementoString}.${serverName}`, saveInfo); } } @@ -1365,12 +1350,10 @@ export class MigrationStateModel implements Model, vscode.Disposable { this._sqlMigrationServiceSubscription = this.savedInfo.serviceSubscription || undefined!; this._sqlMigrationServiceResourceGroup = this.savedInfo.serviceResourceGroup || undefined!; - this._assessedDatabaseList = this.savedInfo.databaseAssessment ?? []; - this._databasesForAssessment = this.savedInfo.databaseAssessment ?? []; - this._xEventsFilesFolderPath = this.savedInfo.xEventsFilesFolderPath ?? ''; const savedAssessmentResults = this.savedInfo.serverAssessment; if (savedAssessmentResults) { this._assessmentResults = savedAssessmentResults; + this._assessedDatabaseList = this.savedInfo.databaseAssessment; } const savedSkuRecommendation = this.savedInfo.skuRecommendation; diff --git a/extensions/sql-migration/src/wizard/databaseBackupPage.ts b/extensions/sql-migration/src/wizard/databaseBackupPage.ts index a7674dac4b..639ca0db0a 100644 --- a/extensions/sql-migration/src/wizard/databaseBackupPage.ts +++ b/extensions/sql-migration/src/wizard/databaseBackupPage.ts @@ -19,7 +19,6 @@ import * as styles from '../constants/styles'; import { TableMigrationSelectionDialog } from '../dialog/tableMigrationSelection/tableMigrationSelectionDialog'; import { ValidateIrDialog } from '../dialog/validationResults/validateIrDialog'; import { canTargetConnectToStorageAccount, getSourceConnectionCredentials, getSourceConnectionProfile, getSourceConnectionQueryProvider, getSourceConnectionUri } from '../api/sqlUtils'; -import { SchemaMigrationAssessmentDialog } from '../dialog/tableMigrationSelection/schemaMigrationAssessmentDialog'; const WIZARD_TABLE_COLUMN_WIDTH = '200px'; const WIZARD_TABLE_COLUMN_WIDTH_SMALL = '170px'; @@ -1422,10 +1421,6 @@ export class DatabaseBackupPage extends MigrationWizardPage { for (const targetDatabaseInfo of this.migrationStateModel._sourceTargetMapping) { const databaseInfo = targetDatabaseInfo[1]; if (databaseInfo) { - if (databaseInfo.enableSchemaMigration) { - return true; - } - for (const sourceTable of databaseInfo.sourceTables) { const tableInfo = sourceTable[1]; if (tableInfo.selectedForMigration === true) { @@ -1727,7 +1722,7 @@ export class DatabaseBackupPage extends MigrationWizardPage { const cssClass = undefined; this._databaseTable = this._view.modelBuilder.table() .withProps({ - width: '820px', + forceFitColumns: azdata.ColumnSizingMode.AutoFit, height: '600px', CSSStyles: { 'margin': '15px 0 0 0' }, data: [], @@ -1748,15 +1743,6 @@ export class DatabaseBackupPage extends MigrationWizardPage { cssClass: cssClass, headerCssClass: cssClass, }, - { - name: constants.SCHEMA_MIGRATION_COLUMN_LABLE, - value: 'enableSchemaMigration', - width: 180, - type: azdata.ColumnType.hyperlink, - cssClass: cssClass, - headerCssClass: cssClass, - icon: IconPathHelper.cancel - }, { name: constants.DATABASE_TABLE_SELECTED_TABLES_COLUMN_LABEL, value: 'selectedTables', @@ -1781,11 +1767,6 @@ export class DatabaseBackupPage extends MigrationWizardPage { async (rowState: azdata.ICellActionEventArgs) => { const buttonState = rowState; if (buttonState?.column === 2) { - const sourceDatabaseName = this._databaseTable.data[rowState.row][0]; - if (sourceDatabaseName && this._hasSchemaMigraitonBlockerIssues(sourceDatabaseName)) { - await this._openSchemaMigrationAssessmentDialog(sourceDatabaseName, () => this._loadTableData()); - } - } else if (buttonState?.column === 3) { // open table selection dialog const sourceDatabaseName = this._databaseTable.data[rowState.row][0]; const targetDatabaseInfo = this.migrationStateModel._sourceTargetMapping.get(sourceDatabaseName); @@ -1810,16 +1791,6 @@ export class DatabaseBackupPage extends MigrationWizardPage { .component(); } - private async _openSchemaMigrationAssessmentDialog( - sourceDatabaseName: string, - onSaveCallback: () => Promise): Promise { - const dialog = new SchemaMigrationAssessmentDialog( - this.migrationStateModel, - sourceDatabaseName, - onSaveCallback); - await dialog.openDialog(""); - } - private async _openTableSelectionDialog( sourceDatabaseName: string, targetDatabaseName: string, @@ -1850,14 +1821,6 @@ export class DatabaseBackupPage extends MigrationWizardPage { data.push([ sourceDatabaseName, targetDatabaseInfo?.databaseName, - { - icon: targetDatabaseInfo?.enableSchemaMigration - ? this._hasSchemaMigraitonBlockerIssues(sourceDatabaseName) ? IconPathHelper.warning : IconPathHelper.completedMigration - : IconPathHelper.cancel, - title: targetDatabaseInfo?.enableSchemaMigration - ? this._hasSchemaMigraitonBlockerIssues(sourceDatabaseName) ? "Assessment results" : "" - : "", - }, { // table selection icon: hasSelectedTables ? IconPathHelper.completedMigration @@ -1872,12 +1835,4 @@ export class DatabaseBackupPage extends MigrationWizardPage { await this._databaseTable.updateProperty('data', data); this._refreshLoading.loading = false; } - - private _hasSchemaMigraitonBlockerIssues(sourceDatabaseName: string): boolean { - var assessmentResults = this.migrationStateModel._assessmentResults?.databaseAssessments.find(r => r.name === sourceDatabaseName)?.issues.filter(i => i.appliesToMigrationTargetPlatform === MigrationTargetType.SQLDB) ?? []; - if (assessmentResults !== null && assessmentResults !== undefined) { - return assessmentResults.find(i => constants.SchemaMigrationFailedRulesLookup[i.ruleId] !== undefined) !== undefined; - } - return false; - } } diff --git a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts index 8f28446d35..918820a562 100644 --- a/extensions/sql-migration/src/wizard/databaseSelectorPage.ts +++ b/extensions/sql-migration/src/wizard/databaseSelectorPage.ts @@ -18,12 +18,11 @@ export class DatabaseSelectorPage extends MigrationWizardPage { private _databaseSelectorTable!: azdata.TableComponent; private _xEventsGroup!: azdata.GroupContainer; private _xEventsFolderPickerInput!: azdata.InputBoxComponent; - private _xEventsFilesFolderPath: string = ''; + private _xEventsFilesFolderPath!: string; private _dbNames!: string[]; private _dbCount!: azdata.TextComponent; private _databaseTableValues!: any[]; private _disposables: vscode.Disposable[] = []; - private _enableNavigationValidation: boolean = true; private readonly TABLE_WIDTH = 650; @@ -58,12 +57,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage { if (pageChangeInfo.newPage < pageChangeInfo.lastPage) { return true; } - - if (!this._enableNavigationValidation) { - this._enableNavigationValidation = true; - return true; - } - if (this.selectedDbs().length === 0) { this.wizard.message = { text: constants.SELECT_DATABASE_TO_CONTINUE, @@ -74,14 +67,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage { return true; }); - if (this.migrationStateModel.resumeAssessment) { - // if start a new session, it won't trigger navigation validator until clicking 'Next'. - // It works as expected if no target or databases are selected, it should show errors and block to next page. - // However, if resume the previously saved session, wizard.setCurrentPage will trigger wizard navigation validator without clicking 'Next'. - // At this moment, all components are not initialized yet. Therefore, _databaseSelectorTable is undefined, so selectedDbs().length is always 0. - this._enableNavigationValidation = false; - } - this._xEventsFilesFolderPath = this.migrationStateModel._xEventsFilesFolderPath; } diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 1af5d1b036..db957beb82 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -654,10 +654,6 @@ export class SKURecommendationPage extends MigrationWizardPage { public async refreshCardText(showLoadingIcon: boolean = true): Promise { this._rbgLoader.loading = showLoadingIcon && true; - if (!this._rbg.selectedCardId) { - this._rbg.selectedCardId = MigrationTargetType.SQLMI; - } - switch (this._rbg.selectedCardId) { case MigrationTargetType.SQLMI: this.migrationStateModel._databasesForMigration = this.migrationStateModel._miDbs; diff --git a/extensions/sql-migration/src/wizard/targetSelectionPage.ts b/extensions/sql-migration/src/wizard/targetSelectionPage.ts index d066fa3d5f..c1e5b3ba47 100644 --- a/extensions/sql-migration/src/wizard/targetSelectionPage.ts +++ b/extensions/sql-migration/src/wizard/targetSelectionPage.ts @@ -89,7 +89,7 @@ export class TargetSelectionPage extends MigrationWizardPage { { component: this._pageDescription }, { component: this.createAzureAccountsDropdown() }, { component: this.createAzureTenantContainer() }, - { component: await this.createTargetDropdownContainer() }, + { component: this.createTargetDropdownContainer() }, { component: this._certMigrationRequiredInfoBox } ]).withProps({ CSSStyles: { 'padding-top': '0' } @@ -271,10 +271,6 @@ export class TargetSelectionPage extends MigrationWizardPage { await this._targetPasswordInputBox.updateProperties({ required: isSqlDbTarget }); await utils.updateControlDisplay(this._resourceAuthenticationContainer, isSqlDbTarget); - if (!this._migrationTargetPlatform) { - this._migrationTargetPlatform = this.migrationStateModel._targetType; - } - 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 @@ -291,19 +287,14 @@ export class TargetSelectionPage extends MigrationWizardPage { await utils.clearDropDown(this._azureAccountsDropdown); await utils.clearDropDown(this._accountTenantDropdown); - await utils.clearDropDownWithLoading(this._azureSubscriptionDropdown, constants.SELECT_A_SUBSCRIPTION); - await utils.clearDropDownWithLoading(this._azureLocationDropdown, constants.SELECT_A_LOCATION); - await utils.clearDropDownWithLoading(this._azureResourceGroupDropdown, constants.SELECT_A_RESOURCE_GROUP); - await utils.clearDropDownWithLoading(this._azureResourceDropdown, constants.SELECT_SERVICE_PLACEHOLDER); + await utils.clearDropDown(this._azureSubscriptionDropdown); + await utils.clearDropDown(this._azureLocationDropdown); + await utils.clearDropDown(this._azureResourceGroupDropdown); + await utils.clearDropDown(this._azureResourceDropdown); } await utils.updateControlDisplay(this._certMigrationRequiredInfoBox, this.migrationStateModel.tdeMigrationConfig.shouldAdsMigrateCertificates()); await this.populateAzureAccountsDropdown(); - await this.populateTenantsDropdown(); - await this.populateSubscriptionDropdown(); - await this.populateLocationDropdown(); - await this.populateResourceGroupDropdown(); - await this.populateResourceInstanceDropdown(); } public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise { @@ -412,7 +403,7 @@ export class TargetSelectionPage extends MigrationWizardPage { return this._accountTenantFlexContainer; } - private async createTargetDropdownContainer(): Promise { + private createTargetDropdownContainer(): azdata.FlexContainer { const subscriptionDropdownLabel = this._view.modelBuilder.text() .withProps({ value: constants.SUBSCRIPTION, @@ -483,11 +474,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._resourceSelectionContainer = this._createResourceDropdowns(); this._resourceAuthenticationContainer = this._createResourceAuthenticationContainer(); - 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); - return this._view.modelBuilder.flexContainer() .withItems([ subscriptionDropdownLabel, @@ -714,13 +700,9 @@ export class TargetSelectionPage extends MigrationWizardPage { await this.populateResourceInstanceDropdown(); })); - const resourceDropDownValue = this.migrationStateModel._targetType === MigrationTargetType.SQLMI - ? constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE - : this.migrationStateModel._targetType === MigrationTargetType.SQLDB ? constants.AZURE_SQL_DATABASE : constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE; - this._azureResourceDropdownLabel = this._view.modelBuilder.text() .withProps({ - value: resourceDropDownValue, + value: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE, description: constants.TARGET_RESOURCE_INFO, width: WIZARD_INPUT_COMPONENT_WIDTH, requiredIndicator: true, @@ -728,7 +710,7 @@ export class TargetSelectionPage extends MigrationWizardPage { }).component(); this._azureResourceDropdown = this._view.modelBuilder.dropDown() .withProps({ - ariaLabel: resourceDropDownValue, + ariaLabel: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE, width: WIZARD_INPUT_COMPONENT_WIDTH, editable: true, required: true, @@ -928,10 +910,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._azureAccountsDropdown, accountId, false); - const selectedAccount = this.migrationStateModel._azureAccounts?.find(account => account.displayInfo.displayName === (this._azureAccountsDropdown.value)?.displayName); - this.migrationStateModel._azureAccount = (selectedAccount) - ? utils.deepClone(selectedAccount)! - : undefined!; } finally { this._azureAccountsDropdown.loading = false; } @@ -954,10 +932,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._accountTenantDropdown, tenantId, true); - const selectedTenant = this.migrationStateModel._accountTenants?.find(tenant => tenant.displayName === (this._accountTenantDropdown.value)?.displayName); - this.migrationStateModel._azureTenant = selectedTenant - ? utils.deepClone(selectedTenant) - : undefined!; } await this._azureAccountsDropdown.validate(); } finally { @@ -986,12 +960,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._azureSubscriptionDropdown, subscriptionId, false); - const selectedSubscription = this.migrationStateModel._subscriptions?.find( - subscription => `${subscription.name} - ${subscription.id}` === (this._azureSubscriptionDropdown.value)?.displayName); - this.migrationStateModel._targetSubscription = (selectedSubscription) - ? utils.deepClone(selectedSubscription)! - : undefined!; - this.migrationStateModel.refreshDatabaseBackupPage = true; } catch (e) { console.log(e); } finally { @@ -1041,10 +1009,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._azureLocationDropdown, location, true); - const selectedLocation = this.migrationStateModel._locations?.find(location => location.displayName === (this._azureLocationDropdown.value)?.displayName); - this.migrationStateModel._location = (selectedLocation) - ? utils.deepClone(selectedLocation)! - : undefined!; } catch (e) { console.log(e); } finally { @@ -1083,11 +1047,6 @@ export class TargetSelectionPage extends MigrationWizardPage { this._azureResourceGroupDropdown, resourceGroupId, false); - - const selectedResourceGroup = this.migrationStateModel._resourceGroups?.find(rg => rg.name === (this._azureResourceGroupDropdown.value)?.displayName); - this.migrationStateModel._resourceGroup = (selectedResourceGroup) - ? utils.deepClone(selectedResourceGroup)! - : undefined!; } catch (e) { console.log(e); } finally {