Custom Summary Page for NotebookWizard and notebook Cell with wizard variables (#10297)

* save not yet tested work

* Merge from master.

* Screeens Shared for Feeedback

* Code complete

* remove unneeded changes

* remove unnecessary comma

* remov wss

* remove dead code

* PR feedback

* checkpoint fixes

* PR & minor fixes

* minor fix for feature of  resourceType options being optional.

* reverting experimental change

* separating out changes for future featurework.

* revert unneeded change

* review feedback fixes

* review feedback

* rename InputFieldComponent to InputComponent

* working version of custom summary page

* add option to align items in a flex- container.

* changes to support labelColor

* save work , still pending issue with labelCSSStyles

* Summary page and setting variabless in notebook.

* minor fixes.

* pr feedbck

* fix formatting issues

* pr feedback

* pr feedback

* pr feedback

* fixing docs

* summary page value setting fix

* rename children of RowInfo to items

* rename a method

* rename summary_text to evaluated_text

* rename properties of fieldInfo

* revert inadvertent change

* rename linked_texttext to hyperlinked_text and removing linking facility from readonly_text

* pr feedback

* fix setting tools variables in env and notebook

* removing saving of originalValues for EvaluatedText

* await on launchNotebookWithEdits

* await on launchNotebookWithContent

* merge RadioOptions & Options into 1

* merge ReadOnlyText, links  & evaluatedText

* Samples for new generic wizard features

* fix comment

* fix assertions

* return type and comment for getClusterContext

* fix inadvertent change

* increase minimum required azdata version

* remove unneeded environment variable settings

* not leaking passwords in notebooks
This commit is contained in:
Arvind Ranasaria
2020-06-01 11:14:59 -07:00
committed by GitHub
parent 84492049e8
commit 678bbe3142
23 changed files with 1190 additions and 473 deletions

View File

@@ -2,20 +2,23 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { INotebookService } from '../../services/notebookService';
import { INotebookService, Notebook } from '../../services/notebookService';
import { IToolsService } from '../../services/toolsService';
import { Model } from '../model';
import { InputComponents, setModelValues } from '../modelViewUtils';
import { WizardBase } from '../wizardBase';
import { WizardPageBase } from '../wizardPageBase';
import { DeploymentType, NotebookWizardInfo } from './../../interfaces';
import { IPlatformService } from './../../services/platformService';
import { NotebookWizardAutoSummaryPage } from './notebookWizardAutoSummaryPage';
import { NotebookWizardPage } from './notebookWizardPage';
import { NotebookWizardSummaryPage } from './notebookWizardSummaryPage';
const localize = nls.loadMessageBundle();
export class NotebookWizard extends WizardBase<NotebookWizard, Model> {
export class NotebookWizard extends WizardBase<NotebookWizard, NotebookWizardPage, Model> {
private _inputComponents: InputComponents = {};
public get notebookService(): INotebookService {
return this._notebookService;
@@ -29,8 +32,15 @@ export class NotebookWizard extends WizardBase<NotebookWizard, Model> {
return this._wizardInfo;
}
constructor(private _wizardInfo: NotebookWizardInfo, private _notebookService: INotebookService, private _platformService: IPlatformService) {
public get inputComponents(): InputComponents {
return this._inputComponents;
}
constructor(private _wizardInfo: NotebookWizardInfo, private _notebookService: INotebookService, private _platformService: IPlatformService, private _toolsService: IToolsService) {
super(_wizardInfo.title, new Model());
if (this._wizardInfo.codeCellInsertionPosition === undefined) {
this._wizardInfo.codeCellInsertionPosition = 0;
}
this.wizardObject.doneButton.label = _wizardInfo.actionText || this.wizardObject.doneButton.label;
}
@@ -41,31 +51,64 @@ export class NotebookWizard extends WizardBase<NotebookWizard, Model> {
protected initialize(): void {
this.setPages(this.getPages());
this.wizardObject.generateScriptButton.hidden = true;
this.wizardInfo.actionText = this.wizardInfo.actionText || localize('deployCluster.ScriptToNotebook', "Script to Notebook");
this.wizardInfo.actionText = this.wizardInfo.actionText || localize('notebookWizard.ScriptToNotebook', "Script to Notebook");
this.wizardObject.doneButton.label = this.wizardInfo.actionText;
}
protected onCancel(): void {
}
protected onOk(): void {
this.model.setEnvironmentVariables();
if (this.wizardInfo.runNotebook) {
this.notebookService.backgroundExecuteNotebook(this.wizardInfo.taskName, this.wizardInfo.notebook, 'deploy', this.platformService);
} else {
this.notebookService.launchNotebook(this.wizardInfo.notebook).then(() => { }, (error) => {
vscode.window.showErrorMessage(error);
});
protected async onOk(): Promise<void> {
setModelValues(this.inputComponents, this.model);
const env: NodeJS.ProcessEnv = {};
this.model.setEnvironmentVariables(env, (varName) => {
const isPassword = !!this.inputComponents[varName]?.isPassword;
return isPassword;
});
console.log(`TCL:: env`, env);
const notebook: Notebook = await this.notebookService.getNotebook(this.wizardInfo.notebook);
// generate python code statements for all variables captured by the wizard
const statements = this.model.getCodeCellContentForNotebook(
this._toolsService.toolsForCurrentProvider,
(varName) => {
const isPassword = !!this.inputComponents[varName]?.isPassword;
return !isPassword;
}
);
// insert generated code statements into the notebook.
notebook.cells.splice(
this.wizardInfo.codeCellInsertionPosition ?? 0,
0,
{
cell_type: 'code',
source: statements,
metadata: {},
outputs: [],
execution_count: 0
}
);
try {
if (this.wizardInfo.runNotebook) {
this.notebookService.backgroundExecuteNotebook(this.wizardInfo.taskName, notebook, 'deploy', this.platformService, env);
} else {
Object.assign(process.env, env);
const title = path.basename(this.notebookService.getNotebookPath(this.wizardInfo.notebook));
await this.notebookService.launchNotebookWithContent(title, JSON.stringify(notebook, undefined, 4));
}
} catch (error) {
vscode.window.showErrorMessage(error);
}
}
private getPages(): WizardPageBase<NotebookWizard>[] {
const pages: WizardPageBase<NotebookWizard>[] = [];
private getPages(): NotebookWizardPage[] {
const pages: NotebookWizardPage[] = [];
for (let pageIndex: number = 0; pageIndex < this.wizardInfo.pages.length; pageIndex++) {
pages.push(new NotebookWizardPage(this, pageIndex));
}
if (this.wizardInfo.generateSummaryPage) {
pages.push(new NotebookWizardSummaryPage(this));
if (this.wizardInfo.pages[pageIndex].isSummaryPage && this.wizardInfo.isSummaryPageAutoGenerated) {
// If we are auto-generating the summary page
pages.push(new NotebookWizardAutoSummaryPage(this, pageIndex));
} else {
pages.push(new NotebookWizardPage(this, pageIndex));
}
}
return pages;
}

View File

@@ -6,19 +6,23 @@ import * as azdata from 'azdata';
import * as nls from 'vscode-nls';
import { SubFieldInfo, FieldType, FontWeight, LabelPosition, SectionInfo } from '../../interfaces';
import { createSection, DefaultInputComponentWidth, DefaultLabelComponentWidth } from '../modelViewUtils';
import { WizardPageBase } from '../wizardPageBase';
import { createSection, DefaultInputWidth, DefaultLabelWidth, DefaultFieldAlignItems, DefaultFieldWidth, DefaultFieldHeight } from '../modelViewUtils';
import { NotebookWizard } from './notebookWizard';
import { NotebookWizardPage } from './notebookWizardPage';
const localize = nls.loadMessageBundle();
export class NotebookWizardSummaryPage extends WizardPageBase<NotebookWizard> {
export class NotebookWizardAutoSummaryPage extends NotebookWizardPage {
private formItems: azdata.FormComponent[] = [];
private form!: azdata.FormBuilder;
private view!: azdata.ModelView;
constructor(wizard: NotebookWizard) {
super(localize('notebookWizard.summaryPageTitle', "Review your configuration"), '', wizard);
constructor(wizard: NotebookWizard, _pageIndex: number) {
super(wizard,
_pageIndex,
wizard.wizardInfo.pages[_pageIndex].title || localize('notebookWizard.autoSummaryPageTitle', "Review your configuration"),
wizard.wizardInfo.pages[_pageIndex].description || ''
);
}
public initialize(): void {
@@ -29,25 +33,31 @@ export class NotebookWizardSummaryPage extends WizardPageBase<NotebookWizard> {
});
}
public onLeave() {
public onLeave(): void {
this.wizard.wizardObject.message = { text: '' };
}
public onEnter() {
public onEnter(): void {
this.formItems.forEach(item => {
this.form!.removeFormItem(item);
});
this.formItems = [];
const inputWidth = this.wizard.wizardInfo.inputWidth || (this.wizard.wizardInfo.summaryPage && this.wizard.wizardInfo.summaryPage.inputWidth) || DefaultInputComponentWidth;
const labelWidth = this.wizard.wizardInfo.labelWidth || (this.wizard.wizardInfo.summaryPage && this.wizard.wizardInfo.summaryPage.labelWidth) || DefaultLabelComponentWidth;
const labelPosition = this.wizard.wizardInfo.labelPosition || (this.wizard.wizardInfo.summaryPage && this.wizard.wizardInfo.summaryPage.labelPosition) || LabelPosition.Left;
const fieldWidth = this.pageInfo.fieldWidth || this.wizard.wizardInfo.fieldWidth || DefaultFieldWidth;
const fieldHeight = this.pageInfo.fieldHeight || this.wizard.wizardInfo.fieldHeight || DefaultFieldHeight;
const fieldAlignItems = this.pageInfo.fieldAlignItems || this.wizard.wizardInfo.fieldAlignItems || DefaultFieldAlignItems;
const labelWidth = this.pageInfo.labelWidth || this.wizard.wizardInfo.labelWidth || DefaultLabelWidth;
const labelPosition = this.pageInfo.labelPosition || this.wizard.wizardInfo.labelPosition || LabelPosition.Left;
const inputWidth = this.pageInfo.inputWidth || this.wizard.wizardInfo.inputWidth || DefaultInputWidth;
this.wizard.wizardInfo.pages.forEach(pageInfo => {
this.wizard.wizardInfo.pages.filter((undefined, index) => index < this._pageIndex).forEach(pageInfo => {
const summarySectionInfo: SectionInfo = {
labelPosition: labelPosition,
labelWidth: labelWidth,
inputWidth: inputWidth,
fieldWidth: fieldWidth,
fieldHeight: fieldHeight,
fieldAlignItems: fieldAlignItems,
title: '',
rows: []
};
@@ -68,6 +78,7 @@ export class NotebookWizardSummaryPage extends WizardPageBase<NotebookWizard> {
title: pageInfo.title,
component: createSection({
container: this.wizard.wizardObject,
inputComponents: this.wizard.inputComponents,
sectionInfo: summarySectionInfo,
view: this.view,
onNewDisposableCreated: () => { },
@@ -84,11 +95,11 @@ export class NotebookWizardSummaryPage extends WizardPageBase<NotebookWizard> {
private addSummaryForVariable(summarySectionInfo: SectionInfo, fieldInfo: SubFieldInfo) {
summarySectionInfo!.rows!.push({
fields: [{
items: [{
type: FieldType.ReadonlyText,
label: fieldInfo.label,
defaultValue: this.wizard.model.getStringValue(fieldInfo.variableName!),
labelFontWeight: FontWeight.Bold
labelCSSStyles: { fontWeight: FontWeight.Bold }
}]
});
}

View File

@@ -7,58 +7,73 @@ import { EOL } from 'os';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { NotebookWizardPageInfo } from '../../interfaces';
import { initializeWizardPage, InputComponents, InputComponent, setModelValues, Validator } from '../modelViewUtils';
import { initializeWizardPage, InputComponentInfo, setModelValues, Validator } from '../modelViewUtils';
import { WizardPageBase } from '../wizardPageBase';
import { NotebookWizard } from './notebookWizard';
const localize = nls.loadMessageBundle();
export class NotebookWizardPage extends WizardPageBase<NotebookWizard> {
private inputComponents: InputComponents = {};
protected get pageInfo(): NotebookWizardPageInfo {
return this.wizard.wizardInfo.pages[this._pageIndex];
}
constructor(wizard: NotebookWizard, private _pageIndex: number) {
super(wizard.wizardInfo.pages[_pageIndex].title, wizard.wizardInfo.pages[_pageIndex].description || '', wizard);
constructor(
wizard: NotebookWizard,
protected _pageIndex: number,
title?: string,
description?: string
) {
super(
wizard.wizardInfo.pages[_pageIndex].title || title || '',
wizard.wizardInfo.pages[_pageIndex].description || description || '',
wizard
);
}
public initialize(): void {
const self = this;
initializeWizardPage({
container: this.wizard.wizardObject,
inputComponents: this.wizard.inputComponents,
wizardInfo: this.wizard.wizardInfo,
pageInfo: this.pageInfo,
page: this.pageObject,
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
self.wizard.registerDisposable(disposable);
this.wizard.registerDisposable(disposable);
},
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
self.inputComponents[name] = { component: component };
onNewInputComponentCreated: (
name: string,
inputComponentInfo: InputComponentInfo
): void => {
if (name) {
this.wizard.inputComponents[name] = inputComponentInfo;
}
},
onNewValidatorCreated: (validator: Validator): void => {
self.validators.push(validator);
}
this.validators.push(validator);
},
});
}
public onLeave() {
setModelValues(this.inputComponents, this.wizard.model);
public onLeave(): void {
// The following callback registration clears previous navigation validators.
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
return true;
});
}
public onEnter() {
public onEnter(): void {
if (this.pageInfo.isSummaryPage) {
setModelValues(this.wizard.inputComponents, this.wizard.model);
}
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
this.wizard.wizardObject.message = { text: '' };
if (pcInfo.newPage > pcInfo.lastPage) {
const messages: string[] = [];
this.validators.forEach(validator => {
this.validators.forEach((validator) => {
const result = validator();
if (!result.valid) {
messages.push(result.message);
@@ -67,9 +82,15 @@ export class NotebookWizardPage extends WizardPageBase<NotebookWizard> {
if (messages.length > 0) {
this.wizard.wizardObject.message = {
text: messages.length === 1 ? messages[0] : localize('wizardPage.ValidationError', "There are some errors on this page, click 'Show Details' to view the errors."),
text:
messages.length === 1
? messages[0]
: localize(
"wizardPage.ValidationError",
"There are some errors on this page, click 'Show Details' to view the errors."
),
description: messages.length === 1 ? undefined : messages.join(EOL),
level: azdata.window.MessageLevel.Error
level: azdata.window.MessageLevel.Error,
};
}
return messages.length === 0;