Skip to content

Commit

Permalink
add schema migration (#24594)
Browse files Browse the repository at this point in the history
* add schema migration

* fix table selection issue

* fix table validation

* data only wont show schema info grids

* address comments

* update schema migration helper banner

* fix undefined

* refactor

* endedOn is - when not started

* fix toString

* refactor

* auto referesh dashboardtab and migrationtab

* add migration type in dashboard

* fix saving issue in page 0 and page 1

* fix compile issue

* fix save and close in page 3

* refactor

* fix save

* fix save

* fix save

* dont load location twice

* fix target type undefined issue

* set MI as default

* fix cannot load in step 3

* integrate assessment with schema

---------

Co-authored-by: Siyang Yao <[email protected]>
  • Loading branch information
siyao-Siyang and siyangMicrosoft authored Oct 11, 2023
1 parent 32fa73f commit f70ff23
Show file tree
Hide file tree
Showing 16 changed files with 1,346 additions and 138 deletions.
46 changes: 46 additions & 0 deletions extensions/sql-migration/src/api/azure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,7 @@ 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
Expand Down Expand Up @@ -956,6 +957,8 @@ export interface StartDatabaseMigrationRequest {
tableList?: string[],
scope: string,
offlineConfiguration?: OfflineConfiguration,
sqlSchemaMigrationConfiguration?: SqlSchemaMigrationConfiguration,
sqlDataMigrationConfiguration?: SqlDataMigrationConfiguration
}
}

Expand Down Expand Up @@ -1082,6 +1085,8 @@ export interface DatabaseMigrationProperties {
migrationOperationId: string;
backupConfiguration: BackupConfiguration;
offlineConfiguration: OfflineConfiguration;
sqlSchemaMigrationConfiguration: SqlSchemaMigrationConfiguration;
sqlDataMigrationConfiguration: SqlDataMigrationConfiguration;
migrationFailureError: ErrorInfo;
tableList: string[];
}
Expand All @@ -1103,6 +1108,7 @@ export interface MigrationStatusDetails {
invalidFiles: string[];
listOfCopyProgressDetails: CopyProgressDetail[];
sqlDataCopyErrors: string[];
sqlSchemaMigrationStatus: SqlSchemaMigrationStatus;

// new fields
pendingDiffBackupsCount: number;
Expand Down Expand Up @@ -1142,6 +1148,38 @@ 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;
Expand All @@ -1152,6 +1190,14 @@ export interface OfflineConfiguration {
lastBackupName?: string;
}

export interface SqlSchemaMigrationConfiguration {
enableSchemaMigration: boolean;
}

export interface SqlDataMigrationConfiguration {
enableDataMigration: boolean;
}

export interface ErrorInfo {
code: string;
message: string;
Expand Down
2 changes: 2 additions & 0 deletions extensions/sql-migration/src/api/sqlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export interface TargetDatabaseInfo {
isReadOnly: boolean;
sourceTables: Map<string, TableInfo>;
targetTables: Map<string, TableInfo>;
enableSchemaMigration: boolean;
}

export interface LoginTableInfo {
Expand Down Expand Up @@ -370,6 +371,7 @@ export async function collectTargetDatabaseInfo(
isReadOnly: getSqlBoolean(row[7]),
sourceTables: new Map(),
targetTables: new Map(),
enableSchemaMigration: false,
};
}) ?? [];
}
Expand Down
96 changes: 95 additions & 1 deletion extensions/sql-migration/src/api/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes } from '../constants/helper';
import { getMigrationMode, getMigrationStatus, getSchemaMigrationStatus, getMigrationTargetType, hasRestoreBlockingReason, PipelineStatusCodes, getMigrationType } from '../constants/helper';
import * as os from 'os';
import * as styles from '../constants/styles';
import { SqlMigrationService, getSqlMigrationServiceAuthKeys, regenerateSqlMigrationServiceAuthKey } from './azure';
Expand Down Expand Up @@ -178,6 +178,7 @@ 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)
Expand Down Expand Up @@ -399,6 +400,93 @@ 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',
Expand Down Expand Up @@ -1169,3 +1257,9 @@ export async function clearDropDown(dropDown: DropDownComponent): Promise<void>
await dropDown.updateProperty('value', undefined);
await dropDown.updateProperty('values', []);
}

export async function clearDropDownWithLoading(dropDown: DropDownComponent, placeHolder: string | undefined = undefined): Promise<void> {
dropDown.loading = true;
await dropDown.updateProperties({ 'values': [], 'value': undefined, 'placeholder': placeHolder });

}
23 changes: 23 additions & 0 deletions extensions/sql-migration/src/constants/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,20 @@ 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
Expand Down Expand Up @@ -242,11 +256,20 @@ 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 ?? '';
Expand Down
Loading

0 comments on commit f70ff23

Please sign in to comment.