mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
[Feature] SKU recommendations in SQL migration extension (#18252)
* Initial check in for SQL migration SKU recommendation feature (#18116) Co-authored-by: Raymond Truong <ratruong@microsoft.com> * add TargetSelectionPage, remove AccountSelectionPage, fix saveAndClose bugs (#18092) * update sku interfaces (#18150) * create the skuRecommendationResultsDialog (#18151) * add TargetSelectionPage, remove AccountSelectionPage, fix saveAndClose bugs * create skuRecommendationResultsDialog * Replace placeholder SKU recommendation results with actual results (#18153) * Replace placeholder SKU recommendation results with actual backend call results * Remove skuRecommendationExample * Replace number fields in interfaces with correct enums, and update UI text * add getAzureRecommendationDialog for performance collection (#18159) * add getAzureRecommendationDialog when there are no recommendations available * update 'get azure rec' / 'view details' link values * add condition to check if recommendations are available * Implement start/stop perf data collection + import perf data into new UI (#18149) * Implement start/stop perf data collection * add getAzureRecommendationDialog when there are no recommendations available * update 'get azure rec' / 'view details' link values * add condition to check if recommendations are available * Implement import existing data + start/stop perf collection with new UI Co-authored-by: Rachel Kim <rackim@microsoft.com> * Expose SqlInstanceRequirements in SKU recommendation results (#18207) * Expose SqlInstanceRequirements * Move string literals to constants file * Fix formatting in mssql.d.ts * create storage properties table (#18215) * Edit sku recommendation parameters (#18244) * Edit sku recommendation parameters * make _targetPercentileDropdown not editable; styling updates * Azure recommendation section exposes data collection status and stop option (#18246) * Edit sku recommendation parameters * create azure recommendation details section on sku page * Improve error handling + add auto refresh + other small changes (#18228) * Update source properties table * WIP - refresh perf data collection * Add auto refresh logic * Address comments Co-authored-by: Rachel Kim <rackim@microsoft.com> * Show/hide azure recommendation components based on data collection source and status (#18254) * add refresh recommendation button; show/hide content based on perf collection status * show/hide azure rec content based on perf data source scenarios * add popups for start/stop; allow user to restart data collection; add perf collection to save close; add info tooltips (#18278) * Update SKU recommendation timer logic (#18281) * Update timer logic * Fix misc UI bugs * update sql migration extension readme (#18295) * Remove empty constant, as this may have broken the build * Fix 'save and close' behavior for SKU recommendation (#18301) * Update timer logic * Fix misc UI bugs * 'WIP' * Add logic to restore correct SKU recommendation state when reloading * SKU UX enhancements - status info, button validations, savedInfo logic (#18320) * add stop/inprogress status icons to perf collection status text, update restart icon * refactor savedInfo as an interface, edit parameter recommednations are saved, add open folder inputbox validation, handle no recommendations available scenario * fix getazureredialog bug, cleanup cold * nit card styling * Update recommendations whenever user changes list of databases to assess + misc clean up (#18323) * Consolidate constants, clean up redundant functions, misc clean up * Remove old SKU recommendation interfaces * Update some more strings * Telemetry events for SKU Recommendation (#18282) * Telemetry events for SKU Recommendation * Addressing comments - 1) fixed camel casing 2) removed extra logging to console 3) added telemetry for subid, rg, tenantid on targetselectionpage * Resolving conflicts * Addressing comments - 1) removing filename 2) moving all numbers to measurements. * Resolving comment - Fixing telemetry value for tenant id. * removing warning 'logError' is declared but its value is never read (#18333) * Stop existing data collection when leaving and starting a new migration + update timers (#18339) * Refresh recommendations when pressing stop data collection button * Fix orphaned data collection when save and closing and starting a new migration * Revert "Refresh recommendations when pressing stop data collection button" This reverts commit e6fb2ade8f8a41952adb81cb0ee852414dfa4ef2. * Update timers to use production values * Remove unused import * address bug bash issues: add learn more link, add last refreshed time, fix vm card view detail open issue, remember last selected folder, remove strings, refactor refresh logic on sku page (#18340) * Address comments * Update to sqltoolsservice 3.0.0-release.204 Co-authored-by: Rachel Kim <rackim@microsoft.com> Co-authored-by: Neetu Singh <23.neetu@gmail.com>
This commit is contained in:
@@ -1,266 +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 { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, Page, StateChangeEvent } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils';
|
||||
import { getSubscriptions } from '../api/azure';
|
||||
import * as styles from '../constants/styles';
|
||||
|
||||
export class AccountsSelectionPage extends MigrationWizardPage {
|
||||
private _azureAccountsDropdown!: azdata.DropDownComponent;
|
||||
private _accountTenantDropdown!: azdata.DropDownComponent;
|
||||
private _accountTenantFlexContainer!: azdata.FlexContainer;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.ACCOUNTS_SELECTION_PAGE_TITLE), migrationStateModel);
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
const pageDescription = {
|
||||
title: '',
|
||||
component: view.modelBuilder.text().withProps({
|
||||
value: constants.ACCOUNTS_SELECTION_PAGE_DESCRIPTION,
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS,
|
||||
'margin': '0',
|
||||
}
|
||||
}).component()
|
||||
};
|
||||
|
||||
this.wizard.customButtons[0].enabled = true;
|
||||
const form = view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
pageDescription,
|
||||
await this.createAzureAccountsDropdown(view),
|
||||
await this.createAzureTenantContainer(view),
|
||||
]
|
||||
).withProps({
|
||||
CSSStyles: {
|
||||
'padding-top': '0'
|
||||
}
|
||||
}).component();
|
||||
await view.initializeModel(form);
|
||||
await this.populateAzureAccountsDropdown();
|
||||
this._disposables.push(view.onClosed(e =>
|
||||
this._disposables.forEach(
|
||||
d => { try { d.dispose(); } catch { } })));
|
||||
}
|
||||
|
||||
private createAzureAccountsDropdown(view: azdata.ModelView): azdata.FormComponent {
|
||||
|
||||
const azureAccountLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._azureAccountsDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
ariaLabel: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
fireOnTextChange: true,
|
||||
})
|
||||
.withValidation((c) => {
|
||||
if (c.value) {
|
||||
if ((<azdata.CategoryValue>c.value)?.displayName === constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR) {
|
||||
this.wizard.message = {
|
||||
text: constants.ACCOUNT_SELECTION_PAGE_NO_LINKED_ACCOUNTS_ERROR,
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
return false;
|
||||
}
|
||||
if (this.migrationStateModel._azureAccount?.isStale) {
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount)
|
||||
};
|
||||
return false;
|
||||
}
|
||||
this.wizard.message = {
|
||||
text: ''
|
||||
};
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).component();
|
||||
|
||||
this._disposables.push(this._azureAccountsDropdown.onValueChanged(async (value) => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureAccountsDropdown, value);
|
||||
if (selectedIndex > -1) {
|
||||
const selectedAzureAccount = this.migrationStateModel.getAccount(selectedIndex);
|
||||
// Making a clone of the account object to preserve the original tenants
|
||||
this.migrationStateModel._azureAccount = deepClone(selectedAzureAccount);
|
||||
if (this.migrationStateModel._azureAccount.properties.tenants.length > 1) {
|
||||
this.migrationStateModel._accountTenants = selectedAzureAccount.properties.tenants;
|
||||
this._accountTenantDropdown.values = await this.migrationStateModel.getTenantValues();
|
||||
selectDropDownIndex(this._accountTenantDropdown, 0);
|
||||
await this._accountTenantFlexContainer.updateCssStyles({
|
||||
'display': 'inline'
|
||||
});
|
||||
} else {
|
||||
await this._accountTenantFlexContainer.updateCssStyles({
|
||||
'display': 'none'
|
||||
});
|
||||
if (this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.AzureAccount)) {
|
||||
(<azdata.CategoryValue[]>this._azureAccountsDropdown.values)?.forEach((account, index) => {
|
||||
if (account.name.toLowerCase() === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureAccountsDropdown, index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
if (!(this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.Summary)) {
|
||||
this.migrationStateModel._subscriptions = undefined!;
|
||||
this.migrationStateModel._targetSubscription = undefined!;
|
||||
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
||||
}
|
||||
await this._azureAccountsDropdown.validate();
|
||||
}
|
||||
}));
|
||||
|
||||
const linkAccountButton = view.modelBuilder.hyperlink()
|
||||
.withProps({
|
||||
label: constants.ACCOUNT_LINK_BUTTON_LABEL,
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS
|
||||
}
|
||||
})
|
||||
.component();
|
||||
|
||||
this._disposables.push(linkAccountButton.onDidClick(async (event) => {
|
||||
await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount');
|
||||
await this.populateAzureAccountsDropdown();
|
||||
this.wizard.message = {
|
||||
text: ''
|
||||
};
|
||||
await this._azureAccountsDropdown.validate();
|
||||
}));
|
||||
|
||||
const flexContainer = view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column'
|
||||
})
|
||||
.withItems([
|
||||
azureAccountLabel,
|
||||
this._azureAccountsDropdown,
|
||||
linkAccountButton
|
||||
])
|
||||
.component();
|
||||
|
||||
return {
|
||||
title: '',
|
||||
component: flexContainer
|
||||
};
|
||||
}
|
||||
|
||||
private createAzureTenantContainer(view: azdata.ModelView): azdata.FormComponent {
|
||||
|
||||
const azureTenantDropdownLabel = view.modelBuilder.text().withProps({
|
||||
value: constants.AZURE_TENANT,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._accountTenantDropdown = view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.AZURE_TENANT,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
fireOnTextChange: true,
|
||||
}).component();
|
||||
|
||||
this._disposables.push(this._accountTenantDropdown.onValueChanged(value => {
|
||||
/**
|
||||
* Replacing all the tenants in azure account with the tenant user has selected.
|
||||
* All azure requests will only run on this tenant from now on
|
||||
*/
|
||||
const selectedIndex = findDropDownItemIndex(this._accountTenantDropdown, value);
|
||||
const selectedTenant = this.migrationStateModel.getTenant(selectedIndex);
|
||||
this.migrationStateModel._azureTenant = deepClone(selectedTenant);
|
||||
if (selectedIndex > -1) {
|
||||
this.migrationStateModel._azureAccount.properties.tenants = [this.migrationStateModel.getTenant(selectedIndex)];
|
||||
this.migrationStateModel._subscriptions = undefined!;
|
||||
this.migrationStateModel._targetSubscription = undefined!;
|
||||
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
this._accountTenantFlexContainer = view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column'
|
||||
})
|
||||
.withItems([
|
||||
azureTenantDropdownLabel,
|
||||
this._accountTenantDropdown
|
||||
])
|
||||
.withProps({
|
||||
CSSStyles: {
|
||||
'display': 'none'
|
||||
}
|
||||
})
|
||||
.component();
|
||||
|
||||
return {
|
||||
title: '',
|
||||
component: this._accountTenantFlexContainer
|
||||
};
|
||||
}
|
||||
|
||||
private async populateAzureAccountsDropdown(): Promise<void> {
|
||||
this._azureAccountsDropdown.loading = true;
|
||||
try {
|
||||
this._azureAccountsDropdown.values = await this.migrationStateModel.getAccountValues();
|
||||
} finally {
|
||||
this._azureAccountsDropdown.loading = false;
|
||||
}
|
||||
|
||||
selectDropDownIndex(this._azureAccountsDropdown, 0);
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator(async pageChangeInfo => {
|
||||
try {
|
||||
this.wizard.message = { text: '', };
|
||||
|
||||
if (this.migrationStateModel._azureAccount && !this.migrationStateModel._azureAccount?.isStale) {
|
||||
const subscriptions = await getSubscriptions(this.migrationStateModel._azureAccount);
|
||||
if (subscriptions?.length > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: constants.ACCOUNT_STALE_ERROR(this.migrationStateModel._azureAccount),
|
||||
};
|
||||
} catch (error) {
|
||||
this.wizard.message = {
|
||||
level: azdata.window.MessageLevel.Error,
|
||||
text: constants.ACCOUNT_ACCESS_ERROR(this.migrationStateModel._azureAccount, error),
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.SOURCE_CONFIGURATION, 'MigrationModePage'), migrationStateModel);
|
||||
super(wizard, azdata.window.createWizardPage(constants.DATABASE_FOR_ASSESSMENT_PAGE_TITLE), migrationStateModel);
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
@@ -87,6 +87,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<void> {
|
||||
const assessedDatabases = this.migrationStateModel._databaseAssessment ?? [];
|
||||
const selectedDatabases = this.selectedDbs();
|
||||
@@ -202,16 +203,8 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
this._dbNames.push(finalResult[index].options.name);
|
||||
}
|
||||
|
||||
const title = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_FOR_MIGRATION,
|
||||
CSSStyles: {
|
||||
...styles.PAGE_TITLE_CSS,
|
||||
'margin-bottom': '8px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const text = this._view.modelBuilder.text().withProps({
|
||||
value: constants.DATABASE_MIGRATE_TEXT,
|
||||
value: constants.DATABASE_FOR_ASSESSMENT_DESCRIPTION,
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS
|
||||
}
|
||||
@@ -284,14 +277,12 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
const dbName = row[1].value as string;
|
||||
if (dbName?.toLowerCase() === sourceDatabaseName?.toLowerCase()) {
|
||||
row[0].value = true;
|
||||
} else {
|
||||
row[0].enabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
await this._databaseSelectorTable.setDataValues(this._databaseTableValues);
|
||||
await this.updateValuesOnSelection();
|
||||
}
|
||||
await this.updateValuesOnSelection();
|
||||
|
||||
this._disposables.push(this._databaseSelectorTable.onDataChanged(async () => {
|
||||
await this.updateValuesOnSelection();
|
||||
@@ -304,7 +295,6 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
'margin': '0px 28px 0px 28px'
|
||||
}
|
||||
}).component();
|
||||
flex.addItem(title, { flex: '0 0 auto' });
|
||||
flex.addItem(text, { flex: '0 0 auto' });
|
||||
flex.addItem(this.createSearchComponent(), { flex: '0 0 auto' });
|
||||
flex.addItem(this._dbCount, { flex: '0 0 auto' });
|
||||
@@ -315,7 +305,7 @@ export class DatabaseSelectorPage extends MigrationWizardPage {
|
||||
|
||||
public selectedDbs(): string[] {
|
||||
let result: string[] = [];
|
||||
this._databaseSelectorTable.dataValues?.forEach((arr, index) => {
|
||||
this._databaseSelectorTable?.dataValues?.forEach((arr, index) => {
|
||||
if (arr[0].value === true) {
|
||||
result.push(this._dbNames[index]);
|
||||
}
|
||||
|
||||
@@ -50,14 +50,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
this._dmsInfoContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
this._statusLoadingComponent
|
||||
]).component();
|
||||
const dmsPortalInfo = this._view.modelBuilder.infoBox().withProps({
|
||||
text: constants.DMS_PORTAL_INFO,
|
||||
style: 'information',
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS
|
||||
},
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH
|
||||
}).component();
|
||||
|
||||
const form = view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
@@ -65,9 +57,6 @@ export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
{
|
||||
component: this.migrationServiceDropdownContainer()
|
||||
},
|
||||
{
|
||||
component: dmsPortalInfo
|
||||
},
|
||||
{
|
||||
component: this._dmsInfoContainer
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -113,8 +113,8 @@ export class SummaryPage extends MigrationWizardPage {
|
||||
await createHeadingTextComponent(this._view, constants.SOURCE_DATABASES),
|
||||
targetDatabaseRow,
|
||||
|
||||
await createHeadingTextComponent(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE),
|
||||
createInformationRow(this._view, constants.SKU_RECOMMENDATION_PAGE_TITLE, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE),
|
||||
await createHeadingTextComponent(this._view, constants.AZURE_SQL_TARGET_PAGE_TITLE),
|
||||
createInformationRow(this._view, constants.AZURE_SQL_TARGET_PAGE_TITLE, (this.migrationStateModel._targetType === MigrationTargetType.SQLVM) ? constants.SUMMARY_VM_TYPE : constants.SUMMARY_MI_TYPE),
|
||||
createInformationRow(this._view, constants.SUBSCRIPTION, this.migrationStateModel._targetSubscription.name),
|
||||
createInformationRow(this._view, constants.LOCATION, await this.migrationStateModel.getLocationDisplayName(this.migrationStateModel._targetServerInstance.location)),
|
||||
createInformationRow(this._view, constants.RESOURCE_GROUP, getResourceGroupFromId(this.migrationStateModel._targetServerInstance.id)),
|
||||
|
||||
560
extensions/sql-migration/src/wizard/targetSelectionPage.ts
Normal file
560
extensions/sql-migration/src/wizard/targetSelectionPage.ts
Normal file
@@ -0,0 +1,560 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { EOL } from 'os';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { MigrationStateModel, MigrationTargetType, Page, StateChangeEvent } from '../models/stateMachine';
|
||||
import * as constants from '../constants/strings';
|
||||
import * as styles from '../constants/styles';
|
||||
import { WIZARD_INPUT_COMPONENT_WIDTH } from './wizardController';
|
||||
import { deepClone, findDropDownItemIndex, selectDropDownIndex } from '../api/utils';
|
||||
import { sendSqlMigrationActionEvent, TelemetryAction, TelemetryViews } from '../telemtery';
|
||||
|
||||
export class TargetSelectionPage extends MigrationWizardPage {
|
||||
private _view!: azdata.ModelView;
|
||||
private _disposables: vscode.Disposable[] = [];
|
||||
|
||||
private _pageDescription!: azdata.TextComponent;
|
||||
private _azureAccountsDropdown!: azdata.DropDownComponent;
|
||||
private _accountTenantDropdown!: azdata.DropDownComponent;
|
||||
private _accountTenantFlexContainer!: azdata.FlexContainer;
|
||||
private _azureSubscriptionDropdown!: azdata.DropDownComponent;
|
||||
private _azureLocationDropdown!: azdata.DropDownComponent;
|
||||
private _azureResourceGroupDropdown!: azdata.DropDownComponent;
|
||||
private _azureResourceDropdownLabel!: azdata.TextComponent;
|
||||
private _azureResourceDropdown!: azdata.DropDownComponent;
|
||||
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.AZURE_SQL_TARGET_PAGE_TITLE), migrationStateModel);
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
this._view = view;
|
||||
|
||||
this._pageDescription = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(),
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS,
|
||||
'margin': '0'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const form = this._view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
{
|
||||
component: this._pageDescription
|
||||
},
|
||||
{
|
||||
component: this.createAzureAccountsDropdown()
|
||||
},
|
||||
{
|
||||
component: this.createAzureTenantContainer()
|
||||
},
|
||||
{
|
||||
component: this.createTargetDropdownContainer()
|
||||
}
|
||||
]
|
||||
).withProps({
|
||||
CSSStyles: {
|
||||
'padding-top': '0'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._disposables.push(this._view.onClosed(e => {
|
||||
this._disposables.forEach(
|
||||
d => { try { d.dispose(); } catch { } });
|
||||
}));
|
||||
await this._view.initializeModel(form);
|
||||
}
|
||||
|
||||
public async onPageEnter(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLMI:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_MI_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE;
|
||||
break;
|
||||
|
||||
case MigrationTargetType.SQLVM:
|
||||
this._pageDescription.value = constants.AZURE_SQL_TARGET_PAGE_DESCRIPTION(constants.SKU_RECOMMENDATION_VM_CARD_TEXT);
|
||||
this._azureResourceDropdownLabel.value = constants.AZURE_SQL_DATABASE_VIRTUAL_MACHINE;
|
||||
break;
|
||||
}
|
||||
|
||||
await this.populateAzureAccountsDropdown();
|
||||
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
const errors: string[] = [];
|
||||
this.wizard.message = {
|
||||
text: '',
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((<azdata.CategoryValue>this._azureSubscriptionDropdown.value)?.displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._azureLocationDropdown.value)?.displayName === constants.NO_LOCATION_FOUND) {
|
||||
errors.push(constants.INVALID_LOCATION_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._azureResourceGroupDropdown.value)?.displayName === constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
|
||||
const resourceDropdownValue = (<azdata.CategoryValue>this._azureResourceDropdown.value)?.displayName;
|
||||
if (resourceDropdownValue === constants.NO_MANAGED_INSTANCE_FOUND) {
|
||||
errors.push(constants.INVALID_MANAGED_INSTANCE_ERROR);
|
||||
}
|
||||
else if (resourceDropdownValue === constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||
errors.push(constants.INVALID_VIRTUAL_MACHINE_ERROR);
|
||||
}
|
||||
|
||||
if (errors.length > 0) {
|
||||
this.wizard.message = {
|
||||
text: errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public async onPageLeave(pageChangeInfo: azdata.window.WizardPageChangeInfo): Promise<void> {
|
||||
this.wizard.registerNavigationValidator((e) => {
|
||||
|
||||
sendSqlMigrationActionEvent(
|
||||
TelemetryViews.MigrationWizardTargetSelectionPage,
|
||||
TelemetryAction.OnPageLeave,
|
||||
{
|
||||
'sessionId': this.migrationStateModel?._sessionId,
|
||||
'subscriptionId': this.migrationStateModel?._targetSubscription?.id,
|
||||
'resourceGroup': this.migrationStateModel?._resourceGroup?.name,
|
||||
'tenantId': this.migrationStateModel?._azureTenant?.id || this.migrationStateModel?._azureAccount?.properties?.tenants[0]?.id
|
||||
}, {});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
|
||||
private createAzureAccountsDropdown(): azdata.FlexContainer {
|
||||
const azureAccountLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS,
|
||||
'margin-top': '-1em'
|
||||
}
|
||||
}).component();
|
||||
this._azureAccountsDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.ACCOUNTS_SELECTION_PAGE_TITLE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
required: true,
|
||||
CSSStyles: {
|
||||
'margin-top': '-1em'
|
||||
},
|
||||
}).component();
|
||||
this._disposables.push(this._azureAccountsDropdown.onValueChanged(async (value) => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureAccountsDropdown, value);
|
||||
if (selectedIndex > -1) {
|
||||
const selectedAzureAccount = this.migrationStateModel.getAccount(selectedIndex);
|
||||
// Making a clone of the account object to preserve the original tenants
|
||||
this.migrationStateModel._azureAccount = deepClone(selectedAzureAccount);
|
||||
if (this.migrationStateModel._azureAccount.properties.tenants.length > 1) {
|
||||
this.migrationStateModel._accountTenants = selectedAzureAccount.properties.tenants;
|
||||
this._accountTenantDropdown.values = await this.migrationStateModel.getTenantValues();
|
||||
selectDropDownIndex(this._accountTenantDropdown, 0);
|
||||
await this._accountTenantFlexContainer.updateCssStyles({
|
||||
'display': 'inline'
|
||||
});
|
||||
} else {
|
||||
await this._accountTenantFlexContainer.updateCssStyles({
|
||||
'display': 'none'
|
||||
});
|
||||
}
|
||||
await this._azureAccountsDropdown.validate();
|
||||
await this.populateSubscriptionDropdown();
|
||||
}
|
||||
}));
|
||||
|
||||
const linkAccountButton = this._view.modelBuilder.hyperlink()
|
||||
.withProps({
|
||||
label: constants.ACCOUNT_LINK_BUTTON_LABEL,
|
||||
url: '',
|
||||
CSSStyles: {
|
||||
...styles.BODY_CSS
|
||||
}
|
||||
})
|
||||
.component();
|
||||
|
||||
this._disposables.push(linkAccountButton.onDidClick(async (event) => {
|
||||
await vscode.commands.executeCommand('workbench.actions.modal.linkedAccount');
|
||||
await this.populateAzureAccountsDropdown();
|
||||
this.wizard.message = {
|
||||
text: ''
|
||||
};
|
||||
await this._azureAccountsDropdown.validate();
|
||||
}));
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column'
|
||||
})
|
||||
.withItems([
|
||||
azureAccountLabel,
|
||||
this._azureAccountsDropdown,
|
||||
linkAccountButton
|
||||
])
|
||||
.component();
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
private createAzureTenantContainer(): azdata.FlexContainer {
|
||||
const azureTenantDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AZURE_TENANT,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._accountTenantDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.AZURE_TENANT,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
fireOnTextChange: true,
|
||||
}).component();
|
||||
|
||||
this._disposables.push(this._accountTenantDropdown.onValueChanged(value => {
|
||||
/**
|
||||
* Replacing all the tenants in azure account with the tenant user has selected.
|
||||
* All azure requests will only run on this tenant from now on
|
||||
*/
|
||||
const selectedIndex = findDropDownItemIndex(this._accountTenantDropdown, value);
|
||||
const selectedTenant = this.migrationStateModel.getTenant(selectedIndex);
|
||||
this.migrationStateModel._azureTenant = deepClone(selectedTenant);
|
||||
if (selectedIndex > -1) {
|
||||
this.migrationStateModel._azureAccount.properties.tenants = [this.migrationStateModel.getTenant(selectedIndex)];
|
||||
this.migrationStateModel._subscriptions = undefined!;
|
||||
this.migrationStateModel._targetSubscription = undefined!;
|
||||
this.migrationStateModel._databaseBackup.subscription = undefined!;
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
this._accountTenantFlexContainer = this._view.modelBuilder.flexContainer()
|
||||
.withLayout({
|
||||
flexFlow: 'column'
|
||||
})
|
||||
.withItems([
|
||||
azureTenantDropdownLabel,
|
||||
this._accountTenantDropdown
|
||||
])
|
||||
.withProps({
|
||||
CSSStyles: {
|
||||
'display': 'none'
|
||||
}
|
||||
})
|
||||
.component();
|
||||
return this._accountTenantFlexContainer;
|
||||
}
|
||||
|
||||
private createTargetDropdownContainer(): azdata.FlexContainer {
|
||||
const subscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION,
|
||||
description: constants.TARGET_SUBSCRIPTION_INFO,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS,
|
||||
}
|
||||
}).component();
|
||||
this._azureSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.SUBSCRIPTION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
required: true,
|
||||
fireOnTextChange: true,
|
||||
CSSStyles: {
|
||||
'margin-top': '-1em'
|
||||
},
|
||||
}).component();
|
||||
this._disposables.push(this._azureSubscriptionDropdown.onValueChanged(async (value) => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureSubscriptionDropdown, value);
|
||||
if (selectedIndex > -1 &&
|
||||
value !== constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
this.migrationStateModel._targetSubscription = this.migrationStateModel.getSubscription(selectedIndex);
|
||||
this.migrationStateModel._targetServerInstance = undefined!;
|
||||
this.migrationStateModel._sqlMigrationService = undefined!;
|
||||
await this.populateLocationDropdown();
|
||||
}
|
||||
}));
|
||||
|
||||
const azureLocationLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.LOCATION,
|
||||
description: constants.TARGET_LOCATION_INFO,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
this._azureLocationDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.LOCATION,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
required: true,
|
||||
fireOnTextChange: true,
|
||||
CSSStyles: {
|
||||
'margin-top': '-1em'
|
||||
},
|
||||
}).component();
|
||||
this._disposables.push(this._azureLocationDropdown.onValueChanged(async (value) => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureLocationDropdown, value);
|
||||
if (selectedIndex > -1 &&
|
||||
value !== constants.NO_LOCATION_FOUND) {
|
||||
this.migrationStateModel._location = this.migrationStateModel.getLocation(selectedIndex);
|
||||
await this.populateResourceGroupDropdown();
|
||||
}
|
||||
}));
|
||||
|
||||
const azureResourceGroupLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP,
|
||||
description: constants.TARGET_RESOURCE_GROUP_INFO,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
this._azureResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.RESOURCE_GROUP,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
required: true,
|
||||
fireOnTextChange: true,
|
||||
CSSStyles: {
|
||||
'margin-top': '-1em'
|
||||
},
|
||||
}).component();
|
||||
this._disposables.push(this._azureResourceGroupDropdown.onValueChanged(async (value) => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureResourceGroupDropdown, value);
|
||||
if (selectedIndex > -1) {
|
||||
if (value !== constants.RESOURCE_GROUP_NOT_FOUND) {
|
||||
this.migrationStateModel._resourceGroup = this.migrationStateModel.getAzureResourceGroup(selectedIndex);
|
||||
}
|
||||
await this.populateResourceInstanceDropdown();
|
||||
}
|
||||
}));
|
||||
|
||||
this._azureResourceDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE,
|
||||
description: constants.TARGET_RESOURCE_INFO,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
requiredIndicator: true,
|
||||
CSSStyles: {
|
||||
...styles.LABEL_CSS
|
||||
}
|
||||
}).component();
|
||||
this._azureResourceDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
ariaLabel: constants.AZURE_SQL_DATABASE_MANAGED_INSTANCE,
|
||||
width: WIZARD_INPUT_COMPONENT_WIDTH,
|
||||
editable: true,
|
||||
required: true,
|
||||
fireOnTextChange: true,
|
||||
CSSStyles: {
|
||||
'margin-top': '-1em'
|
||||
},
|
||||
}).component();
|
||||
this._disposables.push(this._azureResourceDropdown.onValueChanged(value => {
|
||||
const selectedIndex = findDropDownItemIndex(this._azureResourceDropdown, value);
|
||||
if (selectedIndex > -1 &&
|
||||
value !== constants.NO_MANAGED_INSTANCE_FOUND &&
|
||||
value !== constants.NO_VIRTUAL_MACHINE_FOUND) {
|
||||
this.migrationStateModel._sqlMigrationServices = undefined!;
|
||||
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLVM:
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getVirtualMachine(selectedIndex);
|
||||
break;
|
||||
|
||||
case MigrationTargetType.SQLMI:
|
||||
this.migrationStateModel._targetServerInstance = this.migrationStateModel.getManagedInstance(selectedIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
return this._view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
subscriptionDropdownLabel,
|
||||
this._azureSubscriptionDropdown,
|
||||
azureLocationLabel,
|
||||
this._azureLocationDropdown,
|
||||
azureResourceGroupLabel,
|
||||
this._azureResourceGroupDropdown,
|
||||
this._azureResourceDropdownLabel,
|
||||
this._azureResourceDropdown
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column',
|
||||
}).component();
|
||||
}
|
||||
|
||||
private async populateAzureAccountsDropdown(): Promise<void> {
|
||||
try {
|
||||
this._azureAccountsDropdown.loading = true;
|
||||
this._azureSubscriptionDropdown.loading = true;
|
||||
this._azureLocationDropdown.loading = true;
|
||||
this._azureResourceGroupDropdown.loading = true;
|
||||
this._azureResourceDropdown.loading = true;
|
||||
|
||||
this._azureAccountsDropdown.values = await this.migrationStateModel.getAccountValues();
|
||||
|
||||
if (this.hasSavedInfo() && this._azureAccountsDropdown.values) {
|
||||
(<azdata.CategoryValue[]>this._azureAccountsDropdown.values)?.forEach((account, index) => {
|
||||
if ((<azdata.CategoryValue>account).name.toLowerCase() === this.migrationStateModel.savedInfo.azureAccount?.displayInfo.userId.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureAccountsDropdown, index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectDropDownIndex(this._azureAccountsDropdown, 0);
|
||||
}
|
||||
} finally {
|
||||
this._azureAccountsDropdown.loading = false;
|
||||
this._azureSubscriptionDropdown.loading = false;
|
||||
this._azureLocationDropdown.loading = false;
|
||||
this._azureResourceGroupDropdown.loading = false;
|
||||
this._azureResourceDropdown.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private async populateSubscriptionDropdown(): Promise<void> {
|
||||
try {
|
||||
this._azureSubscriptionDropdown.loading = true;
|
||||
this._azureLocationDropdown.loading = true;
|
||||
this._azureResourceGroupDropdown.loading = true;
|
||||
this._azureResourceDropdown.loading = true;
|
||||
|
||||
this._azureSubscriptionDropdown.values = await this.migrationStateModel.getSubscriptionsDropdownValues();
|
||||
if (this.hasSavedInfo() && this._azureSubscriptionDropdown.values) {
|
||||
this._azureSubscriptionDropdown.values!.forEach((subscription, index) => {
|
||||
if ((<azdata.CategoryValue>subscription).name.toLowerCase() === this.migrationStateModel.savedInfo?.subscription?.id.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureSubscriptionDropdown, index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectDropDownIndex(this._azureSubscriptionDropdown, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureSubscriptionDropdown.loading = false;
|
||||
this._azureLocationDropdown.loading = false;
|
||||
this._azureResourceGroupDropdown.loading = false;
|
||||
this._azureResourceDropdown.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async populateLocationDropdown(): Promise<void> {
|
||||
try {
|
||||
this._azureLocationDropdown.loading = true;
|
||||
this._azureResourceGroupDropdown.loading = true;
|
||||
this._azureResourceDropdown.loading = true;
|
||||
|
||||
this._azureLocationDropdown.values = await this.migrationStateModel.getAzureLocationDropdownValues(this.migrationStateModel._targetSubscription);
|
||||
if (this.hasSavedInfo() && this._azureLocationDropdown.values) {
|
||||
this._azureLocationDropdown.values.forEach((location, index) => {
|
||||
if ((<azdata.CategoryValue>location)?.displayName.toLowerCase() === this.migrationStateModel.savedInfo?.location?.displayName.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureLocationDropdown, index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectDropDownIndex(this._azureLocationDropdown, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureLocationDropdown.loading = false;
|
||||
this._azureResourceGroupDropdown.loading = false;
|
||||
this._azureResourceDropdown.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
public async populateResourceGroupDropdown(): Promise<void> {
|
||||
try {
|
||||
this._azureResourceGroupDropdown.loading = true;
|
||||
this._azureResourceDropdown.loading = true;
|
||||
|
||||
this._azureResourceGroupDropdown.values = await this.migrationStateModel.getAzureResourceGroupDropdownValues(this.migrationStateModel._targetSubscription);
|
||||
if (this.hasSavedInfo() && this._azureResourceGroupDropdown.values) {
|
||||
this._azureResourceGroupDropdown.values.forEach((resourceGroup, index) => {
|
||||
if ((<azdata.CategoryValue>resourceGroup)?.name.toLowerCase() === this.migrationStateModel.savedInfo?.resourceGroup?.id.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureResourceGroupDropdown, index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectDropDownIndex(this._azureResourceGroupDropdown, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureResourceGroupDropdown.loading = false;
|
||||
this._azureResourceDropdown.loading = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private async populateResourceInstanceDropdown(): Promise<void> {
|
||||
try {
|
||||
this._azureResourceDropdown.loading = true;
|
||||
|
||||
switch (this.migrationStateModel._targetType) {
|
||||
case MigrationTargetType.SQLVM:
|
||||
this._azureResourceDropdown.values = await this.migrationStateModel.getSqlVirtualMachineValues(
|
||||
this.migrationStateModel._targetSubscription,
|
||||
this.migrationStateModel._location,
|
||||
this.migrationStateModel._resourceGroup);
|
||||
break;
|
||||
|
||||
case MigrationTargetType.SQLMI:
|
||||
this._azureResourceDropdown.values = await this.migrationStateModel.getManagedInstanceValues(
|
||||
this.migrationStateModel._targetSubscription,
|
||||
this.migrationStateModel._location,
|
||||
this.migrationStateModel._resourceGroup);
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.hasSavedInfo() && this._azureResourceDropdown.values) {
|
||||
this._azureResourceDropdown.values.forEach((resource, index) => {
|
||||
if ((<azdata.CategoryValue>resource).name.toLowerCase() === this.migrationStateModel.savedInfo?.targetServerInstance?.name.toLowerCase()) {
|
||||
selectDropDownIndex(this._azureResourceDropdown, index);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
selectDropDownIndex(this._azureResourceDropdown, 0);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
} finally {
|
||||
this._azureResourceDropdown.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private hasSavedInfo(): boolean {
|
||||
return this.migrationStateModel.retryMigration || (this.migrationStateModel.resumeAssessment && this.migrationStateModel.savedInfo.closedPage >= Page.TargetSelection);
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import * as loc from '../constants/strings';
|
||||
import { MigrationWizardPage } from '../models/migrationWizardPage';
|
||||
import { SKURecommendationPage } from './skuRecommendationPage';
|
||||
import { DatabaseBackupPage } from './databaseBackupPage';
|
||||
import { AccountsSelectionPage } from './accountsSelectionPage';
|
||||
import { TargetSelectionPage } from './targetSelectionPage';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
import { SummaryPage } from './summaryPage';
|
||||
import { MigrationModePage } from './migrationModePage';
|
||||
@@ -41,18 +41,18 @@ export class WizardController {
|
||||
this._wizardObject.generateScriptButton.hidden = true;
|
||||
const saveAndCloseButton = azdata.window.createButton(loc.SAVE_AND_CLOSE);
|
||||
this._wizardObject.customButtons = [saveAndCloseButton];
|
||||
const skuRecommendationPage = new SKURecommendationPage(this._wizardObject, stateModel);
|
||||
const migrationModePage = new MigrationModePage(this._wizardObject, stateModel);
|
||||
const databaseSelectorPage = new DatabaseSelectorPage(this._wizardObject, stateModel);
|
||||
const azureAccountsPage = new AccountsSelectionPage(this._wizardObject, stateModel);
|
||||
const skuRecommendationPage = new SKURecommendationPage(this._wizardObject, stateModel);
|
||||
const targetSelectionPage = new TargetSelectionPage(this._wizardObject, stateModel);
|
||||
const migrationModePage = new MigrationModePage(this._wizardObject, stateModel);
|
||||
const databaseBackupPage = new DatabaseBackupPage(this._wizardObject, stateModel);
|
||||
const integrationRuntimePage = new IntergrationRuntimePage(this._wizardObject, stateModel);
|
||||
const summaryPage = new SummaryPage(this._wizardObject, stateModel);
|
||||
|
||||
const pages: MigrationWizardPage[] = [
|
||||
azureAccountsPage,
|
||||
databaseSelectorPage,
|
||||
skuRecommendationPage,
|
||||
targetSelectionPage,
|
||||
migrationModePage,
|
||||
databaseBackupPage,
|
||||
integrationRuntimePage,
|
||||
@@ -61,6 +61,13 @@ export class WizardController {
|
||||
|
||||
this._wizardObject.pages = pages.map(p => p.getwizardPage());
|
||||
|
||||
// kill existing data collection if user relaunches the wizard via new migration or retry existing migration
|
||||
await this._model.refreshPerfDataCollection();
|
||||
if ((!this._model.resumeAssessment || this._model.retryMigration) && this._model._perfDataCollectionIsCollecting) {
|
||||
void this._model.stopPerfDataCollection();
|
||||
void vscode.window.showInformationMessage(loc.AZURE_RECOMMENDATION_STOP_POPUP);
|
||||
}
|
||||
|
||||
const wizardSetupPromises: Thenable<void>[] = [];
|
||||
wizardSetupPromises.push(...pages.map(p => p.registerWizardContent()));
|
||||
wizardSetupPromises.push(this._wizardObject.open());
|
||||
@@ -68,6 +75,7 @@ export class WizardController {
|
||||
if (this._model.savedInfo.closedPage >= Page.MigrationMode) {
|
||||
this._model.refreshDatabaseBackupPage = true;
|
||||
}
|
||||
|
||||
// if the user selected network share and selected save & close afterwards, it should always return to the database backup page so that
|
||||
// the user can input their password again
|
||||
if (this._model.savedInfo.closedPage >= Page.DatabaseBackup && this._model.savedInfo.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
@@ -106,6 +114,10 @@ export class WizardController {
|
||||
saveAndCloseButton.onClick(async () => {
|
||||
await stateModel.saveInfo(serverName, this._wizardObject.currentPage);
|
||||
await this._wizardObject.close();
|
||||
|
||||
if (stateModel.performanceCollectionInProgress()) {
|
||||
void vscode.window.showInformationMessage(loc.SAVE_AND_CLOSE_POPUP);
|
||||
}
|
||||
});
|
||||
|
||||
this._wizardObject.cancelButton.onClick(e => {
|
||||
|
||||
Reference in New Issue
Block a user