mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 02:48:30 -05:00
Adding Integration Runtime Page to Migration extension wizard (#14020)
* - Fixed GetMigrationController - Added createMigrationController and getControllerAuthKeys API in azure core. - Added typings for Migration Controller - Fixed database backup page validation logic - Added IR page with create controller * Fixing all the comments from the PR * Fixed typings
This commit is contained in:
@@ -0,0 +1,492 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { createMigrationController, getMigrationControllerRegions, getMigrationController, getResourceGroups, getSubscriptions, Subscription, getMigrationControllerAuthKeys } from '../api/azure';
|
||||
import { MigrationStateModel } from '../models/stateMachine';
|
||||
import * as constants from '../models/strings';
|
||||
import * as os from 'os';
|
||||
import { azureResource } from 'azureResource';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
|
||||
export class CreateMigrationControllerDialog {
|
||||
|
||||
private migrationControllerSubscriptionDropdown!: azdata.DropDownComponent;
|
||||
private migrationControllerResourceGroupDropdown!: azdata.DropDownComponent;
|
||||
private migrationControllerRegionDropdown!: azdata.DropDownComponent;
|
||||
private migrationControllerNameText!: azdata.InputBoxComponent;
|
||||
private _formSubmitButton!: azdata.ButtonComponent;
|
||||
|
||||
private _statusLoadingComponent!: azdata.LoadingComponent;
|
||||
private migrationControllerAuthKeyTable!: azdata.DeclarativeTableComponent;
|
||||
private _connectionStatus!: azdata.TextComponent;
|
||||
private _copyKey1Button!: azdata.ButtonComponent;
|
||||
private _copyKey2Button!: azdata.ButtonComponent;
|
||||
private _setupContainer!: azdata.FlexContainer;
|
||||
|
||||
private _dialogObject!: azdata.window.Dialog;
|
||||
private _view!: azdata.ModelView;
|
||||
private _subscriptionMap: Map<string, Subscription> = new Map();
|
||||
|
||||
constructor(private migrationStateModel: MigrationStateModel, private irPage: IntergrationRuntimePage) {
|
||||
this._dialogObject = azdata.window.createModelViewDialog(constants.IR_PAGE_TITLE, 'MigrationControllerDialog', 'wide');
|
||||
}
|
||||
|
||||
initialize() {
|
||||
let tab = azdata.window.createTab('');
|
||||
this._dialogObject.registerCloseValidator(async () => {
|
||||
return true;
|
||||
});
|
||||
tab.registerContent((view: azdata.ModelView) => {
|
||||
this._view = view;
|
||||
|
||||
this._formSubmitButton = view.modelBuilder.button().withProps({
|
||||
label: constants.SUBMIT,
|
||||
width: '80px'
|
||||
}).component();
|
||||
|
||||
this._formSubmitButton.onDidClick(async (e) => {
|
||||
this._statusLoadingComponent.loading = true;
|
||||
this._formSubmitButton.enabled = false;
|
||||
|
||||
const subscription = this._subscriptionMap.get((this.migrationControllerSubscriptionDropdown.value as azdata.CategoryValue).name)!;
|
||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
||||
const controllerName = this.migrationControllerNameText.value;
|
||||
|
||||
const formValidationErrors = this.validateCreateControllerForm(subscription, resourceGroup, region, controllerName);
|
||||
|
||||
if (formValidationErrors.length > 0) {
|
||||
this.setDialogMessage(formValidationErrors);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const createdController = await createMigrationController(this.migrationStateModel.azureAccount, subscription, resourceGroup, region, controllerName!);
|
||||
if (createdController.error) {
|
||||
this.setDialogMessage(`${createdController.error.code} : ${createdController.error.message}`);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
return;
|
||||
}
|
||||
this._dialogObject.message = {
|
||||
text: ''
|
||||
};
|
||||
this.migrationStateModel.migrationController = createdController;
|
||||
await this.refreshAuthTable();
|
||||
await this.refreshStatus();
|
||||
this._setupContainer.display = 'inline';
|
||||
this._statusLoadingComponent.loading = false;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this._statusLoadingComponent.loading = false;
|
||||
this._formSubmitButton.enabled = true;
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
this._statusLoadingComponent = view.modelBuilder.loadingComponent().withProps({
|
||||
loadingText: constants.CONTROLLER_DIALOG_CONTROLLER_CONTAINER_LOADING_HELP,
|
||||
loading: false
|
||||
}).component();
|
||||
|
||||
const creationStatusContainer = this.createControllerStatus();
|
||||
|
||||
const formBuilder = view.modelBuilder.formContainer().withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.migrationControllerDropdownsContainer()
|
||||
},
|
||||
{
|
||||
component: this._formSubmitButton
|
||||
},
|
||||
{
|
||||
component: this._statusLoadingComponent
|
||||
},
|
||||
{
|
||||
component: creationStatusContainer
|
||||
}
|
||||
],
|
||||
{
|
||||
horizontal: false
|
||||
}
|
||||
);
|
||||
|
||||
const form = formBuilder.withLayout({ width: '100%' }).component();
|
||||
|
||||
return view.initializeModel(form).then(() => {
|
||||
this.populateSubscriptions();
|
||||
});
|
||||
});
|
||||
|
||||
this._dialogObject.content = [tab];
|
||||
this._dialogObject.okButton.enabled = false;
|
||||
azdata.window.openDialog(this._dialogObject);
|
||||
this._dialogObject.cancelButton.onClick((e) => {
|
||||
this.migrationStateModel.migrationController = undefined;
|
||||
});
|
||||
this._dialogObject.okButton.onClick((e) => {
|
||||
this.irPage.populateMigrationController();
|
||||
});
|
||||
}
|
||||
|
||||
private migrationControllerDropdownsContainer(): azdata.FlexContainer {
|
||||
const dialogDescription = this._view.modelBuilder.text().withProps({
|
||||
value: constants.IR_PAGE_DESCRIPTION,
|
||||
links: [
|
||||
{
|
||||
text: constants.LEARN_MORE,
|
||||
url: 'https://www.microsoft.com' // TODO: add a proper link to the docs.
|
||||
}
|
||||
]
|
||||
}).component();
|
||||
|
||||
const formHeading = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_DIALOG_CREATE_CONTROLLER_FORM_HEADING
|
||||
}).component();
|
||||
|
||||
const subscriptionDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SUBSCRIPTION
|
||||
}).component();
|
||||
|
||||
this.migrationControllerSubscriptionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
this.migrationControllerSubscriptionDropdown.onValueChanged((e) => {
|
||||
if (this.migrationControllerSubscriptionDropdown.value) {
|
||||
this.populateResourceGroups();
|
||||
}
|
||||
});
|
||||
|
||||
const resourceGroupDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.RESOURCE_GROUP
|
||||
}).component();
|
||||
|
||||
this.migrationControllerResourceGroupDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
required: true
|
||||
}).component();
|
||||
|
||||
const controllerNameLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.NAME
|
||||
}).component();
|
||||
|
||||
this.migrationControllerNameText = this._view.modelBuilder.inputBox().component();
|
||||
|
||||
const regionsDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.REGION
|
||||
}).component();
|
||||
|
||||
this.migrationControllerRegionDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
required: true,
|
||||
values: getMigrationControllerRegions()
|
||||
}).component();
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
dialogDescription,
|
||||
formHeading,
|
||||
subscriptionDropdownLabel,
|
||||
this.migrationControllerSubscriptionDropdown,
|
||||
resourceGroupDropdownLabel,
|
||||
this.migrationControllerResourceGroupDropdown,
|
||||
controllerNameLabel,
|
||||
this.migrationControllerNameText,
|
||||
regionsDropdownLabel,
|
||||
this.migrationControllerRegionDropdown
|
||||
]).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
private validateCreateControllerForm(subscription: azureResource.AzureResourceSubscription, resourceGroup: string | undefined, region: string | undefined, controllerName: string | undefined): string {
|
||||
const errors: string[] = [];
|
||||
if (!subscription) {
|
||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
if (!resourceGroup) {
|
||||
errors.push(constants.INVALID_RESOURCE_GROUP_ERROR);
|
||||
}
|
||||
if (!region) {
|
||||
errors.push(constants.INVALID_REGION_ERROR);
|
||||
}
|
||||
if (!controllerName || controllerName.length === 0) {
|
||||
errors.push(constants.INVALID_CONTROLLER_NAME_ERROR);
|
||||
}
|
||||
return errors.join(os.EOL);
|
||||
}
|
||||
|
||||
private async populateSubscriptions(): Promise<void> {
|
||||
this.migrationControllerSubscriptionDropdown.loading = true;
|
||||
this.migrationControllerResourceGroupDropdown.loading = true;
|
||||
const subscriptions = await getSubscriptions(this.migrationStateModel.azureAccount);
|
||||
|
||||
let subscriptionDropdownValues: azdata.CategoryValue[] = [];
|
||||
if (subscriptions && subscriptions.length > 0) {
|
||||
|
||||
subscriptions.forEach((subscription) => {
|
||||
this._subscriptionMap.set(subscription.id, subscription);
|
||||
subscriptionDropdownValues.push({
|
||||
name: subscription.id,
|
||||
displayName: subscription.name + ' - ' + subscription.id,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
} else {
|
||||
subscriptionDropdownValues = [
|
||||
{
|
||||
displayName: constants.NO_SUBSCRIPTIONS_FOUND,
|
||||
name: ''
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
this.migrationControllerSubscriptionDropdown.values = subscriptionDropdownValues;
|
||||
this.migrationControllerSubscriptionDropdown.loading = false;
|
||||
this.populateResourceGroups();
|
||||
}
|
||||
|
||||
private async populateResourceGroups(): Promise<void> {
|
||||
this.migrationControllerResourceGroupDropdown.loading = true;
|
||||
let subscription = this._subscriptionMap.get((this.migrationControllerSubscriptionDropdown.value as azdata.CategoryValue).name)!;
|
||||
const resourceGroups = await getResourceGroups(this.migrationStateModel.azureAccount, subscription);
|
||||
let resourceGroupDropdownValues: azdata.CategoryValue[] = [];
|
||||
if (resourceGroups && resourceGroups.length > 0) {
|
||||
resourceGroups.forEach((resourceGroup) => {
|
||||
resourceGroupDropdownValues.push({
|
||||
name: resourceGroup.name,
|
||||
displayName: resourceGroup.name
|
||||
});
|
||||
});
|
||||
} else {
|
||||
resourceGroupDropdownValues = [
|
||||
{
|
||||
displayName: constants.RESOURCE_GROUP_NOT_FOUND,
|
||||
name: ''
|
||||
}
|
||||
];
|
||||
}
|
||||
this.migrationControllerResourceGroupDropdown.values = resourceGroupDropdownValues;
|
||||
this.migrationControllerResourceGroupDropdown.loading = false;
|
||||
}
|
||||
|
||||
private createControllerStatus(): azdata.FlexContainer {
|
||||
|
||||
const informationTextBox = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_DIALOG_CONTROLLER_CONTAINER_DESCRIPTION
|
||||
}).component();
|
||||
|
||||
const expressSetupTitle = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_OPTION1_HEADING,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const expressSetupLink = this._view.modelBuilder.hyperlink().withProps({
|
||||
label: constants.CONTROLLER_OPTION1_SETUP_LINK_TEXT,
|
||||
url: ''
|
||||
}).component();
|
||||
|
||||
expressSetupLink.onDidClick((e) => {
|
||||
vscode.window.showInformationMessage(constants.FEATURE_NOT_AVAILABLE);
|
||||
});
|
||||
|
||||
const manualSetupTitle = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_OPTION2_HEADING,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const manualSetupButton = this._view.modelBuilder.hyperlink().withProps({
|
||||
label: constants.CONTROLLER_OPTION2_STEP1,
|
||||
url: 'https://www.microsoft.com/download/details.aspx?id=39717'
|
||||
}).component();
|
||||
|
||||
const manualSetupSecondDescription = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_OPTION2_STEP2
|
||||
}).component();
|
||||
|
||||
const connectionStatusTitle = this._view.modelBuilder.text().withProps({
|
||||
value: constants.CONTROLLER_CONNECTION_STATUS,
|
||||
CSSStyles: {
|
||||
'font-weight': 'bold'
|
||||
}
|
||||
}).component();
|
||||
|
||||
this._connectionStatus = this._view.modelBuilder.text().withProps({
|
||||
value: ''
|
||||
}).component();
|
||||
|
||||
const refreshButton = this._view.modelBuilder.button().withProps({
|
||||
label: constants.REFRESH,
|
||||
}).component();
|
||||
|
||||
const refreshLoadingIndicator = this._view.modelBuilder.loadingComponent().withProps({
|
||||
loading: false
|
||||
}).component();
|
||||
|
||||
refreshButton.onDidClick(async (e) => {
|
||||
refreshLoadingIndicator.loading = true;
|
||||
try {
|
||||
await this.refreshStatus();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
refreshLoadingIndicator.loading = false;
|
||||
});
|
||||
|
||||
const connectionStatusContainer = this._view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
this._connectionStatus,
|
||||
refreshButton,
|
||||
refreshLoadingIndicator
|
||||
]
|
||||
).component();
|
||||
|
||||
|
||||
this.migrationControllerAuthKeyTable = this._view.modelBuilder.declarativeTable().withProps({
|
||||
columns: [
|
||||
{
|
||||
displayName: constants.NAME,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '100px',
|
||||
isReadOnly: true,
|
||||
},
|
||||
{
|
||||
displayName: constants.AUTH_KEY_COLUMN_HEADER,
|
||||
valueType: azdata.DeclarativeDataType.string,
|
||||
width: '300px',
|
||||
isReadOnly: true,
|
||||
},
|
||||
{
|
||||
displayName: '',
|
||||
valueType: azdata.DeclarativeDataType.component,
|
||||
width: '100px',
|
||||
isReadOnly: true,
|
||||
}
|
||||
],
|
||||
CSSStyles: {
|
||||
'margin-top': '25px'
|
||||
}
|
||||
}).component();
|
||||
|
||||
const refreshKeyButton = this._view.modelBuilder.button().withProps({
|
||||
label: constants.REFRESH_KEYS,
|
||||
CSSStyles: {
|
||||
'margin-top': '10px'
|
||||
},
|
||||
width: '100px'
|
||||
}).component();
|
||||
|
||||
refreshKeyButton.onDidClick(async (e) => {
|
||||
this.refreshAuthTable();
|
||||
|
||||
});
|
||||
|
||||
this._setupContainer = this._view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
informationTextBox,
|
||||
expressSetupTitle,
|
||||
expressSetupLink,
|
||||
manualSetupTitle,
|
||||
manualSetupButton,
|
||||
manualSetupSecondDescription,
|
||||
refreshKeyButton,
|
||||
this.migrationControllerAuthKeyTable,
|
||||
connectionStatusTitle,
|
||||
connectionStatusContainer
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
this._setupContainer.display = 'none';
|
||||
return this._setupContainer;
|
||||
}
|
||||
|
||||
private async refreshStatus(): Promise<void> {
|
||||
const subscription = this._subscriptionMap.get((this.migrationControllerSubscriptionDropdown.value as azdata.CategoryValue).name)!;
|
||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
||||
const controllerStatus = await getMigrationController(this.migrationStateModel.azureAccount, subscription, resourceGroup, region, this.migrationStateModel.migrationController!.name);
|
||||
if (controllerStatus) {
|
||||
const state = controllerStatus.properties.integrationRunTimeState;
|
||||
|
||||
if (state === 'Online') {
|
||||
this._connectionStatus.value = constants.CONTRLLER_READY(this.migrationStateModel.migrationController!.name, os.hostname());
|
||||
this._dialogObject.okButton.enabled = true;
|
||||
} else {
|
||||
this._connectionStatus.value = constants.CONTRLLER_NOT_READY(this.migrationStateModel.migrationController!.name);
|
||||
this._dialogObject.okButton.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private async refreshAuthTable(): Promise<void> {
|
||||
const subscription = this._subscriptionMap.get((this.migrationControllerSubscriptionDropdown.value as azdata.CategoryValue).name)!;
|
||||
const resourceGroup = (this.migrationControllerResourceGroupDropdown.value as azdata.CategoryValue).name;
|
||||
const region = (this.migrationControllerRegionDropdown.value as azdata.CategoryValue).name;
|
||||
const keys = await getMigrationControllerAuthKeys(this.migrationStateModel.azureAccount, subscription, resourceGroup, region, this.migrationStateModel.migrationController!.name);
|
||||
|
||||
this._copyKey1Button = this._view.modelBuilder.button().withProps({
|
||||
label: constants.COPY_KEY
|
||||
}).component();
|
||||
|
||||
this._copyKey1Button.onDidClick((e) => {
|
||||
vscode.env.clipboard.writeText(<string>this.migrationControllerAuthKeyTable.dataValues![0][1].value);
|
||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
this._copyKey2Button = this._view.modelBuilder.button().withProps({
|
||||
label: constants.COPY_KEY
|
||||
}).component();
|
||||
|
||||
this._copyKey2Button.onDidClick((e) => {
|
||||
vscode.env.clipboard.writeText(<string>this.migrationControllerAuthKeyTable.dataValues![1][1].value);
|
||||
vscode.window.showInformationMessage(constants.CONTROLLER_KEY_COPIED_HELP);
|
||||
});
|
||||
|
||||
this.migrationControllerAuthKeyTable.updateProperties({
|
||||
dataValues: [
|
||||
[
|
||||
{
|
||||
value: constants.CONTROLELR_KEY1_LABEL
|
||||
},
|
||||
{
|
||||
value: keys.keyName1
|
||||
},
|
||||
{
|
||||
value: this._copyKey1Button
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
value: constants.CONTROLELR_KEY2_LABEL
|
||||
},
|
||||
{
|
||||
value: keys.keyName2
|
||||
},
|
||||
{
|
||||
value: this._copyKey2Button
|
||||
}
|
||||
]
|
||||
]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private setDialogMessage(message: string, level: azdata.window.MessageLevel = azdata.window.MessageLevel.Error): void {
|
||||
this._dialogObject.message = {
|
||||
text: message,
|
||||
level: level
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
private _subscriptionMap: Map<string, Subscription> = new Map();
|
||||
private _storageAccountMap: Map<string, StorageAccount> = new Map();
|
||||
|
||||
private _errors: string[] = [];
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.DATABASE_BACKUP_PAGE_TITLE), migrationStateModel);
|
||||
this.wizardPage.description = constants.DATABASE_BACKUP_PAGE_DESCRIPTION;
|
||||
@@ -139,16 +137,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
}).component();
|
||||
this._fileShareSubscriptionDropdown = view.modelBuilder.dropDown().withProps({
|
||||
required: true,
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.FILE_SHARE) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._fileShareSubscriptionDropdown.onValueChanged(async (value) => {
|
||||
if (this._fileShareSubscriptionDropdown.value) {
|
||||
@@ -165,16 +153,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._fileShareStorageAccountDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.FILE_SHARE) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._fileShareStorageAccountDropdown.onValueChanged(async (value) => {
|
||||
if (this._fileShareStorageAccountDropdown.value) {
|
||||
@@ -191,16 +169,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._fileShareFileShareDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.FILE_SHARE) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_FILESHARES_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_FILESHARE_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_FILESHARE_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._fileShareFileShareDropdown.onValueChanged((value) => {
|
||||
if (this._fileShareFileShareDropdown.value) {
|
||||
@@ -235,17 +203,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._blobContainerSubscriptionDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
||||
if (
|
||||
(<azdata.CategoryValue>c.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._blobContainerSubscriptionDropdown.onValueChanged(async (value) => {
|
||||
if (this._blobContainerSubscriptionDropdown.value) {
|
||||
@@ -262,16 +219,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._blobContainerStorageAccountDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._blobContainerStorageAccountDropdown.onValueChanged(async (value) => {
|
||||
if (this._blobContainerStorageAccountDropdown.value) {
|
||||
@@ -287,16 +234,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._blobContainerBlobDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.BLOB_CONTAINER) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._blobContainerBlobDropdown.onValueChanged((value) => {
|
||||
if (this._blobContainerBlobDropdown.value) {
|
||||
@@ -339,9 +276,11 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
validationErrorMessage: constants.INVALID_NETWORK_SHARE_LOCATION
|
||||
})
|
||||
.withValidation((component) => {
|
||||
if (component.value) {
|
||||
if (!/^(\\)(\\[\w\.-_]+){2,}(\\?)$/.test(component.value)) {
|
||||
return false;
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if (component.value) {
|
||||
if (!/^(\\)(\\[\w\.-_]+){2,}(\\?)$/.test(component.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -362,9 +301,11 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
validationErrorMessage: constants.INVALID_USER_ACCOUNT
|
||||
})
|
||||
.withValidation((component) => {
|
||||
if (component.value) {
|
||||
if (!/^[a-zA-Z][a-zA-Z0-9\-\.]{0,61}[a-zA-Z]\\\w[\w\.\- ]*$/.test(component.value)) {
|
||||
return false;
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if (component.value) {
|
||||
if (!/^[a-zA-Z][a-zA-Z0-9\-\.]{0,61}[a-zA-Z]\\\w[\w\.\- ]*$/.test(component.value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -401,16 +342,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._networkShareContainerSubscriptionDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._networkShareContainerSubscriptionDropdown.onValueChanged(async (value) => {
|
||||
if (this._networkShareContainerSubscriptionDropdown.value) {
|
||||
@@ -427,16 +358,6 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
this._networkShareContainerStorageAccountDropdown = view.modelBuilder.dropDown()
|
||||
.withProps({
|
||||
required: true
|
||||
}).withValidation((c) => {
|
||||
if (this.migrationStateModel.databaseBackup.networkContainerType === NetworkContainerType.NETWORK_SHARE) {
|
||||
if ((<azdata.CategoryValue>c.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
this.addErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
return false;
|
||||
} else {
|
||||
this.removeErrorMessage(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}).component();
|
||||
this._networkShareContainerStorageAccountDropdown.onValueChanged((value) => {
|
||||
if (this._networkShareContainerStorageAccountDropdown.value) {
|
||||
@@ -533,6 +454,55 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
|
||||
public async onPageEnter(): Promise<void> {
|
||||
await this.getSubscriptionValues();
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const errors: string[] = [];
|
||||
|
||||
switch (this.migrationStateModel.databaseBackup.networkContainerType) {
|
||||
case NetworkContainerType.NETWORK_SHARE:
|
||||
if ((<azdata.CategoryValue>this._networkShareContainerSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._networkShareContainerStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.BLOB_CONTAINER:
|
||||
if ((<azdata.CategoryValue>this._blobContainerSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._blobContainerStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._blobContainerBlobDropdown.value).displayName === constants.NO_BLOBCONTAINERS_FOUND) {
|
||||
errors.push(constants.INVALID_BLOBCONTAINER_ERROR);
|
||||
}
|
||||
break;
|
||||
case NetworkContainerType.FILE_SHARE:
|
||||
if ((<azdata.CategoryValue>this._fileShareSubscriptionDropdown.value).displayName === constants.NO_SUBSCRIPTIONS_FOUND) {
|
||||
errors.push(constants.INVALID_SUBSCRIPTION_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._fileShareStorageAccountDropdown.value).displayName === constants.NO_STORAGE_ACCOUNT_FOUND) {
|
||||
errors.push(constants.INVALID_STORAGE_ACCOUNT_ERROR);
|
||||
}
|
||||
if ((<azdata.CategoryValue>this._fileShareFileShareDropdown.value).displayName === constants.NO_FILESHARES_FOUND) {
|
||||
errors.push(constants.INVALID_FILESHARE_ERROR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.wizard.message = {
|
||||
text: errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
if (errors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<void> {
|
||||
@@ -721,22 +691,4 @@ export class DatabaseBackupPage extends MigrationWizardPage {
|
||||
name: ''
|
||||
}];
|
||||
}
|
||||
|
||||
private addErrorMessage(message: string) {
|
||||
if (!this._errors.includes(message)) {
|
||||
this._errors.push(message);
|
||||
}
|
||||
this.wizard.message = {
|
||||
text: this._errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
}
|
||||
|
||||
private removeErrorMessage(message: string) {
|
||||
this._errors = this._errors.filter(e => e !== message);
|
||||
this.wizard.message = {
|
||||
text: this._errors.join(EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
209
extensions/sql-migration/src/wizard/integrationRuntimePage.ts
Normal file
209
extensions/sql-migration/src/wizard/integrationRuntimePage.ts
Normal file
@@ -0,0 +1,209 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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, StateChangeEvent } from '../models/stateMachine';
|
||||
import { CreateMigrationControllerDialog } from './createMigrationControllerDialog';
|
||||
import * as constants from '../models/strings';
|
||||
import * as os from 'os';
|
||||
|
||||
export class IntergrationRuntimePage extends MigrationWizardPage {
|
||||
|
||||
private migrationControllerDropdown!: azdata.DropDownComponent;
|
||||
private defaultSetupRadioButton!: azdata.RadioButtonComponent;
|
||||
private customSetupRadioButton!: azdata.RadioButtonComponent;
|
||||
private startSetupButton!: azdata.ButtonComponent;
|
||||
private cancelSetupButton!: azdata.ButtonComponent;
|
||||
private _connectionStatus!: azdata.TextComponent;
|
||||
private createMigrationContainer!: azdata.FlexContainer;
|
||||
private _view!: azdata.ModelView;
|
||||
|
||||
constructor(wizard: azdata.window.Wizard, migrationStateModel: MigrationStateModel) {
|
||||
super(wizard, azdata.window.createWizardPage(constants.IR_PAGE_TITLE), migrationStateModel);
|
||||
}
|
||||
|
||||
protected async registerContent(view: azdata.ModelView): Promise<void> {
|
||||
this._view = view;
|
||||
|
||||
const createNewController = view.modelBuilder.button().withProps({
|
||||
label: constants.NEW,
|
||||
width: '100px',
|
||||
}).component();
|
||||
|
||||
createNewController.onDidClick((e) => {
|
||||
this.createMigrationContainer.display = 'inline';
|
||||
});
|
||||
|
||||
const setupButtonGroup = 'setupOptions';
|
||||
|
||||
this.defaultSetupRadioButton = view.modelBuilder.radioButton().withProps({
|
||||
label: constants.DEFAULT_SETUP_BUTTON,
|
||||
name: setupButtonGroup
|
||||
}).component();
|
||||
this.defaultSetupRadioButton.checked = true;
|
||||
|
||||
this.customSetupRadioButton = view.modelBuilder.radioButton().withProps({
|
||||
label: constants.CUSTOM_SETUP_BUTTON,
|
||||
name: setupButtonGroup
|
||||
}).component();
|
||||
|
||||
this.startSetupButton = view.modelBuilder.button().withProps({
|
||||
label: constants.CREATE,
|
||||
width: '100px'
|
||||
}).component();
|
||||
|
||||
this.startSetupButton.onDidClick((e) => {
|
||||
if (this.defaultSetupRadioButton.checked) {
|
||||
vscode.window.showInformationMessage(constants.FEATURE_NOT_AVAILABLE);
|
||||
} else {
|
||||
this.createMigrationContainer.display = 'none';
|
||||
const dialog = new CreateMigrationControllerDialog(this.migrationStateModel, this);
|
||||
dialog.initialize();
|
||||
}
|
||||
});
|
||||
|
||||
this.cancelSetupButton = view.modelBuilder.button().withProps({
|
||||
label: constants.CANCEL,
|
||||
width: '100px'
|
||||
}).component();
|
||||
|
||||
this.cancelSetupButton.onDidClick((e) => {
|
||||
this.createMigrationContainer.display = 'none';
|
||||
});
|
||||
|
||||
const setupButtonsContainer = view.modelBuilder.flexContainer().withItems([
|
||||
this.startSetupButton,
|
||||
this.cancelSetupButton
|
||||
],
|
||||
{ CSSStyles: { 'margin': '10px', } }
|
||||
).withLayout({
|
||||
flexFlow: 'row'
|
||||
}).component();
|
||||
|
||||
this.createMigrationContainer = view.modelBuilder.flexContainer().withItems(
|
||||
[
|
||||
this.defaultSetupRadioButton,
|
||||
this.customSetupRadioButton,
|
||||
setupButtonsContainer
|
||||
]
|
||||
).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
|
||||
this._connectionStatus = view.modelBuilder.text().component();
|
||||
|
||||
this.createMigrationContainer.display = 'none';
|
||||
|
||||
const form = view.modelBuilder.formContainer()
|
||||
.withFormItems(
|
||||
[
|
||||
{
|
||||
component: this.migrationControllerDropdownsContainer()
|
||||
},
|
||||
{
|
||||
component: createNewController
|
||||
},
|
||||
{
|
||||
component: this.createMigrationContainer
|
||||
},
|
||||
{
|
||||
component: this._connectionStatus
|
||||
}
|
||||
|
||||
]
|
||||
);
|
||||
await view.initializeModel(form.component());
|
||||
}
|
||||
|
||||
public async onPageEnter(): Promise<void> {
|
||||
this.populateMigrationController();
|
||||
this.wizard.registerNavigationValidator((pageChangeInfo) => {
|
||||
if (pageChangeInfo.newPage < pageChangeInfo.lastPage) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const errors: string[] = [];
|
||||
if (((<azdata.CategoryValue>this.migrationControllerDropdown.value).displayName === constants.CONTROLLER_NOT_FOUND)) {
|
||||
errors.push(constants.CONTROLLER_NOT_SETUP_ERROR);
|
||||
}
|
||||
|
||||
this.wizard.message = {
|
||||
text: errors.join(os.EOL),
|
||||
level: azdata.window.MessageLevel.Error
|
||||
};
|
||||
|
||||
if (errors.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public async onPageLeave(): Promise<void> {
|
||||
}
|
||||
|
||||
protected async handleStateChange(e: StateChangeEvent): Promise<void> {
|
||||
}
|
||||
|
||||
private migrationControllerDropdownsContainer(): azdata.FlexContainer {
|
||||
const descriptionText = this._view.modelBuilder.text().withProps({
|
||||
value: constants.IR_PAGE_DESCRIPTION,
|
||||
links: [
|
||||
{
|
||||
url: 'https://www.microsoft.com', // TODO: Add proper link
|
||||
text: constants.LEARN_MORE
|
||||
},
|
||||
]
|
||||
}).component();
|
||||
|
||||
const migrationControllerDropdownLabel = this._view.modelBuilder.text().withProps({
|
||||
value: constants.SELECT_A_MIGRATION_CONTROLLER
|
||||
}).component();
|
||||
|
||||
this.migrationControllerDropdown = this._view.modelBuilder.dropDown().withProps({
|
||||
required: true,
|
||||
}).component();
|
||||
|
||||
const flexContainer = this._view.modelBuilder.flexContainer().withItems([
|
||||
descriptionText,
|
||||
migrationControllerDropdownLabel,
|
||||
this.migrationControllerDropdown
|
||||
]).withLayout({
|
||||
flexFlow: 'column'
|
||||
}).component();
|
||||
return flexContainer;
|
||||
}
|
||||
|
||||
public async populateMigrationController(controllerStatus?: string): Promise<void> {
|
||||
let migrationContollerValues: azdata.CategoryValue[] = [];
|
||||
if (this.migrationStateModel.migrationController) {
|
||||
migrationContollerValues = [
|
||||
{
|
||||
displayName: this.migrationStateModel.migrationController.name,
|
||||
name: this.migrationStateModel.migrationController.name
|
||||
}
|
||||
];
|
||||
|
||||
this._connectionStatus.value = constants.CONTRLLER_READY(this.migrationStateModel.migrationController!.name, os.hostname());
|
||||
}
|
||||
else {
|
||||
migrationContollerValues = [
|
||||
{
|
||||
displayName: constants.CONTROLLER_NOT_FOUND,
|
||||
name: ''
|
||||
}
|
||||
];
|
||||
this._connectionStatus.value = '';
|
||||
}
|
||||
this.migrationControllerDropdown.values = migrationContollerValues;
|
||||
this.migrationControllerDropdown.loading = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SKURecommendationPage } from './skuRecommendationPage';
|
||||
// import { SubscriptionSelectionPage } from './subscriptionSelectionPage';
|
||||
import { DatabaseBackupPage } from './databaseBackupPage';
|
||||
import { AccountsSelectionPage } from './accountsSelectionPage';
|
||||
import { IntergrationRuntimePage } from './integrationRuntimePage';
|
||||
|
||||
export class WizardController {
|
||||
constructor(private readonly extensionContext: vscode.ExtensionContext) {
|
||||
@@ -38,12 +39,16 @@ export class WizardController {
|
||||
// const subscriptionSelectionPage = new SubscriptionSelectionPage(wizard, stateModel);
|
||||
const azureAccountsPage = new AccountsSelectionPage(wizard, stateModel);
|
||||
const databaseBackupPage = new DatabaseBackupPage(wizard, stateModel);
|
||||
const integrationRuntimePage = new IntergrationRuntimePage(wizard, stateModel);
|
||||
|
||||
const pages: MigrationWizardPage[] = [
|
||||
// subscriptionSelectionPage,
|
||||
azureAccountsPage,
|
||||
sourceConfigurationPage,
|
||||
skuRecommendationPage,
|
||||
databaseBackupPage];
|
||||
databaseBackupPage,
|
||||
integrationRuntimePage
|
||||
];
|
||||
|
||||
wizard.pages = pages.map(p => p.getwizardPage());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user