Skip to content

Commit

Permalink
[New Migration Experience] Creating the new import performance dialog…
Browse files Browse the repository at this point in the history
… box (#24597)

* added import performance dialog

* updated open folder icon

* updated alignment for the import performance dialog

* added localized strings

* updated skuRecommendationPage with openExisting option
  • Loading branch information
krritik authored Oct 5, 2023
1 parent 4c8e076 commit 5f8d597
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 11 deletions.
3 changes: 3 additions & 0 deletions extensions/sql-migration/images/openFolder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion extensions/sql-migration/src/constants/iconPathHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export class IconPathHelper {
public static stopDataCollection: IconPath;
public static import: IconPath;
public static settings: IconPath;
public static openFolder: IconPath;

public static setExtensionContext(context: vscode.ExtensionContext) {
IconPathHelper.copy = {
Expand Down Expand Up @@ -228,6 +229,9 @@ export class IconPathHelper {
light: context.asAbsolutePath('images/settings.svg'),
dark: context.asAbsolutePath('images/settings.svg')
};

IconPathHelper.openFolder = {
light: context.asAbsolutePath('images/openFolder.svg'),
dark: context.asAbsolutePath('images/openFolder.svg')
};
}
}
4 changes: 4 additions & 0 deletions extensions/sql-migration/src/constants/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ export const START_PERFORMANCE_COLLECTION = localize('sql.migration.sku.start.pe
export const STOP_PERFORMANCE_COLLECTION = localize('sql.migration.sku.stop.performance.collection', "Stop data collection");
export const RESTART_PERFORMANCE_COLLECTION = localize('sql.migration.sku.restart.performance.collection', "Restart data collection");
export const IMPORT_PERFORMANCE_DATA = localize('sql.migration.sku.import.performance.data', "Import performance data");
export const IMPORT_PERFORMANCE_DATA_DIALOG_DESCRIPTION = localize('sql.migration.sku.import.performance.data.dialog.description', "Import this data file from an existing folder, if you have already collected it using Data Migration Assistant.");
export const IMPORT_PERFORMANCE_DATA_DIALOG_HELPER_MESSAGE = localize('sql.migration.sku.import.performance.data.dialog.helper.message', "Select a folder on your local drive");
export const IMPORT_PERFORMANCE_DATA_DIALOG_OPEN_FILE = localize('sql.migration.sku.import.performance.data.dialog.open.file', "Select a file");
// allow-any-unicode-next-line
export const AZURE_RECOMMENDATION_CARD_NOT_ENABLED = localize('sql.migration.sku.card.azureRecommendation.notEnabled', "Azure recommendation is not available. Click “Start data collection” button above to get started.");
export const AZURE_RECOMMENDATION_CARD_IN_PROGRESS = localize('sql.migration.sku.card.azureRecommendation.inProgress', "Azure recommendation will be displayed once data collection is complete.");
Expand Down Expand Up @@ -961,6 +964,7 @@ export const NAME = localize('sql.migration.name', "Name");
export const LOCATION = localize('sql.migration.location', "Location");
export const REFRESH = localize('sql.migration.refresh', "Refresh");
export const CREATE = localize('sql.migration.create', "Create");
export const IMPORT = localize('sql.migration.cancel', "Import");
export const CANCEL = localize('sql.migration.cancel', "Cancel");
export const TYPE = localize('sql.migration.type', "Type");
export const USER_ACCOUNT = localize('sql.migration.path.user.account', "User account");
Expand Down
6 changes: 6 additions & 0 deletions extensions/sql-migration/src/constants/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,9 @@ export const ASSESSMENT_SUMMARY_CARD_CSS = {
'font-weight': '700',
'margin': '0px',
};

export const PERFORMANCE_DATA_DIALOG_CSS = {
'font-size': '13px',
'line-height': '18px',
'font-weight': '600',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
/*---------------------------------------------------------------------------------------------
* 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 * as styles from '../../constants/styles';
import * as utils from '../../api/utils';
import * as constants from '../../constants/strings';
import { IconPathHelper } from '../../constants/iconPathHelper';
import { MigrationStateModel, PerformanceDataSourceOptions } from '../../models/stateMachine';
import { SKURecommendationPage } from '../../wizard/skuRecommendation/skuRecommendationPage';
import { getSourceConnectionProfile } from '../../api/sqlUtils';
import { EOL } from 'os';
import { EventEmitter } from 'stream';


/*---------------------------------------------------------------------------------------------
* For selecting the path which contains the data files.
* This file contains the code for import performance data dialog.
-----------------------------------------------------------------------------------------------*/
export class ImportPerformanceDataDialog {
private dialog!: azdata.window.Dialog;
private _importButton!: azdata.ButtonComponent;

private _disposables: vscode.Disposable[] = [];

private _creationEvent: EventEmitter = new EventEmitter;
private _isOpen: boolean = false;

private _performanceDataSource!: PerformanceDataSourceOptions;
private _openExistingContainer!: azdata.FlexContainer;
private _openExistingFolderInput!: azdata.InputBoxComponent;

constructor(public skuRecommendationPage: SKURecommendationPage, public wizard: azdata.window.Wizard, public migrationStateModel: MigrationStateModel) {
this._performanceDataSource = PerformanceDataSourceOptions.OpenExisting;
}

private async initializeDialog(dialog: azdata.window.Dialog): Promise<void> {
return new Promise<void>((resolve, reject) => {
dialog.registerContent(async (view) => {
try {
const flex = this.createContainer(view);

this._disposables.push(
view.onClosed(e =>
this._disposables.forEach(
d => { try { d.dispose(); } catch { } })));

await view.initializeModel(flex);

this._creationEvent.once('done', async () => {
azdata.window.closeDialog(this.dialog);
resolve();
});

}
catch (ex) {
reject(ex);
}
});
});
}

private createContainer(_view: azdata.ModelView): azdata.FlexContainer {
const container = _view.modelBuilder.flexContainer().withProps({
CSSStyles: {
'margin': '0px',
'flex-direction': 'column',
}
}).component();
const headingLabel = _view.modelBuilder.text().withProps({
value: constants.IMPORT_PERFORMANCE_DATA,
CSSStyles: {
...styles.PAGE_TITLE_CSS,
'margin-top': '5px',
}
}).component();
const description = _view.modelBuilder.text().withProps({
value: constants.IMPORT_PERFORMANCE_DATA_DIALOG_DESCRIPTION,
CSSStyles: {
...styles.TOOLBAR_CSS,
}
}).component();

this._openExistingContainer = this.createOpenExistingFolderContainer(_view);

this._importButton = _view.modelBuilder.button().withProps({
label: constants.IMPORT,
width: '80px',
enabled: false,
CSSStyles: {
...styles.PERFORMANCE_DATA_DIALOG_CSS,
'padding': '3px 20px 3px 20px',
'gap': '10px'
}
}).component();

this._disposables.push(this._importButton.onDidClick(async () => {
await this.execute();
this._creationEvent.emit('done');
}));

const cancelButton = _view.modelBuilder.button().withProps({
label: constants.CANCEL,
secondary: true,
width: '80px',
CSSStyles: {
...styles.PERFORMANCE_DATA_DIALOG_CSS,
'padding': '3px 20px 3px 20px',
'gap': '10px'
}
}).component();

this._disposables.push(cancelButton.onDidClick(e => {
this._creationEvent.emit('done');
}));

const buttonContainer = _view.modelBuilder.flexContainer().withProps({
CSSStyles: {
'height': '48px',
'margin-top': '20px',
'margin-left': '70px',
}
}).component();

buttonContainer.addItem(this._importButton, {
flex: '0',
});

buttonContainer.addItem(cancelButton, {
flex: '0',
CSSStyles: {
'margin-left': '2px',
'width': '80px'
}
});

container.addItems([
headingLabel,
description,
this._openExistingContainer,
buttonContainer
]);
return container;
}

public async openDialog(dialogName?: string) {
if (!this._isOpen) {
this._isOpen = true;

this.dialog = azdata.window.createModelViewDialog(
'',
'ImportPerformanceDataDialog',
350,
'callout',
'below',
false,
false,
<azdata.window.IDialogProperties>{
height: 51,
width: 0,
xPos: 0,
yPos: 0
}
);

const promise = this.initializeDialog(this.dialog);
azdata.window.openDialog(this.dialog);
await promise;
}
}

private createOpenExistingFolderContainer(_view: azdata.ModelView): azdata.FlexContainer {
const container = _view.modelBuilder.flexContainer()
.withProps({
CSSStyles: {
'flex-direction': 'column',
'margin-top': '10px'
}
})
.component();

const instructions = _view.modelBuilder.text().withProps({
value: constants.IMPORT_PERFORMANCE_DATA_DIALOG_HELPER_MESSAGE,
CSSStyles: {
'font-size': '13px',
'line-height': '18px',
'font-weight': '600',
}
}).component();

const selectFolderContainer = _view.modelBuilder.flexContainer()
.withProps({
CSSStyles: {
'flex-direction': 'row',
'align-items': 'center',
'marign-right': '30px'
}
})
.component();

this._openExistingFolderInput = _view.modelBuilder.inputBox().withProps({
placeHolder: constants.IMPORT_PERFORMANCE_DATA_DIALOG_OPEN_FILE,
readOnly: true,
height: 24,
width: 222,
CSSStyles: {
'font-size': '13px',
'line-height': '18px',
'font-weight': '400',
'margin': '0px',
},
}).component();
this._disposables.push(
this._openExistingFolderInput.onTextChanged(async (value) => {
if (value) {
this.migrationStateModel._skuRecommendationPerformanceLocation = value.trim();
this.dialog!.okButton.enabled = true;
this._importButton.enabled = true;
}
}));

const openButton = _view.modelBuilder.button()
.withProps({
iconHeight: 24,
iconWidth: 24,
iconPath: IconPathHelper.openFolder,
CSSStyles: {
'margin': '0',
'padding': '0',
}
}).component();
this._disposables.push(
openButton.onDidClick(
async (e) => this._openExistingFolderInput.value = await utils.promptUserForFolder()));

selectFolderContainer.addItems([
this._openExistingFolderInput,
openButton]);
container.addItems([
instructions,
selectFolderContainer]);

return container;
}

protected async execute() {
this._isOpen = false;
this.migrationStateModel._skuRecommendationPerformanceDataSource = this._performanceDataSource;

const serverName = (await getSourceConnectionProfile()).serverName;
const errors: string[] = [];
try {
// TODO - ADD the card loading.
// await this.skuRecommendationPage.startCardLoading();
await this.migrationStateModel.getSkuRecommendations();

const skuRecommendationError = this.migrationStateModel._skuRecommendationResults?.recommendationError;
if (skuRecommendationError) {
errors.push(`message: ${skuRecommendationError.message}`);
}
} catch (e) {
console.log(e);
errors.push(constants.SKU_RECOMMENDATION_ASSESSMENT_UNEXPECTED_ERROR(serverName, e));
} finally {
if (errors.length > 0) {
this.wizard.message = {
text: constants.SKU_RECOMMENDATION_ERROR(serverName),
description: errors.join(EOL),
level: azdata.window.MessageLevel.Error
};
}
}

await this.skuRecommendationPage.refreshSkuRecommendationComponents();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as styles from '../../constants/styles';
import * as constants from '../../constants/strings';
import { MigrationStateModel, PerformanceDataSourceOptions } from '../../models/stateMachine';
import { SKURecommendationPage } from './skuRecommendationPage';
import { ImportPerformanceDataDialog } from '../../dialog/skuRecommendationResults/importPerformanceDataDialog';

// TODO - "Change this to actual default path once it is available"
const DEFAULT_PATH_FOR_START_DATA_COLLECTION = "C:\DataPointsCollectionFolder";
Expand All @@ -31,7 +32,7 @@ export class SkuDataCollectionToolbar implements vscode.Disposable {

private _disposables: vscode.Disposable[] = [];

constructor(private skuRecommendationPage: SKURecommendationPage, private migrationStateModel: MigrationStateModel) {
constructor(private skuRecommendationPage: SKURecommendationPage, public wizard: azdata.window.Wizard, private migrationStateModel: MigrationStateModel) {
}

public createToolbar(view: azdata.ModelView): azdata.ToolbarContainer {
Expand Down Expand Up @@ -171,7 +172,13 @@ export class SkuDataCollectionToolbar implements vscode.Disposable {
...styles.TOOLBAR_CSS
}
}).component();
// TODO - implement onDidClick and add to disposables

this._disposables.push(
importPerformanceDataButton.onDidClick(async (e) => {
const importPerformanceDataDialog = new ImportPerformanceDataDialog(this.skuRecommendationPage, this.wizard, this.migrationStateModel);
await importPerformanceDataDialog.openDialog();
})
);
return importPerformanceDataButton;
}

Expand Down
Loading

0 comments on commit 5f8d597

Please sign in to comment.