Skip to content

Commit

Permalink
GenerateToNotebook and Deploy buttons for Notebook Wizards (microsof…
Browse files Browse the repository at this point in the history
…t#12656)

* enable userChooses how to run notebook

* arc ext changes

* nb fixes

* working version

* revert unneeded changes

* fix comments

* Update interfaces.ts

* fix comments

* fix comments

* fix comments

* runAllCells instead of background execute

* pr feedback

* PR feedback

* pr feedback

* arc ext changes for new WizardInfo syntax

* fix doc comments

* pr feedback
  • Loading branch information
ranasaria authored Sep 30, 2020
1 parent fd5acf7 commit b8de69d
Show file tree
Hide file tree
Showing 15 changed files with 203 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
{
"cell_type": "markdown",
"source": [
"### **Setup and Check Prerequisites**"
"### **Setup**"
],
"metadata": {
"azdata_cell_guid": "e3dd8e75-e15f-44b4-81fc-1f54d6f0b1e2"
Expand Down
24 changes: 18 additions & 6 deletions extensions/arc/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,13 @@
"notebookWizard": {
"notebook": "./notebooks/arcDeployment/deploy.arc.data.controller.ipynb",
"type": "new-arc-control-plane",
"runNotebook": false,
"doneAction": {
"label": "%deploy.done.action%"
},
"scriptAction": {
"label": "%deploy.script.action%"
},
"codeCellInsertionPosition": 5,
"actionText": "%deploy.script.to.notebook.action%",
"title": "%arc.data.controller.new.wizard.title%",
"name": "arc.data.controller.new.wizard",
"labelPosition": "left",
Expand Down Expand Up @@ -521,9 +525,13 @@
{
"notebookWizard": {
"notebook": "./notebooks/arcDeployment/deploy.sql.existing.arc.ipynb",
"runNotebook": false,
"doneAction": {
"label": "%deploy.done.action%"
},
"scriptAction": {
"label": "%deploy.script.action%"
},
"codeCellInsertionPosition": 5,
"actionText": "%deploy.script.to.notebook.action%",
"title": "%arc.sql.wizard.title%",
"name": "arc.sql.wizard",
"labelPosition": "left",
Expand Down Expand Up @@ -682,9 +690,13 @@
{
"notebookWizard": {
"notebook": "./notebooks/arcDeployment/deploy.postgres.existing.arc.ipynb",
"runNotebook": false,
"doneAction": {
"label": "%deploy.done.action%"
},
"scriptAction": {
"label": "%deploy.script.action%"
},
"codeCellInsertionPosition": 5,
"actionText": "%deploy.script.to.notebook.action%",
"title": "%arc.postgres.wizard.title%",
"name": "arc.postgres.wizard",
"labelPosition": "left",
Expand Down
4 changes: 2 additions & 2 deletions extensions/arc/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
"arc.data.controller.summary.location": "Location",
"arc.data.controller.arc.data.controller.agreement": "I accept {0} and {1}.",
"microsoft.agreement.privacy.statement":"Microsoft Privacy Statement",
"deploy.script.to.notebook.action":"Script to notebook",

"deploy.script.action":"Script to notebook",
"deploy.done.action":"Deploy",

"resource.type.arc.sql.display.name": "Azure SQL managed instance - Azure Arc (preview)",
"resource.type.arc.postgres.display.name": "PostgreSQL Hyperscale server groups - Azure Arc (preview)",
Expand Down
44 changes: 36 additions & 8 deletions extensions/resource-deployment/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,21 +114,53 @@ export interface BdcWizardInfo {
notebook: string | NotebookPathInfo;
type: BdcDeploymentType;
}
/**
* An object that configures Script and Done buttons of the wizard.
*/
export interface WizardAction {
label?: string
}

/**
* This object defines the shape, form and behavior of a Notebook Wizard.
*/
export interface NotebookWizardInfo extends WizardInfoBase {
/**
* path to the template python notebook that is modified with variables collected in the wizard. A copy of this modified notebook is executed at the end of the wizard either from commonadline of from notebook editor in ADS.
*/
notebook: string | NotebookPathInfo;
runNotebook?: boolean;
/**
* 0 based position number where the variables values are inserted into the notebook as python statements.
*/
codeCellInsertionPosition?: number;
/**
* This array defines the json for the pages of this wizard.
*/
pages: NotebookWizardPageInfo[]
}

export interface WizardInfoBase extends FieldInfoBase {
taskName?: string;
type?: DeploymentType;
actionText?: string;
/**
* done button attributes.
*/
doneAction: WizardAction;
/**
* script button attributes.
*/
scriptAction?: WizardAction;
/**
* title displayed on every page of the wizard
*/
title: string;
name?: string;
/**
* This array defines the json for the pages of this wizard.
*/
pages: PageInfoBase[];
/**
* if true an auto generated summary page is inserted at the end of the wizard
*/
isSummaryPageAutoGenerated?: boolean
}

Expand Down Expand Up @@ -401,11 +433,7 @@ export const enum BdcDeploymentType {
ExistingOpenShift = 'existing-openshift'
}

export const enum ArcDeploymentType {
NewControlPlane = 'new-control-plane'
}

export type DeploymentType = ArcDeploymentType | BdcDeploymentType;
export type DeploymentType = BdcDeploymentType;

export interface Command {
command: string;
Expand Down
4 changes: 3 additions & 1 deletion extensions/resource-deployment/src/localizedConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vscode-nls';
import { FieldType, OptionsType } from './interfaces';
import { OptionsSourceType } from './helpers/optionSources';
import { FieldType, OptionsType } from './interfaces';

const localize = nls.loadMessageBundle();

Expand Down Expand Up @@ -37,3 +37,5 @@ export const optionsTypeRadioOrDropdown = localize('optionsTypeRadioOrDropdown',
export const azdataEulaNotAccepted = localize('azdataEulaNotAccepted', "Deployment cannot continue. Azure Data CLI license terms have not yet been accepted. Please accept the EULA to enable the features that requires Azure Data CLI.");
export const azdataEulaDeclined = localize('azdataEulaDeclined', "Deployment cannot continue. Azure Data CLI license terms were declined.You can either Accept EULA to continue or Cancel this operation");
export const acceptEulaAndSelect = localize('deploymentDialog.RecheckEulaButton', "Accept EULA & Select");
export const scriptToNotebook = localize('ui.ScriptToNotebookButton', "Script");
export const deployNotebook = localize('ui.DeployButton', "Run");
31 changes: 16 additions & 15 deletions extensions/resource-deployment/src/services/notebookService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ export interface NotebookExecutionResult {
}

export interface INotebookService {
launchNotebook(notebook: string | NotebookPathInfo): Promise<azdata.nb.NotebookEditor>;
launchNotebookWithEdits(notebook: string | NotebookPathInfo, cellStatements: string[], insertionPosition?: number): Promise<void>;
launchNotebookWithContent(title: string, content: string): Promise<azdata.nb.NotebookEditor>;
openNotebook(notebook: string | NotebookPathInfo): Promise<azdata.nb.NotebookEditor>;
openNotebookWithEdits(notebook: string | NotebookPathInfo, cellStatements: string[], insertionPosition?: number): Promise<azdata.nb.NotebookEditor>;
openNotebookWithContent(title: string, content: string): Promise<azdata.nb.NotebookEditor>;
getNotebook(notebook: string | NotebookPathInfo): Promise<Notebook>;
getNotebookPath(notebook: string | NotebookPathInfo): string;
executeNotebook(notebook: any, env?: NodeJS.ProcessEnv): Promise<NotebookExecutionResult>;
Expand All @@ -47,38 +47,39 @@ export class NotebookService implements INotebookService {
constructor(private platformService: IPlatformService, private extensionPath: string) { }

/**
* Launch notebook with file path
* Open notebook with file path
* @param notebook the path of the notebook
*/
async launchNotebook(notebook: string | NotebookPathInfo): Promise<azdata.nb.NotebookEditor> {
async openNotebook(notebook: string | NotebookPathInfo): Promise<azdata.nb.NotebookEditor> {
const notebookPath = await this.getNotebookFullPath(notebook);
return await this.showNotebookAsUntitled(notebookPath);
}

/**
* Inserts cell code given by {@param cellStatements} in an existing notebook given by {@param notebook} file path at the location
* {@param insertionPosition} and then launches the edited notebook.
* {@param insertionPosition} and then opens the edited notebook.
*
* @param notebook - the path to notebook that needs to be launched
* @param notebook - the path to notebook that needs to be opened
* @param cellStatements - array of statements to be inserted in a cell
* @param insertionPosition - the position at which cells are inserted. Default is a new cell at the beginning of the notebook.
*/
async launchNotebookWithEdits(notebook: string, cellStatements: string[], insertionPosition: number = 0): Promise<void> {
const openedNotebook = await this.launchNotebook(notebook);
async openNotebookWithEdits(notebook: string, cellStatements: string[], insertionPosition: number = 0): Promise<azdata.nb.NotebookEditor> {
const openedNotebook = await this.openNotebook(notebook);
await openedNotebook.edit((editBuilder: azdata.nb.NotebookEditorEdit) => {
editBuilder.insertCell({
cell_type: 'code',
source: cellStatements
}, insertionPosition);
});
return openedNotebook;
}

/**
* Launch notebook with file path
* Open notebook with given contents
* @param title the title of the notebook
* @param content the notebook content
*/
async launchNotebookWithContent(title: string, content: string): Promise<azdata.nb.NotebookEditor> {
async openNotebookWithContent(title: string, content: string): Promise<azdata.nb.NotebookEditor> {
const uri: vscode.Uri = vscode.Uri.parse(`untitled:${this.findNextUntitledEditorName(title)}`);
return await azdata.nb.showNotebookDocument(uri, {
connectionProfile: undefined,
Expand Down Expand Up @@ -150,11 +151,11 @@ export class NotebookService implements INotebookService {
platformService.logToOutputChannel(taskFailedMessage);
if (selectedOption === viewErrorDetail) {
try {
await this.launchNotebookWithContent(`${tempNotebookPrefix}-${getDateTimeString()}`, result.outputNotebook);
await this.openNotebookWithContent(`${tempNotebookPrefix}-${getDateTimeString()}`, result.outputNotebook);
} catch (error) {
const launchNotebookError = localize('resourceDeployment.FailedToOpenNotebook', "An error occurred launching the output notebook. {1}{2}.", EOL, getErrorMessage(error));
platformService.logToOutputChannel(launchNotebookError);
vscode.window.showErrorMessage(launchNotebookError);
const openNotebookError = localize('resourceDeployment.FailedToOpenNotebook', "An error occurred opening the output notebook. {1}{2}.", EOL, getErrorMessage(error));
platformService.logToOutputChannel(openNotebookError);
vscode.window.showErrorMessage(openNotebookError);
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import { DeploymentProvider, instanceOfAzureSQLVMDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfNotebookBasedDialogInfo, instanceOfNotebookDeploymentProvider, instanceOfNotebookWizardDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfWizardDeploymentProvider, NotebookInfo, NotebookPathInfo, ResourceType, ResourceTypeOption } from '../interfaces';
import { DeployAzureSQLVMWizard } from '../ui/deployAzureSQLVMWizard/deployAzureSQLVMWizard';
import { DeployClusterWizard } from '../ui/deployClusterWizard/deployClusterWizard';
import { DeploymentInputDialog } from '../ui/deploymentInputDialog';
import { NotebookWizard } from '../ui/notebookWizard/notebookWizard';
import { AzdataService } from './azdataService';
import { KubeService } from './kubeService';
import { INotebookService } from './notebookService';
import { IPlatformService } from './platformService';
import { IToolsService } from './toolsService';
import { ResourceType, ResourceTypeOption, NotebookPathInfo, DeploymentProvider, instanceOfWizardDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfNotebookDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfNotebookBasedDialogInfo, instanceOfNotebookWizardDeploymentProvider, NotebookInfo, instanceOfAzureSQLVMDeploymentProvider } from '../interfaces';
import { DeployClusterWizard } from '../ui/deployClusterWizard/deployClusterWizard';
import { DeploymentInputDialog } from '../ui/deploymentInputDialog';

import { KubeService } from './kubeService';
import { AzdataService } from './azdataService';
import { NotebookWizard } from '../ui/notebookWizard/notebookWizard';
import { DeployAzureSQLVMWizard } from '../ui/deployAzureSQLVMWizard/deployAzureSQLVMWizard';
const localize = nls.loadMessageBundle();

export interface IResourceTypeService {
Expand Down Expand Up @@ -257,7 +257,7 @@ export class ResourceTypeService implements IResourceTypeService {
const dialog = new DeploymentInputDialog(this.notebookService, this.platformService, this.toolsService, provider.dialog);
dialog.open();
} else if (instanceOfNotebookDeploymentProvider(provider)) {
this.notebookService.launchNotebook(provider.notebook);
this.notebookService.openNotebook(provider.notebook);
} else if (instanceOfDownloadDeploymentProvider(provider)) {
const taskName = localize('resourceDeployment.DownloadAndLaunchTaskName', "Download and launch installer, URL: {0}", provider.downloadUrl);
azdata.tasks.startBackgroundOperation({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export class DeployAzureSQLVMWizard extends WizardBase<DeployAzureSQLVMWizard, W
const variableValueStatements = this.model.getCodeCellContentForNotebook();
const insertionPosition = 2; // Cell number 5 is the position where the python variable setting statements need to be inserted in this.wizardInfo.notebook.
try {
await this.notebookService.launchNotebookWithEdits(this.wizardInfo.notebook, variableValueStatements, insertionPosition);
await this.notebookService.openNotebookWithEdits(this.wizardInfo.notebook, variableValueStatements, insertionPosition);
} catch (error) {
vscode.window.showErrorMessage(error);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export class DeployClusterWizard extends WizardBase<DeployClusterWizard, WizardP
const variableValueStatements = this.model.getCodeCellContentForNotebook(this.toolsService.toolsForCurrentProvider);
const insertionPosition = 5; // Cell number 5 is the position where the python variable setting statements need to be inserted in this.wizardInfo.notebook.
try {
await this.notebookService.launchNotebookWithEdits(this.wizardInfo.notebook, variableValueStatements, insertionPosition);
await this.notebookService.openNotebookWithEdits(this.wizardInfo.notebook, variableValueStatements, insertionPosition);
} catch (error) {
vscode.window.showErrorMessage(getErrorMessage(error));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export class DeploymentInputDialog extends DialogBase {
const notebook = Array.isArray(this.dialogInfo.notebook) ?
this.dialogInfo.notebook.find(nb => nb.type === model.getStringValue(NotebookTypeVariableName))?.path :
this.dialogInfo.notebook;
this.notebookService.launchNotebook(notebook!).catch(error => {
this.notebookService.openNotebook(notebook!).catch(error => {
vscode.window.showErrorMessage(error);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as vscode from 'vscode';
import * as nls from 'vscode-nls';
import * as loc from '../../localizedConstants';
import { INotebookService, Notebook } from '../../services/notebookService';
import { IToolsService } from '../../services/toolsService';
import { Model } from '../model';
Expand All @@ -14,8 +14,6 @@ import { IPlatformService } from './../../services/platformService';
import { NotebookWizardAutoSummaryPage } from './notebookWizardAutoSummaryPage';
import { NotebookWizardPage } from './notebookWizardPage';

const localize = nls.loadMessageBundle();

export class NotebookWizard extends WizardBase<NotebookWizard, NotebookWizardPage, Model> {
private _inputComponents: InputComponents = {};

Expand All @@ -40,7 +38,8 @@ export class NotebookWizard extends WizardBase<NotebookWizard, NotebookWizardPag
if (this._wizardInfo.codeCellInsertionPosition === undefined) {
this._wizardInfo.codeCellInsertionPosition = 0;
}
this.wizardObject.doneButton.label = _wizardInfo.actionText || this.wizardObject.doneButton.label;
this.wizardObject.doneButton.label = _wizardInfo.doneAction?.label || loc.deployNotebook;
this.wizardObject.generateScriptButton.label = _wizardInfo.scriptAction?.label || loc.scriptToNotebook;
}

public get deploymentType(): DeploymentType | undefined {
Expand All @@ -49,17 +48,37 @@ export class NotebookWizard extends WizardBase<NotebookWizard, NotebookWizardPag

protected initialize(): void {
this.setPages(this.getPages());
this.wizardObject.generateScriptButton.hidden = true;
this.wizardInfo.actionText = this.wizardInfo.actionText || localize('notebookWizard.ScriptToNotebook', "Script to Notebook");
this.wizardObject.doneButton.label = this.wizardInfo.actionText;
}

protected onCancel(): void {
}

protected async onGenerateScript(): Promise<void> {
try {
const notebook = await this.prepareNotebookAndEnvironment();
await this.openNotebook(notebook);
} catch (error) {
vscode.window.showErrorMessage(error);
}
}
protected async onOk(): Promise<void> {
try {
const notebook = await this.prepareNotebookAndEnvironment();
const openedNotebook = await this.openNotebook(notebook);
openedNotebook.runAllCells();
} catch (error) {
vscode.window.showErrorMessage(error);
}
}

private async openNotebook(notebook: Notebook) {
const notebookPath = this.notebookService.getNotebookPath(this.wizardInfo.notebook);
return await this.notebookService.openNotebookWithContent(notebookPath, JSON.stringify(notebook, undefined, 4));
}

private async prepareNotebookAndEnvironment() {
await setModelValues(this.inputComponents, this.model);
const env: NodeJS.ProcessEnv = {};
const env: NodeJS.ProcessEnv = process.env;
this.model.setEnvironmentVariables(env, (varName) => {
const isPassword = !!this.inputComponents[varName]?.isPassword;
return isPassword;
Expand All @@ -85,17 +104,7 @@ export class NotebookWizard extends WizardBase<NotebookWizard, NotebookWizardPag
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 notebookPath = this.notebookService.getNotebookPath(this.wizardInfo.notebook);
await this.notebookService.launchNotebookWithContent(notebookPath, JSON.stringify(notebook, undefined, 4));
}
} catch (error) {
vscode.window.showErrorMessage(error);
}
return notebook;
}

private getPages(): NotebookWizardPage[] {
Expand Down
Loading

0 comments on commit b8de69d

Please sign in to comment.