mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-03 17:23:42 -05:00
Add SQL DB offline migration wizard experience (#20403)
* sql db wizard with target selection * add database table selection * add sqldb to service and IR page * Code complete * navigation bug fixes * fix target db selection * improve sqldb error and status reporting * fix error count bug * remove table status inference * address review feedback * update resource strings and content * fix migraton status string, use localized value * fix ux navigation issues * fix back/fwd w/o changes from changing data
This commit is contained in:
@@ -5,82 +5,121 @@
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as vscode from 'vscode';
|
||||
import * as mssql from 'mssql';
|
||||
import { promises as fs } from 'fs';
|
||||
import { DatabaseMigration, getMigrationDetails } from '../api/azure';
|
||||
import { MenuCommands, SqlMigrationExtensionId } from '../api/utils';
|
||||
import { canCancelMigration, canRetryMigration } from '../constants/helper';
|
||||
import { IconPathHelper } from '../constants/iconPathHelper';
|
||||
import { MigrationNotebookInfo, NotebookPathHelper } from '../constants/notebookPathHelper';
|
||||
import * as loc from '../constants/strings';
|
||||
import { SavedAssessmentDialog } from '../dialog/assessmentResults/savedAssessmentDialog';
|
||||
import { ConfirmCutoverDialog } from '../dialog/migrationCutover/confirmCutoverDialog';
|
||||
import { MigrationCutoverDialogModel } from '../dialog/migrationCutover/migrationCutoverDialogModel';
|
||||
import { RetryMigrationDialog } from '../dialog/retryMigration/retryMigrationDialog';
|
||||
import { SqlMigrationServiceDetailsDialog } from '../dialog/sqlMigrationService/sqlMigrationServiceDetailsDialog';
|
||||
import { MigrationLocalStorage } from '../models/migrationLocalStorage';
|
||||
import { MigrationStateModel, SavedInfo } from '../models/stateMachine';
|
||||
import { logError, TelemetryViews } from '../telemtery';
|
||||
import { WizardController } from '../wizard/wizardController';
|
||||
import { DashboardStatusBar, ErrorEvent } from './DashboardStatusBar';
|
||||
import { DashboardTab } from './dashboardTab';
|
||||
import { MigrationsTab, MigrationsTabId } from './migrationsTab';
|
||||
import { AdsMigrationStatus } from './tabBase';
|
||||
import { AdsMigrationStatus, MigrationDetailsEvent, ServiceContextChangeEvent } from './tabBase';
|
||||
|
||||
export interface DashboardStatusBar {
|
||||
showError: (errorTitle: string, errorLable: string, errorDescription: string) => Promise<void>;
|
||||
clearError: () => Promise<void>;
|
||||
errorTitle: string;
|
||||
errorLabel: string;
|
||||
errorDescription: string;
|
||||
export interface MenuCommandArgs {
|
||||
connectionId: string,
|
||||
migrationId: string,
|
||||
migrationOperationId: string,
|
||||
}
|
||||
|
||||
export class DashboardWidget implements DashboardStatusBar {
|
||||
private _context: vscode.ExtensionContext;
|
||||
private _view!: azdata.ModelView;
|
||||
private _tabs!: azdata.TabbedPanelComponent;
|
||||
private _statusInfoBox!: azdata.InfoBoxComponent;
|
||||
private _dashboardTab!: DashboardTab;
|
||||
private _migrationsTab!: MigrationsTab;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
export class DashboardWidget {
|
||||
public stateModel!: MigrationStateModel;
|
||||
private readonly _context: vscode.ExtensionContext;
|
||||
private readonly _onServiceContextChanged: vscode.EventEmitter<ServiceContextChangeEvent>;
|
||||
private readonly _migrationDetailsEvent: vscode.EventEmitter<MigrationDetailsEvent>;
|
||||
private readonly _errorEvent: vscode.EventEmitter<ErrorEvent>;
|
||||
|
||||
constructor(context: vscode.ExtensionContext) {
|
||||
this._context = context;
|
||||
NotebookPathHelper.setExtensionContext(context);
|
||||
IconPathHelper.setExtensionContext(context);
|
||||
MigrationLocalStorage.setExtensionContext(context);
|
||||
|
||||
this._onServiceContextChanged = new vscode.EventEmitter<ServiceContextChangeEvent>();
|
||||
this._errorEvent = new vscode.EventEmitter<ErrorEvent>();
|
||||
this._migrationDetailsEvent = new vscode.EventEmitter<MigrationDetailsEvent>();
|
||||
|
||||
context.subscriptions.push(this._onServiceContextChanged);
|
||||
context.subscriptions.push(this._errorEvent);
|
||||
context.subscriptions.push(this._migrationDetailsEvent);
|
||||
}
|
||||
|
||||
public errorTitle: string = '';
|
||||
public errorLabel: string = '';
|
||||
public errorDescription: string = '';
|
||||
public async register(): Promise<void> {
|
||||
await this._registerCommands();
|
||||
|
||||
public async showError(errorTitle: string, errorLabel: string, errorDescription: string): Promise<void> {
|
||||
this.errorTitle = errorTitle;
|
||||
this.errorLabel = errorLabel;
|
||||
this.errorDescription = errorDescription;
|
||||
this._statusInfoBox.style = 'error';
|
||||
this._statusInfoBox.text = errorTitle;
|
||||
await this._updateStatusDisplay(this._statusInfoBox, true);
|
||||
}
|
||||
|
||||
public async clearError(): Promise<void> {
|
||||
await this._updateStatusDisplay(this._statusInfoBox, false);
|
||||
this.errorTitle = '';
|
||||
this.errorLabel = '';
|
||||
this.errorDescription = '';
|
||||
this._statusInfoBox.style = 'success';
|
||||
this._statusInfoBox.text = '';
|
||||
}
|
||||
|
||||
public register(): void {
|
||||
azdata.ui.registerModelViewProvider('migration-dashboard', async (view) => {
|
||||
this._view = view;
|
||||
this._disposables.push(
|
||||
this._view.onClosed(e => {
|
||||
this._disposables.forEach(
|
||||
d => { try { d.dispose(); } catch { } });
|
||||
}));
|
||||
const disposables: vscode.Disposable[] = [];
|
||||
const _view = view;
|
||||
|
||||
const statusInfoBox = view.modelBuilder.infoBox()
|
||||
.withProps({
|
||||
style: 'error',
|
||||
text: '',
|
||||
clickableButtonAriaLabel: loc.ERROR_DIALOG_ARIA_CLICK_VIEW_ERROR_DETAILS,
|
||||
announceText: true,
|
||||
isClickable: true,
|
||||
display: 'none',
|
||||
CSSStyles: { 'font-size': '14px', 'display': 'none', },
|
||||
}).component();
|
||||
|
||||
const connectionProfile = await azdata.connection.getCurrentConnection();
|
||||
const statusBar = new DashboardStatusBar(
|
||||
this._context,
|
||||
connectionProfile.connectionId,
|
||||
statusInfoBox,
|
||||
this._errorEvent);
|
||||
|
||||
disposables.push(
|
||||
statusInfoBox.onDidClick(
|
||||
async e => await statusBar.openErrorDialog()));
|
||||
|
||||
disposables.push(
|
||||
_view.onClosed(e =>
|
||||
disposables.forEach(
|
||||
d => { try { d.dispose(); } catch { } })));
|
||||
|
||||
const openMigrationFcn = async (filter: AdsMigrationStatus): Promise<void> => {
|
||||
this._tabs.selectTab(MigrationsTabId);
|
||||
await this._migrationsTab.setMigrationFilter(filter);
|
||||
if (!migrationsTabInitialized) {
|
||||
migrationsTabInitialized = true;
|
||||
tabs.selectTab(MigrationsTabId);
|
||||
await migrationsTab.setMigrationFilter(AdsMigrationStatus.ALL);
|
||||
await migrationsTab.refresh();
|
||||
await migrationsTab.setMigrationFilter(filter);
|
||||
} else {
|
||||
const promise = migrationsTab.setMigrationFilter(filter);
|
||||
tabs.selectTab(MigrationsTabId);
|
||||
await promise;
|
||||
}
|
||||
};
|
||||
|
||||
this._dashboardTab = await new DashboardTab().create(
|
||||
const dashboardTab = await new DashboardTab().create(
|
||||
view,
|
||||
async (filter: AdsMigrationStatus) => await openMigrationFcn(filter),
|
||||
this);
|
||||
this._disposables.push(this._dashboardTab);
|
||||
this._onServiceContextChanged,
|
||||
statusBar);
|
||||
disposables.push(dashboardTab);
|
||||
|
||||
this._migrationsTab = await new MigrationsTab().create(
|
||||
const migrationsTab = await new MigrationsTab().create(
|
||||
this._context,
|
||||
view,
|
||||
this);
|
||||
this._disposables.push(this._migrationsTab);
|
||||
this._onServiceContextChanged,
|
||||
this._migrationDetailsEvent,
|
||||
statusBar);
|
||||
disposables.push(migrationsTab);
|
||||
|
||||
this._tabs = view.modelBuilder.tabbedPanel()
|
||||
.withTabs([this._dashboardTab, this._migrationsTab])
|
||||
const tabs = view.modelBuilder.tabbedPanel()
|
||||
.withTabs([dashboardTab, migrationsTab])
|
||||
.withLayout({ alwaysShowTabs: true, orientation: azdata.TabOrientation.Horizontal })
|
||||
.withProps({
|
||||
CSSStyles: {
|
||||
@@ -91,107 +130,338 @@ export class DashboardWidget implements DashboardStatusBar {
|
||||
})
|
||||
.component();
|
||||
|
||||
this._disposables.push(
|
||||
this._tabs.onTabChanged(
|
||||
async id => {
|
||||
await this.clearError();
|
||||
await this.onDialogClosed();
|
||||
}));
|
||||
|
||||
this._statusInfoBox = view.modelBuilder.infoBox()
|
||||
.withProps({
|
||||
style: 'error',
|
||||
text: '',
|
||||
announceText: true,
|
||||
isClickable: true,
|
||||
display: 'none',
|
||||
CSSStyles: { 'font-size': '14px' },
|
||||
}).component();
|
||||
|
||||
this._disposables.push(
|
||||
this._statusInfoBox.onDidClick(
|
||||
async e => await this.openErrorDialog()));
|
||||
let migrationsTabInitialized = false;
|
||||
disposables.push(
|
||||
tabs.onTabChanged(async tabId => {
|
||||
const connectionProfile = await azdata.connection.getCurrentConnection();
|
||||
await this.clearError(connectionProfile.connectionId);
|
||||
if (tabId === MigrationsTabId && !migrationsTabInitialized) {
|
||||
migrationsTabInitialized = true;
|
||||
await migrationsTab.refresh();
|
||||
}
|
||||
}));
|
||||
|
||||
const flexContainer = view.modelBuilder.flexContainer()
|
||||
.withLayout({ flexFlow: 'column' })
|
||||
.withItems([this._statusInfoBox, this._tabs])
|
||||
.withItems([statusInfoBox, tabs])
|
||||
.component();
|
||||
await view.initializeModel(flexContainer);
|
||||
|
||||
await this.refresh();
|
||||
await dashboardTab.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
public async refresh(): Promise<void> {
|
||||
void this._migrationsTab.refresh();
|
||||
await this._dashboardTab.refresh();
|
||||
}
|
||||
private async _registerCommands(): Promise<void> {
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.Cutover,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
await this.clearError(args.connectionId);
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
if (canRetryMigration(migration)) {
|
||||
const cutoverDialogModel = new MigrationCutoverDialogModel(
|
||||
await MigrationLocalStorage.getMigrationServiceContext(),
|
||||
migration!);
|
||||
await cutoverDialogModel.fetchStatus();
|
||||
const dialog = new ConfirmCutoverDialog(cutoverDialogModel);
|
||||
await dialog.initialize();
|
||||
if (cutoverDialogModel.CutoverError) {
|
||||
void vscode.window.showErrorMessage(loc.MIGRATION_CUTOVER_ERROR);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.Cutover, cutoverDialogModel.CutoverError);
|
||||
}
|
||||
} else {
|
||||
await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_CUTOVER);
|
||||
}
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.MIGRATION_CUTOVER_ERROR,
|
||||
loc.MIGRATION_CUTOVER_ERROR,
|
||||
e.message);
|
||||
|
||||
public async onDialogClosed(): Promise<void> {
|
||||
await this._dashboardTab.onDialogClosed();
|
||||
await this._migrationsTab.onDialogClosed();
|
||||
}
|
||||
|
||||
private _errorDialogIsOpen: boolean = false;
|
||||
|
||||
protected async openErrorDialog(): Promise<void> {
|
||||
if (this._errorDialogIsOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tab = azdata.window.createTab(this.errorTitle);
|
||||
tab.registerContent(async (view) => {
|
||||
const flex = view.modelBuilder.flexContainer()
|
||||
.withItems([
|
||||
view.modelBuilder.text()
|
||||
.withProps({ value: this.errorLabel, CSSStyles: { 'margin': '0px 0px 5px 5px' } })
|
||||
.component(),
|
||||
view.modelBuilder.inputBox()
|
||||
.withProps({
|
||||
value: this.errorDescription,
|
||||
readOnly: true,
|
||||
multiline: true,
|
||||
inputType: 'text',
|
||||
rows: 20,
|
||||
CSSStyles: { 'overflow': 'hidden auto', 'margin': '0px 0px 0px 5px' },
|
||||
})
|
||||
.component()
|
||||
])
|
||||
.withLayout({
|
||||
flexFlow: 'column',
|
||||
width: 420,
|
||||
})
|
||||
.withProps({ CSSStyles: { 'margin': '0 10px 0 10px' } })
|
||||
.component();
|
||||
|
||||
await view.initializeModel(flex);
|
||||
});
|
||||
|
||||
const dialog = azdata.window.createModelViewDialog(
|
||||
this.errorTitle,
|
||||
'errorDialog',
|
||||
450,
|
||||
'flyout');
|
||||
dialog.content = [tab];
|
||||
dialog.okButton.label = loc.ERROR_DIALOG_CLEAR_BUTTON_LABEL;
|
||||
dialog.okButton.focused = true;
|
||||
dialog.cancelButton.label = loc.CLOSE;
|
||||
this._disposables.push(
|
||||
dialog.onClosed(async e => {
|
||||
if (e === 'ok') {
|
||||
await this.clearError();
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.Cutover, e);
|
||||
}
|
||||
this._errorDialogIsOpen = false;
|
||||
}));
|
||||
|
||||
azdata.window.openDialog(dialog);
|
||||
} catch (error) {
|
||||
this._errorDialogIsOpen = false;
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.ViewDatabase,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
await this.clearError(args.connectionId);
|
||||
this._migrationDetailsEvent.fire({
|
||||
connectionId: args.connectionId,
|
||||
migrationId: args.migrationId,
|
||||
migrationOperationId: args.migrationOperationId,
|
||||
});
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.OPEN_MIGRATION_DETAILS_ERROR,
|
||||
loc.OPEN_MIGRATION_DETAILS_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.ViewDatabase, e);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.ViewTarget,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
const url = 'https://portal.azure.com/#resource/' + migration!.properties.scope;
|
||||
await vscode.env.openExternal(vscode.Uri.parse(url));
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.OPEN_MIGRATION_TARGET_ERROR,
|
||||
loc.OPEN_MIGRATION_TARGET_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.ViewTarget, e);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.ViewService,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
await this.clearError(args.connectionId);
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
const dialog = new SqlMigrationServiceDetailsDialog(
|
||||
await MigrationLocalStorage.getMigrationServiceContext(),
|
||||
migration!);
|
||||
await dialog.initialize();
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.OPEN_MIGRATION_SERVICE_ERROR,
|
||||
loc.OPEN_MIGRATION_SERVICE_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.ViewService, e);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.CopyMigration,
|
||||
async (args: MenuCommandArgs) => {
|
||||
await this.clearError(args.connectionId);
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
if (migration) {
|
||||
const cutoverDialogModel = new MigrationCutoverDialogModel(
|
||||
await MigrationLocalStorage.getMigrationServiceContext(),
|
||||
migration);
|
||||
try {
|
||||
await cutoverDialogModel.fetchStatus();
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.MIGRATION_STATUS_REFRESH_ERROR,
|
||||
loc.MIGRATION_STATUS_REFRESH_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.CopyMigration, e);
|
||||
}
|
||||
|
||||
await vscode.env.clipboard.writeText(JSON.stringify(cutoverDialogModel.migration, undefined, 2));
|
||||
await vscode.window.showInformationMessage(loc.DETAILS_COPIED);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(vscode.commands.registerCommand(
|
||||
MenuCommands.CancelMigration,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
await this.clearError(args.connectionId);
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
if (canCancelMigration(migration)) {
|
||||
void vscode.window.showInformationMessage(loc.CANCEL_MIGRATION_CONFIRMATION, loc.YES, loc.NO)
|
||||
.then(async (v) => {
|
||||
if (v === loc.YES) {
|
||||
const cutoverDialogModel = new MigrationCutoverDialogModel(
|
||||
await MigrationLocalStorage.getMigrationServiceContext(),
|
||||
migration!);
|
||||
await cutoverDialogModel.fetchStatus();
|
||||
await cutoverDialogModel.cancelMigration();
|
||||
|
||||
if (cutoverDialogModel.CancelMigrationError) {
|
||||
void vscode.window.showErrorMessage(loc.MIGRATION_CANNOT_CANCEL);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.CancelMigration, cutoverDialogModel.CancelMigrationError);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_CANCEL);
|
||||
}
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.MIGRATION_CANCELLATION_ERROR,
|
||||
loc.MIGRATION_CANCELLATION_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.CancelMigration, e);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.RetryMigration,
|
||||
async (args: MenuCommandArgs) => {
|
||||
try {
|
||||
await this.clearError(args.connectionId);
|
||||
const migration = await this._getMigrationById(args.migrationId, args.migrationOperationId);
|
||||
if (canRetryMigration(migration)) {
|
||||
const retryMigrationDialog = new RetryMigrationDialog(
|
||||
this._context,
|
||||
await MigrationLocalStorage.getMigrationServiceContext(),
|
||||
migration!,
|
||||
this._onServiceContextChanged);
|
||||
await retryMigrationDialog.openDialog();
|
||||
}
|
||||
else {
|
||||
await vscode.window.showInformationMessage(loc.MIGRATION_CANNOT_RETRY);
|
||||
}
|
||||
} catch (e) {
|
||||
await this.showError(
|
||||
args.connectionId,
|
||||
loc.MIGRATION_RETRY_ERROR,
|
||||
loc.MIGRATION_RETRY_ERROR,
|
||||
e.message);
|
||||
logError(TelemetryViews.MigrationsTab, MenuCommands.RetryMigration, e);
|
||||
}
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.StartMigration,
|
||||
async () => await this.launchMigrationWizard()));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
vscode.commands.registerCommand(
|
||||
MenuCommands.OpenNotebooks,
|
||||
async () => {
|
||||
const input = vscode.window.createQuickPick<MigrationNotebookInfo>();
|
||||
input.placeholder = loc.NOTEBOOK_QUICK_PICK_PLACEHOLDER;
|
||||
input.items = NotebookPathHelper.getAllMigrationNotebooks();
|
||||
|
||||
this._context.subscriptions.push(
|
||||
input.onDidAccept(async (e) => {
|
||||
const selectedNotebook = input.selectedItems[0];
|
||||
if (selectedNotebook) {
|
||||
try {
|
||||
await azdata.nb.showNotebookDocument(vscode.Uri.parse(`untitled: ${selectedNotebook.label}`), {
|
||||
preview: false,
|
||||
initialContent: (await fs.readFile(selectedNotebook.notebookPath)).toString(),
|
||||
initialDirtyState: false
|
||||
});
|
||||
} catch (e) {
|
||||
void vscode.window.showErrorMessage(`${loc.NOTEBOOK_OPEN_ERROR} - ${e.toString()}`);
|
||||
}
|
||||
input.hide();
|
||||
}
|
||||
}));
|
||||
|
||||
input.show();
|
||||
}));
|
||||
|
||||
this._context.subscriptions.push(azdata.tasks.registerTask(
|
||||
MenuCommands.StartMigration,
|
||||
async () => await this.launchMigrationWizard()));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
azdata.tasks.registerTask(
|
||||
MenuCommands.NewSupportRequest,
|
||||
async () => await this.launchNewSupportRequest()));
|
||||
|
||||
this._context.subscriptions.push(
|
||||
azdata.tasks.registerTask(
|
||||
MenuCommands.SendFeedback,
|
||||
async () => {
|
||||
const actionId = MenuCommands.IssueReporter;
|
||||
const args = {
|
||||
extensionId: SqlMigrationExtensionId,
|
||||
issueTitle: loc.FEEDBACK_ISSUE_TITLE,
|
||||
};
|
||||
return await vscode.commands.executeCommand(actionId, args);
|
||||
}));
|
||||
}
|
||||
|
||||
private async clearError(connectionId: string): Promise<void> {
|
||||
this._errorEvent.fire({
|
||||
connectionId: connectionId,
|
||||
title: '',
|
||||
label: '',
|
||||
message: '',
|
||||
});
|
||||
}
|
||||
|
||||
private async showError(connectionId: string, title: string, label: string, message: string): Promise<void> {
|
||||
this._errorEvent.fire({
|
||||
connectionId: connectionId,
|
||||
title: title,
|
||||
label: label,
|
||||
message: message,
|
||||
});
|
||||
}
|
||||
|
||||
private async _getMigrationById(migrationId: string, migrationOperationId: string): Promise<DatabaseMigration | undefined> {
|
||||
const context = await MigrationLocalStorage.getMigrationServiceContext();
|
||||
if (context.azureAccount && context.subscription) {
|
||||
return getMigrationDetails(
|
||||
context.azureAccount,
|
||||
context.subscription,
|
||||
migrationId,
|
||||
migrationOperationId);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async launchMigrationWizard(): Promise<void> {
|
||||
const activeConnection = await azdata.connection.getCurrentConnection();
|
||||
let connectionId: string = '';
|
||||
let serverName: string = '';
|
||||
if (!activeConnection) {
|
||||
const connection = await azdata.connection.openConnectionDialog();
|
||||
if (connection) {
|
||||
connectionId = connection.connectionId;
|
||||
serverName = connection.options.server;
|
||||
}
|
||||
} else {
|
||||
connectionId = activeConnection.connectionId;
|
||||
serverName = activeConnection.serverName;
|
||||
}
|
||||
if (serverName) {
|
||||
const api = (await vscode.extensions.getExtension(mssql.extension.name)?.activate()) as mssql.IExtension;
|
||||
if (api) {
|
||||
this.stateModel = new MigrationStateModel(this._context, connectionId, api.sqlMigration);
|
||||
this._context.subscriptions.push(this.stateModel);
|
||||
const savedInfo = this.checkSavedInfo(serverName);
|
||||
if (savedInfo) {
|
||||
this.stateModel.savedInfo = savedInfo;
|
||||
this.stateModel.serverName = serverName;
|
||||
const savedAssessmentDialog = new SavedAssessmentDialog(
|
||||
this._context,
|
||||
this.stateModel,
|
||||
this._onServiceContextChanged);
|
||||
await savedAssessmentDialog.openDialog();
|
||||
} else {
|
||||
const wizardController = new WizardController(
|
||||
this._context,
|
||||
this.stateModel,
|
||||
this._onServiceContextChanged);
|
||||
await wizardController.openWizard(connectionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _updateStatusDisplay(control: azdata.Component, visible: boolean): Promise<void> {
|
||||
await control.updateCssStyles({ 'display': visible ? 'inline' : 'none' });
|
||||
private checkSavedInfo(serverName: string): SavedInfo | undefined {
|
||||
return this._context.globalState.get<SavedInfo>(`${this.stateModel.mementoString}.${serverName}`);
|
||||
}
|
||||
|
||||
public async launchNewSupportRequest(): Promise<void> {
|
||||
await vscode.env.openExternal(vscode.Uri.parse(
|
||||
`https://portal.azure.com/#blade/Microsoft_Azure_Support/HelpAndSupportBlade/newsupportrequest`));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user