diff --git a/extensions/sql-migration/src/models/migrationWizardPage.ts b/extensions/sql-migration/src/models/migrationWizardPage.ts index 44972911a5..4faf988263 100644 --- a/extensions/sql-migration/src/models/migrationWizardPage.ts +++ b/extensions/sql-migration/src/models/migrationWizardPage.ts @@ -6,7 +6,11 @@ import * as azdata from 'azdata'; import { MigrationStateModel, StateChangeEvent } from './stateMachine'; export abstract class MigrationWizardPage { - constructor(private readonly wizard: azdata.window.Wizard, protected readonly wizardPage: azdata.window.WizardPage, protected readonly migrationStateModel: MigrationStateModel) { } + constructor( + private readonly wizard: azdata.window.Wizard, + protected readonly wizardPage: azdata.window.WizardPage, + protected readonly migrationStateModel: MigrationStateModel + ) { } public registerWizardContent(): Promise { return new Promise(async (resolve, reject) => { diff --git a/extensions/sql-migration/src/models/strings.ts b/extensions/sql-migration/src/models/strings.ts index 5ead4b0b28..09c4cecd02 100644 --- a/extensions/sql-migration/src/models/strings.ts +++ b/extensions/sql-migration/src/models/strings.ts @@ -27,5 +27,9 @@ export const SKU_RECOMMENDATION_SOME_SUCCESSFUL = (migratableCount: number, data }; export const SKU_RECOMMENDATION_NONE_SUCCESSFUL = localize('sql.migration.sku.none', "Based on the results of our source configuration scans, none of your databases can be migrated to Azure SQL."); +export const SUBSCRIPTION_SELECTION_PAGE_TITLE = localize('sql.migration.wizard.subscription.title', "Azure Subscription Selection"); +export const SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE = localize('sql.migration.wizard.subscription.azure.account.title', "Azure Account"); +export const SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE = localize('sql.migration.wizard.subscription.azure.subscription.title', "Azure Subscription"); +export const SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE = localize('sql.migration.wizard.subscription.azure.product.title', "Azure Product"); export const CONGRATULATIONS = localize('sql.migration.generic.congratulations', "Congratulations"); diff --git a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts index 7e65d39775..2941d8039b 100644 --- a/extensions/sql-migration/src/wizard/skuRecommendationPage.ts +++ b/extensions/sql-migration/src/wizard/skuRecommendationPage.ts @@ -8,6 +8,7 @@ import { MigrationWizardPage } from '../models/migrationWizardPage'; import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; import { Product } from '../models/product'; import { CONGRATULATIONS, SKU_RECOMMENDATION_PAGE_TITLE, SKU_RECOMMENDATION_ALL_SUCCESSFUL } from '../models/strings'; +import { Disposable } from 'vscode'; export class SKURecommendationPage extends MigrationWizardPage { // For future reference: DO NOT EXPOSE WIZARD DIRECTLY THROUGH HERE. @@ -81,12 +82,14 @@ export class SKURecommendationPage extends MigrationWizardPage { // fill in some of that information } + private eventListener: Disposable | undefined; public async onPageEnter(): Promise { + this.eventListener = this.migrationStateModel.stateChangeEvent(async (e) => this.onStateChangeEvent(e)); this.constructDetails(); } public async onPageLeave(): Promise { - + this.eventListener?.dispose(); } protected async handleStateChange(e: StateChangeEvent): Promise { diff --git a/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts new file mode 100644 index 0000000000..2ece129b27 --- /dev/null +++ b/extensions/sql-migration/src/wizard/subscriptionSelectionPage.ts @@ -0,0 +1,124 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as azdata from 'azdata'; +import { MigrationWizardPage } from '../models/migrationWizardPage'; +import { MigrationStateModel, StateChangeEvent } from '../models/stateMachine'; +import { SUBSCRIPTION_SELECTION_PAGE_TITLE, SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE, SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE, SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE } from '../models/strings'; +import { Disposable } from 'vscode'; + +interface AccountValue extends azdata.CategoryValue { + account: azdata.Account; +} + +export class SubscriptionSelectionPage extends MigrationWizardPage { + private disposables: Disposable[] = []; + + // For future reference: DO NOT EXPOSE WIZARD DIRECTLY THROUGH HERE. + constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) { + super(wizard, azdata.window.createWizardPage(SUBSCRIPTION_SELECTION_PAGE_TITLE), migrationStateModel); + } + + + protected async registerContent(view: azdata.ModelView): Promise { + await this.initialState(view); + } + + private accountDropDown?: azdata.FormComponent; + private subscriptionDropDown?: azdata.FormComponent; + private productDropDown?: azdata.FormComponent; + private async initialState(view: azdata.ModelView) { + this.accountDropDown = this.createAccountDropDown(view); + this.subscriptionDropDown = this.createSubscriptionDropDown(view); + this.productDropDown = this.createProdcutDropDown(view); + + const form = view.modelBuilder.formContainer().withFormItems( + [ + this.accountDropDown, + this.subscriptionDropDown, + this.productDropDown + ] + ); + + await view.initializeModel(form.component()); + } + + private createAccountDropDown(view: azdata.ModelView): azdata.FormComponent { + const dropDown = view.modelBuilder.dropDown().withProperties({ + values: [], + }); + + return { + component: dropDown.component(), + title: SUBSCRIPTION_SELECTION_AZURE_ACCOUNT_TITLE + }; + } + + private createSubscriptionDropDown(view: azdata.ModelView): azdata.FormComponent { + const dropDown = view.modelBuilder.dropDown().withProperties({ + values: [], + }); + this.setupSubscriptionListener(); + + return { + component: dropDown.component(), + title: SUBSCRIPTION_SELECTION_AZURE_SUBSCRIPTION_TITLE + }; + } + + private createProdcutDropDown(view: azdata.ModelView): azdata.FormComponent { + const dropDown = view.modelBuilder.dropDown().withProperties({ + values: [], + }); + this.setupProductListener(); + + return { + component: dropDown.component(), + title: SUBSCRIPTION_SELECTION_AZURE_PRODUCT_TITLE + }; + } + + + private setupSubscriptionListener(): void { + this.disposables.push(this.accountDropDown!.component.onValueChanged((event) => { + console.log(event); + })); + } + + private setupProductListener(): void { + this.disposables.push(this.subscriptionDropDown!.component.onValueChanged((event) => { + console.log(event); + })); + } + + private async populateAccountValues(): Promise { + + + let accounts = await azdata.accounts.getAllAccounts(); + accounts = accounts.filter(a => a.key.providerId.startsWith('azure') && !a.isStale); + + const values: AccountValue[] = accounts.map(a => { + return { + displayName: a.displayInfo.displayName, + name: a.key.accountId, + account: a + }; + }); + + this.accountDropDown!.component.values = values; + } + + public async onPageEnter(): Promise { + this.disposables.push(this.migrationStateModel.stateChangeEvent(async (e) => this.onStateChangeEvent(e))); + await this.populateAccountValues(); + } + + public async onPageLeave(): Promise { + this.disposables.forEach(d => d.dispose()); + } + + protected async handleStateChange(e: StateChangeEvent): Promise { + } +} diff --git a/extensions/sql-migration/src/wizard/wizardController.ts b/extensions/sql-migration/src/wizard/wizardController.ts index c3d3ccaf11..49be4a40b4 100644 --- a/extensions/sql-migration/src/wizard/wizardController.ts +++ b/extensions/sql-migration/src/wizard/wizardController.ts @@ -8,6 +8,8 @@ import { MigrationStateModel } from '../models/stateMachine'; import { SourceConfigurationPage } from './sourceConfigurationPage'; import { WIZARD_TITLE } from '../models/strings'; import { MigrationWizardPage } from '../models/migrationWizardPage'; +import { SKURecommendationPage } from './skuRecommendationPage'; +import { SubscriptionSelectionPage } from './subscriptionSelectionPage'; export class WizardController { constructor(private readonly extensionContext: vscode.ExtensionContext) { @@ -25,13 +27,15 @@ export class WizardController { const wizard = azdata.window.createWizard(WIZARD_TITLE, 'wide'); wizard.generateScriptButton.enabled = false; const sourceConfigurationPage = new SourceConfigurationPage(wizard, stateModel); + const skuRecommendationPage = new SKURecommendationPage(wizard, stateModel); + const subscriptionSelectionPage = new SubscriptionSelectionPage(wizard, stateModel); - const pages: MigrationWizardPage[] = [sourceConfigurationPage]; + const pages: MigrationWizardPage[] = [sourceConfigurationPage, skuRecommendationPage, subscriptionSelectionPage]; wizard.pages = pages.map(p => p.getwizardPage()); const wizardSetupPromises: Thenable[] = []; - wizardSetupPromises.push(sourceConfigurationPage.registerWizardContent()); + wizardSetupPromises.push(...pages.map(p => p.registerWizardContent())); wizardSetupPromises.push(wizard.open()); wizard.onPageChanged(async (pageChangeInfo: azdata.window.WizardPageChangeInfo) => { @@ -49,6 +53,7 @@ export class WizardController { const canEnter = await pages[lastPage]?.canEnter() ?? true; return canEnter && canLeave; + // return true; }); await Promise.all(wizardSetupPromises); diff --git a/src/sql/azdata.d.ts b/src/sql/azdata.d.ts index 87621f24c3..50914516a4 100644 --- a/src/sql/azdata.d.ts +++ b/src/sql/azdata.d.ts @@ -2735,7 +2735,7 @@ declare module 'azdata' { export interface FormComponent { component: T; - title: string; + title?: string; actions?: Component[]; required?: boolean; }