mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Features to resource deployment to add arc control create in ARC extension (#10088)
* 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
This commit is contained in:
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -72,6 +72,7 @@
|
|||||||
"type": "chrome",
|
"type": "chrome",
|
||||||
"request": "attach",
|
"request": "attach",
|
||||||
"name": "Attach to azuredatastudio",
|
"name": "Attach to azuredatastudio",
|
||||||
|
"timeout": 50000,
|
||||||
"port": 9222
|
"port": 9222
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -237,7 +237,7 @@
|
|||||||
],
|
],
|
||||||
"providers": [
|
"providers": [
|
||||||
{
|
{
|
||||||
"wizard": {
|
"bdcWizard": {
|
||||||
"type": "new-aks",
|
"type": "new-aks",
|
||||||
"notebook": "%bdc-2019-aks-notebook%"
|
"notebook": "%bdc-2019-aks-notebook%"
|
||||||
},
|
},
|
||||||
@@ -257,7 +257,7 @@
|
|||||||
"when": "target=new-aks&&version=bdc2019"
|
"when": "target=new-aks&&version=bdc2019"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"wizard": {
|
"bdcWizard": {
|
||||||
"type": "existing-aks",
|
"type": "existing-aks",
|
||||||
"notebook": "%bdc-2019-existing-aks-notebook%"
|
"notebook": "%bdc-2019-existing-aks-notebook%"
|
||||||
},
|
},
|
||||||
@@ -272,7 +272,7 @@
|
|||||||
"when": "target=existing-aks&&version=bdc2019"
|
"when": "target=existing-aks&&version=bdc2019"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"wizard": {
|
"bdcWizard": {
|
||||||
"type": "existing-kubeadm",
|
"type": "existing-kubeadm",
|
||||||
"notebook": "%bdc-2019-existing-kubeadm-notebook%"
|
"notebook": "%bdc-2019-existing-kubeadm-notebook%"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,3 +5,4 @@
|
|||||||
|
|
||||||
export const DeploymentConfigurationKey: string = 'deployment';
|
export const DeploymentConfigurationKey: string = 'deployment';
|
||||||
export const AzdataInstallLocationKey: string = 'azdataInstallLocation';
|
export const AzdataInstallLocationKey: string = 'azdataInstallLocation';
|
||||||
|
export const ToolsInstallPath = 'AZDATA_NB_VAR_TOOLS_INSTALLATION_PATH';
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
@@ -40,8 +41,12 @@ export interface DialogDeploymentProvider extends DeploymentProviderBase {
|
|||||||
dialog: DialogInfo;
|
dialog: DialogInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface WizardDeploymentProvider extends DeploymentProviderBase {
|
export interface BdcWizardDeploymentProvider extends DeploymentProviderBase {
|
||||||
wizard: WizardInfo;
|
bdcWizard: WizardInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotebookWizardDeploymentProvider extends DeploymentProviderBase {
|
||||||
|
notebookWizard: NotebookWizardInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookDeploymentProvider extends DeploymentProviderBase {
|
export interface NotebookDeploymentProvider extends DeploymentProviderBase {
|
||||||
@@ -64,8 +69,12 @@ export function instanceOfDialogDeploymentProvider(obj: any): obj is DialogDeplo
|
|||||||
return obj && 'dialog' in obj;
|
return obj && 'dialog' in obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function instanceOfWizardDeploymentProvider(obj: any): obj is WizardDeploymentProvider {
|
export function instanceOfWizardDeploymentProvider(obj: any): obj is BdcWizardDeploymentProvider {
|
||||||
return obj && 'wizard' in obj;
|
return obj && 'bdcWizard' in obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function instanceOfNotebookWizardDeploymentProvider(obj: any): obj is NotebookWizardDeploymentProvider {
|
||||||
|
return obj && 'notebookWizard' in obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function instanceOfNotebookDeploymentProvider(obj: any): obj is NotebookDeploymentProvider {
|
export function instanceOfNotebookDeploymentProvider(obj: any): obj is NotebookDeploymentProvider {
|
||||||
@@ -89,13 +98,31 @@ export interface DeploymentProviderBase {
|
|||||||
when: string;
|
when: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DeploymentProvider = DialogDeploymentProvider | WizardDeploymentProvider | NotebookDeploymentProvider | WebPageDeploymentProvider | DownloadDeploymentProvider | CommandDeploymentProvider;
|
export type DeploymentProvider = DialogDeploymentProvider | BdcWizardDeploymentProvider | NotebookWizardDeploymentProvider | NotebookDeploymentProvider | WebPageDeploymentProvider | DownloadDeploymentProvider | CommandDeploymentProvider;
|
||||||
|
|
||||||
export interface WizardInfo {
|
export interface WizardInfo {
|
||||||
notebook: string | NotebookInfo;
|
notebook: string | NotebookInfo;
|
||||||
type: BdcDeploymentType;
|
type: BdcDeploymentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NotebookWizardInfo extends WizardInfoBase {
|
||||||
|
notebook: string | NotebookInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WizardInfoBase extends SharedFieldAttributes {
|
||||||
|
taskName?: string;
|
||||||
|
type?: DeploymentType;
|
||||||
|
runNotebook?: boolean;
|
||||||
|
actionText?: string;
|
||||||
|
title: string;
|
||||||
|
pages: NotebookWizardPageInfo[];
|
||||||
|
summaryPage: NotebookWizardPageInfo;
|
||||||
|
generateSummaryPage: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NotebookWizardPageInfo extends PageInfoBase {
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
export interface NotebookBasedDialogInfo extends DialogInfoBase {
|
export interface NotebookBasedDialogInfo extends DialogInfoBase {
|
||||||
notebook: string | NotebookInfo;
|
notebook: string | NotebookInfo;
|
||||||
runNotebook?: boolean;
|
runNotebook?: boolean;
|
||||||
@@ -123,20 +150,24 @@ export interface DialogInfoBase {
|
|||||||
actionText?: string;
|
actionText?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DialogTabInfo {
|
export interface DialogTabInfo extends PageInfoBase {
|
||||||
title: string;
|
|
||||||
sections: SectionInfo[];
|
|
||||||
labelWidth?: string;
|
|
||||||
inputWidth?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SectionInfo {
|
export interface PageInfoBase extends SharedFieldAttributes {
|
||||||
title: string;
|
title: string;
|
||||||
fields?: FieldInfo[]; // Use this if the dialog is not wide. All fields will be displayed in one column, label will be placed on top of the input component.
|
isSummaryPage?: boolean;
|
||||||
rows?: RowInfo[]; // Use this for wide dialog or wizard. label will be placed to the left of the input component.
|
sections: SectionInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SharedFieldAttributes {
|
||||||
labelWidth?: string;
|
labelWidth?: string;
|
||||||
inputWidth?: string;
|
inputWidth?: string;
|
||||||
labelPosition?: LabelPosition; // Default value is top
|
labelPosition?: LabelPosition; // Default value is top
|
||||||
|
}
|
||||||
|
export interface SectionInfo extends SharedFieldAttributes {
|
||||||
|
title?: string;
|
||||||
|
fields?: FieldInfo[]; // Use this if the dialog is not wide. All fields will be displayed in one column, label will be placed on top of the input component.
|
||||||
|
rows?: RowInfo[]; // Use this for wide dialog or wizard. label will be placed to the left of the input component.
|
||||||
collapsible?: boolean;
|
collapsible?: boolean;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
spaceBetweenFields?: string;
|
spaceBetweenFields?: string;
|
||||||
@@ -146,9 +177,13 @@ export interface RowInfo {
|
|||||||
fields: FieldInfo[];
|
fields: FieldInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FieldInfo {
|
export interface SubFieldInfo {
|
||||||
label: string;
|
label: string;
|
||||||
variableName?: string;
|
variableName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FieldInfo extends SubFieldInfo, SharedFieldAttributes {
|
||||||
|
subFields?: SubFieldInfo[];
|
||||||
type: FieldType;
|
type: FieldType;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
confirmationRequired?: boolean;
|
confirmationRequired?: boolean;
|
||||||
@@ -162,21 +197,26 @@ export interface FieldInfo {
|
|||||||
options?: string[] | azdata.CategoryValue[];
|
options?: string[] | azdata.CategoryValue[];
|
||||||
placeHolder?: string;
|
placeHolder?: string;
|
||||||
userName?: string; // needed for sql server's password complexity requirement check, password can not include the login name.
|
userName?: string; // needed for sql server's password complexity requirement check, password can not include the login name.
|
||||||
labelWidth?: string;
|
|
||||||
inputWidth?: string;
|
|
||||||
description?: string;
|
description?: string;
|
||||||
labelPosition?: LabelPosition; // overwrite the labelPosition of SectionInfo.
|
|
||||||
fontStyle?: FontStyle;
|
fontStyle?: FontStyle;
|
||||||
labelFontWeight?: FontWeight;
|
labelFontWeight?: FontWeight;
|
||||||
|
textFontWeight?: FontWeight;
|
||||||
links?: azdata.LinkArea[];
|
links?: azdata.LinkArea[];
|
||||||
editable?: boolean; // for editable dropdown,
|
editable?: boolean; // for editable drop-down,
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AzureAccountFieldInfo extends FieldInfo {
|
export interface KubeClusterContextFieldInfo extends FieldInfo {
|
||||||
|
configFileVariableName?: string;
|
||||||
|
}
|
||||||
|
export interface AzureAccountFieldInfo extends AzureLocationsFieldInfo {
|
||||||
subscriptionVariableName?: string;
|
subscriptionVariableName?: string;
|
||||||
resourceGroupVariableName?: string;
|
resourceGroupVariableName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureLocationsFieldInfo extends FieldInfo {
|
||||||
locationVariableName?: string;
|
locationVariableName?: string;
|
||||||
|
displayLocationVariableName?: string;
|
||||||
locations?: string[]
|
locations?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,9 +242,13 @@ export enum FieldType {
|
|||||||
SQLPassword = 'sql_password',
|
SQLPassword = 'sql_password',
|
||||||
Password = 'password',
|
Password = 'password',
|
||||||
Options = 'options',
|
Options = 'options',
|
||||||
|
RadioOptions = 'radio_options',
|
||||||
ReadonlyText = 'readonly_text',
|
ReadonlyText = 'readonly_text',
|
||||||
Checkbox = 'checkbox',
|
Checkbox = 'checkbox',
|
||||||
AzureAccount = 'azure_account'
|
AzureAccount = 'azure_account',
|
||||||
|
AzureLocations = 'azure_locations',
|
||||||
|
FilePicker = 'file_picker',
|
||||||
|
KubeClusterContextPicker = 'kube_cluster_context_picker'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NotebookInfo {
|
export interface NotebookInfo {
|
||||||
@@ -278,6 +322,12 @@ export const enum BdcDeploymentType {
|
|||||||
ExistingKubeAdm = 'existing-kubeadm'
|
ExistingKubeAdm = 'existing-kubeadm'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum ArcDeploymentType {
|
||||||
|
NewControlPlane = 'new-control-plane'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeploymentType = ArcDeploymentType | BdcDeploymentType;
|
||||||
|
|
||||||
export interface Command {
|
export interface Command {
|
||||||
command: string;
|
command: string;
|
||||||
sudo?: boolean;
|
sudo?: boolean;
|
||||||
|
|||||||
@@ -11,3 +11,7 @@ export const account = localize('azure.account', "Azure Account");
|
|||||||
export const subscription = localize('azure.account.subscription', "Subscription");
|
export const subscription = localize('azure.account.subscription', "Subscription");
|
||||||
export const resourceGroup = localize('azure.account.resourceGroup', "Resource Group");
|
export const resourceGroup = localize('azure.account.resourceGroup', "Resource Group");
|
||||||
export const location = localize('azure.account.location', "Azure Location");
|
export const location = localize('azure.account.location', "Azure Location");
|
||||||
|
export const browse = localize('filePicker.browse', "Browse");
|
||||||
|
export const select = localize('filePicker.select', "Select");
|
||||||
|
export const kubeConfigFilePath = localize('kubeConfigClusterPicker.kubeConfigFilePatht', "Kube config file path");
|
||||||
|
export const clusterContextNotFound = localize('kubeConfigClusterPicker.clusterContextNotFound', "No cluster context information found");
|
||||||
|
|||||||
@@ -14,39 +14,49 @@ export interface KubeClusterContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IKubeService {
|
export interface IKubeService {
|
||||||
getDefautConfigPath(): string;
|
getDefaultConfigPath(): string;
|
||||||
getClusterContexts(configFile: string): Promise<KubeClusterContext[]>;
|
getClusterContexts(configFile: string): Promise<KubeClusterContext[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KubeService implements IKubeService {
|
export class KubeService implements IKubeService {
|
||||||
getDefautConfigPath(): string {
|
getDefaultConfigPath(): string {
|
||||||
return path.join(os.homedir(), '.kube', 'config');
|
return getDefaultKubeConfigPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
getClusterContexts(configFile: string): Promise<KubeClusterContext[]> {
|
getClusterContexts(configFile: string): Promise<KubeClusterContext[]> {
|
||||||
return fs.promises.access(configFile).catch((error) => {
|
return getKubeConfigClusterContexts(configFile);
|
||||||
if (error && error.code === 'ENOENT') {
|
|
||||||
return [];
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}).then(() => {
|
|
||||||
const config = yamljs.load(configFile);
|
|
||||||
const rawContexts = <any[]>config['contexts'];
|
|
||||||
const currentContext = <string>config['current-context'];
|
|
||||||
const contexts: KubeClusterContext[] = [];
|
|
||||||
if (currentContext && rawContexts && rawContexts.length > 0) {
|
|
||||||
rawContexts.forEach(rawContext => {
|
|
||||||
const name = <string>rawContext['name'];
|
|
||||||
if (name) {
|
|
||||||
contexts.push({
|
|
||||||
name: name,
|
|
||||||
isCurrentContext: name === currentContext
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return contexts;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getKubeConfigClusterContexts(configFile: string): Promise<KubeClusterContext[]> {
|
||||||
|
return fs.promises.access(configFile).catch((error) => {
|
||||||
|
if (error && error.code === 'ENOENT') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}).then(() => {
|
||||||
|
const config = yamljs.load(configFile);
|
||||||
|
const rawContexts = <any[]>config['contexts'];
|
||||||
|
const currentContext = <string>config['current-context'];
|
||||||
|
const contexts: KubeClusterContext[] = [];
|
||||||
|
if (currentContext && rawContexts && rawContexts.length > 0) {
|
||||||
|
rawContexts.forEach(rawContext => {
|
||||||
|
const name = <string>rawContext['name'];
|
||||||
|
if (name) {
|
||||||
|
contexts.push({
|
||||||
|
name: name,
|
||||||
|
isCurrentContext: name === currentContext
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return contexts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDefaultKubeConfigPath(): string {
|
||||||
|
return path.join(os.homedir(), '.kube', 'config');
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import { EOL } from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { isString } from 'util';
|
import { isString } from 'util';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { IPlatformService } from './platformService';
|
|
||||||
import { NotebookInfo } from '../interfaces';
|
import { NotebookInfo } from '../interfaces';
|
||||||
import { getErrorMessage, getDateTimeString } from '../utils';
|
import { getDateTimeString, getErrorMessage } from '../utils';
|
||||||
|
import { IPlatformService } from './platformService';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export interface Notebook {
|
export interface Notebook {
|
||||||
@@ -36,6 +37,7 @@ export interface INotebookService {
|
|||||||
launchNotebookWithContent(title: string, content: string): Thenable<azdata.nb.NotebookEditor>;
|
launchNotebookWithContent(title: string, content: string): Thenable<azdata.nb.NotebookEditor>;
|
||||||
getNotebook(notebook: string | NotebookInfo): Promise<Notebook>;
|
getNotebook(notebook: string | NotebookInfo): Promise<Notebook>;
|
||||||
executeNotebook(notebook: any, env?: NodeJS.ProcessEnv): Promise<NotebookExecutionResult>;
|
executeNotebook(notebook: any, env?: NodeJS.ProcessEnv): Promise<NotebookExecutionResult>;
|
||||||
|
backgroundExecuteNotebook(taskName: string | undefined, notebookInfo: string | NotebookInfo, tempNoteBookPrefix: string, platformService: IPlatformService): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NotebookService implements INotebookService {
|
export class NotebookService implements INotebookService {
|
||||||
@@ -107,6 +109,43 @@ export class NotebookService implements INotebookService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public backgroundExecuteNotebook(taskName: string | undefined, notebookInfo: string | NotebookInfo, tempNotebookPrefix: string, platformService: IPlatformService): void {
|
||||||
|
azdata.tasks.startBackgroundOperation({
|
||||||
|
displayName: taskName!,
|
||||||
|
description: taskName!,
|
||||||
|
isCancelable: false,
|
||||||
|
operation: async op => {
|
||||||
|
op.updateStatus(azdata.TaskStatus.InProgress);
|
||||||
|
const notebook = await this.getNotebook(notebookInfo);
|
||||||
|
const result = await this.executeNotebook(notebook);
|
||||||
|
if (result.succeeded) {
|
||||||
|
op.updateStatus(azdata.TaskStatus.Succeeded);
|
||||||
|
} else {
|
||||||
|
op.updateStatus(azdata.TaskStatus.Failed, result.errorMessage);
|
||||||
|
if (result.outputNotebook) {
|
||||||
|
const viewErrorDetail = localize('resourceDeployment.ViewErrorDetail', "View error detail");
|
||||||
|
const taskFailedMessage = localize('resourceDeployment.BackgroundExecutionFailed', "The task \"{0}\" has failed.", taskName);
|
||||||
|
const selectedOption = await vscode.window.showErrorMessage(taskFailedMessage, viewErrorDetail);
|
||||||
|
platformService.logToOutputChannel(taskFailedMessage);
|
||||||
|
if (selectedOption === viewErrorDetail) {
|
||||||
|
try {
|
||||||
|
this.launchNotebookWithContent(`${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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const errorMessage = localize('resourceDeployment.TaskFailedWithNoOutputNotebook', "The task \"{0}\" failed and no output Notebook was generated.", taskName);
|
||||||
|
platformService.logToOutputChannel(errorMessage);
|
||||||
|
vscode.window.showErrorMessage(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async getNotebookFullPath(notebook: string | NotebookInfo): Promise<string> {
|
async getNotebookFullPath(notebook: string | NotebookInfo): Promise<string> {
|
||||||
const notebookPath = this.getNotebookPath(notebook);
|
const notebookPath = this.getNotebookPath(notebook);
|
||||||
let notebookExists = await this.platformService.fileExists(notebookPath);
|
let notebookExists = await this.platformService.fileExists(notebookPath);
|
||||||
|
|||||||
@@ -13,11 +13,13 @@ import * as nls from 'vscode-nls';
|
|||||||
import { INotebookService } from './notebookService';
|
import { INotebookService } from './notebookService';
|
||||||
import { IPlatformService } from './platformService';
|
import { IPlatformService } from './platformService';
|
||||||
import { IToolsService } from './toolsService';
|
import { IToolsService } from './toolsService';
|
||||||
import { ResourceType, ResourceTypeOption, NotebookInfo, DeploymentProvider, instanceOfWizardDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfNotebookDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfNotebookBasedDialogInfo } from '../interfaces';
|
import { ResourceType, ResourceTypeOption, NotebookInfo, DeploymentProvider, instanceOfWizardDeploymentProvider, instanceOfDialogDeploymentProvider, instanceOfNotebookDeploymentProvider, instanceOfDownloadDeploymentProvider, instanceOfWebPageDeploymentProvider, instanceOfCommandDeploymentProvider, instanceOfNotebookBasedDialogInfo, instanceOfNotebookWizardDeploymentProvider } from '../interfaces';
|
||||||
import { DeployClusterWizard } from '../ui/deployClusterWizard/deployClusterWizard';
|
import { DeployClusterWizard } from '../ui/deployClusterWizard/deployClusterWizard';
|
||||||
import { DeploymentInputDialog } from '../ui/deploymentInputDialog';
|
import { DeploymentInputDialog } from '../ui/deploymentInputDialog';
|
||||||
|
|
||||||
import { KubeService } from './kubeService';
|
import { KubeService } from './kubeService';
|
||||||
import { AzdataService } from './azdataService';
|
import { AzdataService } from './azdataService';
|
||||||
|
import { NotebookWizard } from '../ui/notebookWizard/notebookWizard';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export interface IResourceTypeService {
|
export interface IResourceTypeService {
|
||||||
@@ -66,8 +68,11 @@ export class ResourceTypeService implements IResourceTypeService {
|
|||||||
} else if (instanceOfDialogDeploymentProvider(provider) && instanceOfNotebookBasedDialogInfo(provider.dialog)) {
|
} else if (instanceOfDialogDeploymentProvider(provider) && instanceOfNotebookBasedDialogInfo(provider.dialog)) {
|
||||||
this.updateNotebookPath(provider.dialog, extensionPath);
|
this.updateNotebookPath(provider.dialog, extensionPath);
|
||||||
}
|
}
|
||||||
else if ('wizard' in provider) {
|
else if ('bdcWizard' in provider) {
|
||||||
this.updateNotebookPath(provider.wizard, extensionPath);
|
this.updateNotebookPath(provider.bdcWizard, extensionPath);
|
||||||
|
}
|
||||||
|
else if ('notebookWizard' in provider) {
|
||||||
|
this.updateNotebookPath(provider.notebookWizard, extensionPath);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -168,6 +173,7 @@ export class ResourceTypeService implements IResourceTypeService {
|
|||||||
resourceType.providers.forEach(provider => {
|
resourceType.providers.forEach(provider => {
|
||||||
const providerPositionInfo = `${positionInfo}, provider index: ${providerIndex} `;
|
const providerPositionInfo = `${positionInfo}, provider index: ${providerIndex} `;
|
||||||
if (!instanceOfWizardDeploymentProvider(provider)
|
if (!instanceOfWizardDeploymentProvider(provider)
|
||||||
|
&& !instanceOfNotebookWizardDeploymentProvider(provider)
|
||||||
&& !instanceOfDialogDeploymentProvider(provider)
|
&& !instanceOfDialogDeploymentProvider(provider)
|
||||||
&& !instanceOfNotebookDeploymentProvider(provider)
|
&& !instanceOfNotebookDeploymentProvider(provider)
|
||||||
&& !instanceOfDownloadDeploymentProvider(provider)
|
&& !instanceOfDownloadDeploymentProvider(provider)
|
||||||
@@ -203,24 +209,27 @@ export class ResourceTypeService implements IResourceTypeService {
|
|||||||
private getProvider(resourceType: ResourceType, selectedOptions: { option: string, value: string }[]): DeploymentProvider | undefined {
|
private getProvider(resourceType: ResourceType, selectedOptions: { option: string, value: string }[]): DeploymentProvider | undefined {
|
||||||
for (let i = 0; i < resourceType.providers.length; i++) {
|
for (let i = 0; i < resourceType.providers.length; i++) {
|
||||||
const provider = resourceType.providers[i];
|
const provider = resourceType.providers[i];
|
||||||
|
if (provider.when === undefined || provider.when.toString().toLowerCase() === 'true') {
|
||||||
|
return provider;
|
||||||
|
} else {
|
||||||
|
const expected = provider.when.replace(' ', '').split('&&').sort();
|
||||||
|
let actual: string[] = [];
|
||||||
|
selectedOptions.forEach(option => {
|
||||||
|
actual.push(`${option.option}=${option.value}`);
|
||||||
|
});
|
||||||
|
actual = actual.sort();
|
||||||
|
|
||||||
const expected = provider.when.replace(' ', '').split('&&').sort();
|
if (actual.length === expected.length) {
|
||||||
let actual: string[] = [];
|
let matches = true;
|
||||||
selectedOptions.forEach(option => {
|
for (let j = 0; j < actual.length; j++) {
|
||||||
actual.push(`${option.option}=${option.value}`);
|
if (actual[j] !== expected[j]) {
|
||||||
});
|
matches = false;
|
||||||
actual = actual.sort();
|
break;
|
||||||
|
}
|
||||||
if (actual.length === expected.length) {
|
}
|
||||||
let matches = true;
|
if (matches) {
|
||||||
for (let j = 0; j < actual.length; j++) {
|
return provider;
|
||||||
if (actual[j] !== expected[j]) {
|
|
||||||
matches = false;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (matches) {
|
|
||||||
return provider;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,7 +239,10 @@ export class ResourceTypeService implements IResourceTypeService {
|
|||||||
public startDeployment(provider: DeploymentProvider): void {
|
public startDeployment(provider: DeploymentProvider): void {
|
||||||
const self = this;
|
const self = this;
|
||||||
if (instanceOfWizardDeploymentProvider(provider)) {
|
if (instanceOfWizardDeploymentProvider(provider)) {
|
||||||
const wizard = new DeployClusterWizard(provider.wizard, new KubeService(), new AzdataService(this.platformService), this.notebookService);
|
const wizard = new DeployClusterWizard(provider.bdcWizard, new KubeService(), new AzdataService(this.platformService), this.notebookService);
|
||||||
|
wizard.open();
|
||||||
|
} else if (instanceOfNotebookWizardDeploymentProvider(provider)) {
|
||||||
|
const wizard = new NotebookWizard(provider.notebookWizard, this.notebookService, this.platformService);
|
||||||
wizard.open();
|
wizard.open();
|
||||||
} else if (instanceOfDialogDeploymentProvider(provider)) {
|
} else if (instanceOfDialogDeploymentProvider(provider)) {
|
||||||
const dialog = new DeploymentInputDialog(this.notebookService, this.platformService, provider.dialog);
|
const dialog = new DeploymentInputDialog(this.notebookService, this.platformService, provider.dialog);
|
||||||
|
|||||||
@@ -69,4 +69,3 @@ export const DockerRegistry_VariableName = 'AZDATA_NB_VAR_BDC_REGISTRY';
|
|||||||
export const DockerImageTag_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_IMAGE_TAG';
|
export const DockerImageTag_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_IMAGE_TAG';
|
||||||
export const DockerUsername_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_USERNAME';
|
export const DockerUsername_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_USERNAME';
|
||||||
export const DockerPassword_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_PASSWORD';
|
export const DockerPassword_VariableName = 'AZDATA_NB_VAR_BDC_DOCKER_PASSWORD';
|
||||||
export const ToolsInstallPath = 'AZDATA_NB_VAR_TOOLS_INSTALLATION_PATH';
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { KubeCtlToolName } from '../../services/tools/kubeCtlTool';
|
|||||||
import { getRuntimeBinaryPathEnvironmentVariableName } from '../../utils';
|
import { getRuntimeBinaryPathEnvironmentVariableName } from '../../utils';
|
||||||
import { Model } from '../model';
|
import { Model } from '../model';
|
||||||
import * as VariableNames from './constants';
|
import * as VariableNames from './constants';
|
||||||
|
import { ToolsInstallPath } from './../../constants';
|
||||||
|
|
||||||
export class DeployClusterWizardModel extends Model {
|
export class DeployClusterWizardModel extends Model {
|
||||||
constructor(public deploymentTarget: BdcDeploymentType) {
|
constructor(public deploymentTarget: BdcDeploymentType) {
|
||||||
@@ -167,7 +167,7 @@ export class DeployClusterWizardModel extends Model {
|
|||||||
}
|
}
|
||||||
const kubeCtlEnvVarName: string = getRuntimeBinaryPathEnvironmentVariableName(KubeCtlToolName);
|
const kubeCtlEnvVarName: string = getRuntimeBinaryPathEnvironmentVariableName(KubeCtlToolName);
|
||||||
statements.push(`os.environ["${kubeCtlEnvVarName}"] = "${this.escapeForNotebookCodeCell(process.env[kubeCtlEnvVarName]!)}"`);
|
statements.push(`os.environ["${kubeCtlEnvVarName}"] = "${this.escapeForNotebookCodeCell(process.env[kubeCtlEnvVarName]!)}"`);
|
||||||
statements.push(`os.environ["PATH"] = os.environ["PATH"] + "${delimiter}" + "${this.escapeForNotebookCodeCell(process.env[VariableNames.ToolsInstallPath]!)}"`);
|
statements.push(`os.environ["PATH"] = os.environ["PATH"] + "${delimiter}" + "${this.escapeForNotebookCodeCell(process.env[ToolsInstallPath]!)}"`);
|
||||||
statements.push(`print('Variables have been set successfully.')`);
|
statements.push(`print('Variables have been set successfully.')`);
|
||||||
return statements.map(line => line + EOL);
|
return statements.map(line => line + EOL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,13 @@
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DeployClusterWizard } from '../deployClusterWizard';
|
import { FieldType, LabelPosition, SectionInfo } from '../../../interfaces';
|
||||||
import { SectionInfo, FieldType, LabelPosition } from '../../../interfaces';
|
import { createSection, getDropdownComponent, InputComponents, InputComponent, setModelValues, Validator } from '../../modelViewUtils';
|
||||||
import { WizardPageBase } from '../../wizardPageBase';
|
import { WizardPageBase } from '../../wizardPageBase';
|
||||||
import { createSection, InputComponents, setModelValues, Validator, getDropdownComponent, MissingRequiredInformationErrorMessage } from '../../modelViewUtils';
|
import { AksName_VariableName, Location_VariableName, ResourceGroup_VariableName, SubscriptionId_VariableName, VMCount_VariableName, VMSize_VariableName } from '../constants';
|
||||||
import { SubscriptionId_VariableName, ResourceGroup_VariableName, Location_VariableName, AksName_VariableName, VMCount_VariableName, VMSize_VariableName } from '../constants';
|
import { DeployClusterWizard } from '../deployClusterWizard';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
const MissingRequiredInformationErrorMessage = localize('deployCluster.MissingRequiredInfoError', "Please fill out the required fields marked with red asterisks.");
|
||||||
|
|
||||||
export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
||||||
private inputComponents: InputComponents = {};
|
private inputComponents: InputComponents = {};
|
||||||
@@ -136,7 +137,7 @@ export class AzureSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
self.inputComponents[name] = { component: component };
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import * as vscode from 'vscode';
|
|||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DeployClusterWizard } from '../deployClusterWizard';
|
import { DeployClusterWizard } from '../deployClusterWizard';
|
||||||
import { SectionInfo, FieldType, LabelPosition } from '../../../interfaces';
|
import { SectionInfo, FieldType, LabelPosition } from '../../../interfaces';
|
||||||
import { createSection, InputComponents, setModelValues, Validator, getInputBoxComponent, isValidSQLPassword, getInvalidSQLPasswordMessage, getPasswordMismatchMessage } from '../../modelViewUtils';
|
import { createSection, InputComponents, setModelValues, Validator, getInputBoxComponent, isValidSQLPassword, getInvalidSQLPasswordMessage, getPasswordMismatchMessage, InputComponent } from '../../modelViewUtils';
|
||||||
import { WizardPageBase } from '../../wizardPageBase';
|
import { WizardPageBase } from '../../wizardPageBase';
|
||||||
import * as VariableNames from '../constants';
|
import * as VariableNames from '../constants';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
@@ -197,7 +197,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
self.inputComponents[name] = { component: component };
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
@@ -211,7 +211,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
self.inputComponents[name] = { component: component };
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
@@ -225,7 +225,7 @@ export class ClusterSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
self.wizard.registerDisposable(disposable);
|
self.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
self.inputComponents[name] = { component: component };
|
self.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import * as vscode from 'vscode';
|
|||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DeployClusterWizard } from '../deployClusterWizard';
|
import { DeployClusterWizard } from '../deployClusterWizard';
|
||||||
import { SectionInfo, FieldType } from '../../../interfaces';
|
import { SectionInfo, FieldType } from '../../../interfaces';
|
||||||
import { Validator, InputComponents, createSection, createGroupContainer, createLabel, createFlexContainer, createTextInput, createNumberInput, setModelValues, getInputBoxComponent, getCheckboxComponent, getDropdownComponent } from '../../modelViewUtils';
|
import { Validator, InputComponents, InputComponent, createSection, createGroupContainer, createLabel, createFlexContainer, createTextInput, createNumberInput, setModelValues, getInputBoxComponent, getCheckboxComponent, getDropdownComponent } from '../../modelViewUtils';
|
||||||
import { WizardPageBase } from '../../wizardPageBase';
|
import { WizardPageBase } from '../../wizardPageBase';
|
||||||
import * as VariableNames from '../constants';
|
import * as VariableNames from '../constants';
|
||||||
import { AuthenticationMode } from '../deployClusterWizardModel';
|
import { AuthenticationMode } from '../deployClusterWizardModel';
|
||||||
@@ -123,7 +123,7 @@ export class ServiceSettingsPage extends WizardPageBase<DeployClusterWizard> {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
this.wizard.registerDisposable(disposable);
|
this.wizard.registerDisposable(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
this.inputComponents[name] = { component: component };
|
this.inputComponents[name] = { component: component };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class TargetClusterContextPage extends WizardPageBase<DeployClusterWizard
|
|||||||
|
|
||||||
public onEnter() {
|
public onEnter() {
|
||||||
if (this.loadDefaultKubeConfigFile) {
|
if (this.loadDefaultKubeConfigFile) {
|
||||||
let defaultKubeConfigPath = this.wizard.kubeService.getDefautConfigPath();
|
let defaultKubeConfigPath = this.wizard.kubeService.getDefaultConfigPath();
|
||||||
this.loadClusterContexts(defaultKubeConfigPath);
|
this.loadClusterContexts(defaultKubeConfigPath);
|
||||||
this.loadDefaultKubeConfigFile = false;
|
this.loadDefaultKubeConfigFile = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,9 @@ import * as nls from 'vscode-nls';
|
|||||||
import { DialogBase } from './dialogBase';
|
import { DialogBase } from './dialogBase';
|
||||||
import { INotebookService } from '../services/notebookService';
|
import { INotebookService } from '../services/notebookService';
|
||||||
import { DialogInfo, instanceOfNotebookBasedDialogInfo, NotebookBasedDialogInfo } from '../interfaces';
|
import { DialogInfo, instanceOfNotebookBasedDialogInfo, NotebookBasedDialogInfo } from '../interfaces';
|
||||||
import { Validator, initializeDialog, InputComponents, setModelValues, InputValueTransformer } from './modelViewUtils';
|
import { Validator, initializeDialog, InputComponents, setModelValues, InputValueTransformer, InputComponent } from './modelViewUtils';
|
||||||
import { Model } from './model';
|
import { Model } from './model';
|
||||||
import { EOL } from 'os';
|
import { EOL } from 'os';
|
||||||
import { getDateTimeString, getErrorMessage } from '../utils';
|
|
||||||
import { IPlatformService } from '../services/platformService';
|
import { IPlatformService } from '../services/platformService';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
@@ -46,7 +45,7 @@ export class DeploymentInputDialog extends DialogBase {
|
|||||||
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
this._toDispose.push(disposable);
|
this._toDispose.push(disposable);
|
||||||
},
|
},
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.DropDownComponent | azdata.InputBoxComponent | azdata.CheckBoxComponent, inputValueTransformer?: InputValueTransformer): void => {
|
onNewInputComponentCreated: (name: string, component: InputComponent, inputValueTransformer?: InputValueTransformer): void => {
|
||||||
this.inputComponents[name] = { component: component, inputValueTransformer: inputValueTransformer };
|
this.inputComponents[name] = { component: component, inputValueTransformer: inputValueTransformer };
|
||||||
},
|
},
|
||||||
onNewValidatorCreated: (validator: Validator): void => {
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
@@ -88,39 +87,6 @@ export class DeploymentInputDialog extends DialogBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private executeNotebook(notebookDialogInfo: NotebookBasedDialogInfo): void {
|
private executeNotebook(notebookDialogInfo: NotebookBasedDialogInfo): void {
|
||||||
azdata.tasks.startBackgroundOperation({
|
this.notebookService.backgroundExecuteNotebook(notebookDialogInfo.taskName, notebookDialogInfo.notebook, 'deploy', this.platformService);
|
||||||
displayName: notebookDialogInfo.taskName!,
|
|
||||||
description: notebookDialogInfo.taskName!,
|
|
||||||
isCancelable: false,
|
|
||||||
operation: async op => {
|
|
||||||
op.updateStatus(azdata.TaskStatus.InProgress);
|
|
||||||
const notebook = await this.notebookService.getNotebook(notebookDialogInfo.notebook);
|
|
||||||
const result = await this.notebookService.executeNotebook(notebook);
|
|
||||||
if (result.succeeded) {
|
|
||||||
op.updateStatus(azdata.TaskStatus.Succeeded);
|
|
||||||
} else {
|
|
||||||
op.updateStatus(azdata.TaskStatus.Failed, result.errorMessage);
|
|
||||||
if (result.outputNotebook) {
|
|
||||||
const viewErrorDetail = localize('resourceDeployment.ViewErrorDetail', "View error detail");
|
|
||||||
const taskFailedMessage = localize('resourceDeployment.DeployFailed', "The task \"{0}\" has failed.", notebookDialogInfo.taskName);
|
|
||||||
const selectedOption = await vscode.window.showErrorMessage(taskFailedMessage, viewErrorDetail);
|
|
||||||
this.platformService.logToOutputChannel(taskFailedMessage);
|
|
||||||
if (selectedOption === viewErrorDetail) {
|
|
||||||
try {
|
|
||||||
this.notebookService.launchNotebookWithContent(`deploy-${getDateTimeString()}`, result.outputNotebook);
|
|
||||||
} catch (error) {
|
|
||||||
const launchNotebookError = localize('resourceDeployment.FailedToOpenNotebook', "An error occurred launching the output notebook. {1}{2}.", EOL, getErrorMessage(error));
|
|
||||||
this.platformService.logToOutputChannel(launchNotebookError);
|
|
||||||
vscode.window.showErrorMessage(launchNotebookError);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const errorMessage = localize('resourceDeployment.TaskFailedWithNoOutputNotebook', "The task \"{0}\" failed and no output Notebook was generated.", notebookDialogInfo.taskName);
|
|
||||||
this.platformService.logToOutputChannel(errorMessage);
|
|
||||||
vscode.window.showErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,26 @@
|
|||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
|
import { EOL, homedir as os_homedir } from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { DialogInfoBase, FieldType, FieldInfo, SectionInfo, LabelPosition, FontWeight, FontStyle, AzureAccountFieldInfo } from '../interfaces';
|
|
||||||
import { Model } from './model';
|
|
||||||
import { getDateTimeString } from '../utils';
|
|
||||||
import { azureResource } from '../../../azurecore/src/azureResource/azure-resource';
|
|
||||||
import * as azurecore from '../../../azurecore/src/azurecore';
|
import * as azurecore from '../../../azurecore/src/azurecore';
|
||||||
|
import { azureResource } from '../../../azurecore/src/azureResource/azure-resource';
|
||||||
|
import { AzureAccountFieldInfo, AzureLocationsFieldInfo, DialogInfoBase, FieldInfo, FieldType, FontStyle, FontWeight, LabelPosition, PageInfoBase, SectionInfo, KubeClusterContextFieldInfo } from '../interfaces';
|
||||||
import * as loc from '../localizedConstants';
|
import * as loc from '../localizedConstants';
|
||||||
import { EOL } from 'os';
|
import { getDefaultKubeConfigPath, getKubeConfigClusterContexts } from '../services/kubeService';
|
||||||
|
import { getDateTimeString, getErrorMessage } from '../utils';
|
||||||
|
import { WizardInfoBase } from './../interfaces';
|
||||||
|
import { Model } from './model';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export type Validator = () => { valid: boolean, message: string };
|
export type Validator = () => { valid: boolean, message: string };
|
||||||
export type InputValueTransformer = (inputValue: string) => string;
|
export type InputValueTransformer = (inputValue: string) => string;
|
||||||
export type InputComponents = { [s: string]: { component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent; inputValueTransformer?: InputValueTransformer } };
|
export type InputComponent = azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent | azdata.RadioButtonComponent;
|
||||||
|
export type InputComponents = { [s: string]: { component: InputComponent; inputValueTransformer?: InputValueTransformer } };
|
||||||
|
|
||||||
export function getInputBoxComponent(name: string, inputComponents: InputComponents): azdata.InputBoxComponent {
|
export function getInputBoxComponent(name: string, inputComponents: InputComponents): azdata.InputBoxComponent {
|
||||||
return <azdata.InputBoxComponent>inputComponents[name].component;
|
return <azdata.InputBoxComponent>inputComponents[name].component;
|
||||||
@@ -32,6 +35,10 @@ export function getCheckboxComponent(name: string, inputComponents: InputCompone
|
|||||||
return <azdata.CheckBoxComponent>inputComponents[name].component;
|
return <azdata.CheckBoxComponent>inputComponents[name].component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTextComponent(name: string, inputComponents: InputComponents): azdata.TextComponent {
|
||||||
|
return <azdata.TextComponent>inputComponents[name].component;
|
||||||
|
}
|
||||||
|
|
||||||
export const DefaultInputComponentWidth = '400px';
|
export const DefaultInputComponentWidth = '400px';
|
||||||
export const DefaultLabelComponentWidth = '200px';
|
export const DefaultLabelComponentWidth = '200px';
|
||||||
|
|
||||||
@@ -41,7 +48,8 @@ export interface DialogContext extends CreateContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface WizardPageContext extends CreateContext {
|
export interface WizardPageContext extends CreateContext {
|
||||||
sections: SectionInfo[];
|
wizardInfo: WizardInfoBase;
|
||||||
|
pageInfo: PageInfoBase;
|
||||||
page: azdata.window.WizardPage;
|
page: azdata.window.WizardPage;
|
||||||
container: azdata.window.Wizard;
|
container: azdata.window.Wizard;
|
||||||
}
|
}
|
||||||
@@ -57,6 +65,24 @@ interface FieldContext extends CreateContext {
|
|||||||
view: azdata.ModelView;
|
view: azdata.ModelView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FilePickerInputs {
|
||||||
|
input: azdata.InputBoxComponent;
|
||||||
|
browseButton: azdata.ButtonComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RadioOptionsInputs {
|
||||||
|
optionsList: azdata.DivContainer;
|
||||||
|
loader: azdata.LoadingComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface KubeClusterContextFieldContext extends FieldContext {
|
||||||
|
fieldInfo: KubeClusterContextFieldInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AzureLocationsFieldContext extends FieldContext {
|
||||||
|
fieldInfo: AzureLocationsFieldInfo;
|
||||||
|
}
|
||||||
|
|
||||||
interface AzureAccountFieldContext extends FieldContext {
|
interface AzureAccountFieldContext extends FieldContext {
|
||||||
fieldInfo: AzureAccountFieldInfo;
|
fieldInfo: AzureAccountFieldInfo;
|
||||||
}
|
}
|
||||||
@@ -65,7 +91,7 @@ interface CreateContext {
|
|||||||
container: azdata.window.Dialog | azdata.window.Wizard;
|
container: azdata.window.Dialog | azdata.window.Wizard;
|
||||||
onNewValidatorCreated: (validator: Validator) => void;
|
onNewValidatorCreated: (validator: Validator) => void;
|
||||||
onNewDisposableCreated: (disposable: vscode.Disposable) => void;
|
onNewDisposableCreated: (disposable: vscode.Disposable) => void;
|
||||||
onNewInputComponentCreated: (name: string, component: azdata.InputBoxComponent | azdata.DropDownComponent | azdata.CheckBoxComponent, inputValueTransformer?: InputValueTransformer) => void;
|
onNewInputComponentCreated: (name: string, component: InputComponent, inputValueTransformer?: InputValueTransformer) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createTextInput(view: azdata.ModelView, inputInfo: { defaultValue?: string, ariaLabel: string, required?: boolean, placeHolder?: string, width?: string, enabled?: boolean }): azdata.InputBoxComponent {
|
export function createTextInput(view: azdata.ModelView, inputInfo: { defaultValue?: string, ariaLabel: string, required?: boolean, placeHolder?: string, width?: string, enabled?: boolean }): azdata.InputBoxComponent {
|
||||||
@@ -133,6 +159,7 @@ export function initializeDialog(dialogContext: DialogContext): void {
|
|||||||
const sections = tabInfo.sections.map(sectionInfo => {
|
const sections = tabInfo.sections.map(sectionInfo => {
|
||||||
sectionInfo.inputWidth = sectionInfo.inputWidth || tabInfo.inputWidth || DefaultInputComponentWidth;
|
sectionInfo.inputWidth = sectionInfo.inputWidth || tabInfo.inputWidth || DefaultInputComponentWidth;
|
||||||
sectionInfo.labelWidth = sectionInfo.labelWidth || tabInfo.labelWidth || DefaultLabelComponentWidth;
|
sectionInfo.labelWidth = sectionInfo.labelWidth || tabInfo.labelWidth || DefaultLabelComponentWidth;
|
||||||
|
sectionInfo.labelPosition = sectionInfo.labelPosition || tabInfo.labelPosition;
|
||||||
return createSection({
|
return createSection({
|
||||||
sectionInfo: sectionInfo,
|
sectionInfo: sectionInfo,
|
||||||
view: view,
|
view: view,
|
||||||
@@ -161,9 +188,10 @@ export function initializeDialog(dialogContext: DialogContext): void {
|
|||||||
|
|
||||||
export function initializeWizardPage(context: WizardPageContext): void {
|
export function initializeWizardPage(context: WizardPageContext): void {
|
||||||
context.page.registerContent((view: azdata.ModelView) => {
|
context.page.registerContent((view: azdata.ModelView) => {
|
||||||
const sections = context.sections.map(sectionInfo => {
|
const sections = context.pageInfo.sections.map(sectionInfo => {
|
||||||
sectionInfo.inputWidth = sectionInfo.inputWidth || DefaultInputComponentWidth;
|
sectionInfo.inputWidth = sectionInfo.inputWidth || context.pageInfo.inputWidth || context.wizardInfo.inputWidth || DefaultInputComponentWidth;
|
||||||
sectionInfo.labelWidth = sectionInfo.labelWidth || DefaultLabelComponentWidth;
|
sectionInfo.labelWidth = sectionInfo.labelWidth || context.pageInfo.labelWidth || context.wizardInfo.labelWidth || DefaultLabelComponentWidth;
|
||||||
|
sectionInfo.labelPosition = sectionInfo.labelPosition || context.pageInfo.labelPosition || context.wizardInfo.labelPosition;
|
||||||
return createSection({
|
return createSection({
|
||||||
view: view,
|
view: view,
|
||||||
container: context.container,
|
container: context.container,
|
||||||
@@ -180,7 +208,7 @@ export function initializeWizardPage(context: WizardPageContext): void {
|
|||||||
componentWidth: '100%'
|
componentWidth: '100%'
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const form = formBuilder.withLayout({ width: '100%' }).component();
|
const form: azdata.FormContainer = formBuilder.withLayout({ width: '100%' }).component();
|
||||||
return view.initializeModel(form);
|
return view.initializeModel(form);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -239,12 +267,16 @@ export function createGroupContainer(view: azdata.ModelView, items: azdata.Compo
|
|||||||
return view.modelBuilder.groupContainer().withItems(items).withLayout(layout).component();
|
return view.modelBuilder.groupContainer().withItems(items).withLayout(layout).component();
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLabelInputPairToContainer(view: azdata.ModelView, components: azdata.Component[], label: azdata.Component, input: azdata.Component, labelPosition?: LabelPosition) {
|
function addLabelInputPairToContainer(view: azdata.ModelView, components: azdata.Component[], label: azdata.Component, input: azdata.Component, labelPosition?: LabelPosition, additionalComponents?: azdata.Component[]) {
|
||||||
|
const inputs = [label, input];
|
||||||
|
if (additionalComponents && additionalComponents.length > 0) {
|
||||||
|
inputs.push(...additionalComponents);
|
||||||
|
}
|
||||||
if (labelPosition && labelPosition === LabelPosition.Left) {
|
if (labelPosition && labelPosition === LabelPosition.Left) {
|
||||||
const row = createFlexContainer(view, [label, input]);
|
const row = createFlexContainer(view, inputs);
|
||||||
components.push(row);
|
components.push(row);
|
||||||
} else {
|
} else {
|
||||||
components.push(label, input);
|
components.push(...inputs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +285,9 @@ function processField(context: FieldContext): void {
|
|||||||
case FieldType.Options:
|
case FieldType.Options:
|
||||||
processOptionsTypeField(context);
|
processOptionsTypeField(context);
|
||||||
break;
|
break;
|
||||||
|
case FieldType.RadioOptions:
|
||||||
|
processRadioOptionsTypeField(context);
|
||||||
|
break;
|
||||||
case FieldType.DateTimeText:
|
case FieldType.DateTimeText:
|
||||||
processDateTimeTextField(context);
|
processDateTimeTextField(context);
|
||||||
break;
|
break;
|
||||||
@@ -275,6 +310,15 @@ function processField(context: FieldContext): void {
|
|||||||
case FieldType.AzureAccount:
|
case FieldType.AzureAccount:
|
||||||
processAzureAccountField(context);
|
processAzureAccountField(context);
|
||||||
break;
|
break;
|
||||||
|
case FieldType.AzureLocations:
|
||||||
|
processAzureLocationsField(context);
|
||||||
|
break;
|
||||||
|
case FieldType.FilePicker:
|
||||||
|
processFilePickerField(context);
|
||||||
|
break;
|
||||||
|
case FieldType.KubeClusterContextPicker:
|
||||||
|
processKubeConfigClusterPickerField(context);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", context.fieldInfo.type));
|
throw new Error(localize('UnknownFieldTypeError', "Unknown field type: \"{0}\"", context.fieldInfo.type));
|
||||||
}
|
}
|
||||||
@@ -416,8 +460,9 @@ function processPasswordField(context: FieldContext): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function processReadonlyTextField(context: FieldContext): void {
|
function processReadonlyTextField(context: FieldContext): void {
|
||||||
|
let defaultValue = context.fieldInfo.defaultValue || '';
|
||||||
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: false, width: context.fieldInfo.labelWidth, fontWeight: context.fieldInfo.labelFontWeight });
|
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: false, width: context.fieldInfo.labelWidth, fontWeight: context.fieldInfo.labelFontWeight });
|
||||||
const text = createLabel(context.view, { text: context.fieldInfo.defaultValue!, description: '', required: false, width: context.fieldInfo.inputWidth, fontStyle: context.fieldInfo.fontStyle, links: context.fieldInfo.links });
|
const text = createLabel(context.view, { text: defaultValue, description: '', required: false, width: context.fieldInfo.inputWidth, fontWeight: context.fieldInfo.textFontWeight, fontStyle: context.fieldInfo.fontStyle, links: context.fieldInfo.links });
|
||||||
addLabelInputPairToContainer(context.view, context.components, label, text, context.fieldInfo.labelPosition);
|
addLabelInputPairToContainer(context.view, context.components, label, text, context.fieldInfo.labelPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -427,23 +472,168 @@ function processCheckboxField(context: FieldContext): void {
|
|||||||
context.onNewInputComponentCreated(context.fieldInfo.variableName!, checkbox);
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, checkbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A File Picker field consists of a text field and a browse button that allows a user to pick a file system file.
|
||||||
|
* @param context The context to use to create the field
|
||||||
|
*/
|
||||||
|
function processFilePickerField(context: FieldContext, defaultFilePath?: string): FilePickerInputs {
|
||||||
|
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: context.fieldInfo.required, width: context.fieldInfo.labelWidth, fontWeight: context.fieldInfo.labelFontWeight });
|
||||||
|
const input = createTextInput(context.view, {
|
||||||
|
defaultValue: defaultFilePath || context.fieldInfo.defaultValue || '',
|
||||||
|
ariaLabel: context.fieldInfo.label,
|
||||||
|
required: context.fieldInfo.required,
|
||||||
|
placeHolder: context.fieldInfo.placeHolder,
|
||||||
|
width: context.fieldInfo.inputWidth,
|
||||||
|
enabled: context.fieldInfo.enabled
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, input);
|
||||||
|
input.enabled = false;
|
||||||
|
const browseFileButton = context.view!.modelBuilder.button().withProperties({ label: loc.browse }).component();
|
||||||
|
context.onNewDisposableCreated(browseFileButton.onDidClick(async () => {
|
||||||
|
let fileUris = await vscode.window.showOpenDialog({
|
||||||
|
canSelectFiles: true,
|
||||||
|
canSelectFolders: false,
|
||||||
|
canSelectMany: false,
|
||||||
|
defaultUri: vscode.Uri.file(path.dirname(input.value || os_homedir())),
|
||||||
|
openLabel: loc.select,
|
||||||
|
filters: {
|
||||||
|
'Config Files': ['*'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!fileUris || fileUris.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fileUri = fileUris[0];
|
||||||
|
input.value = fileUri.fsPath;
|
||||||
|
}));
|
||||||
|
addLabelInputPairToContainer(context.view, context.components, label, input, LabelPosition.Left, [browseFileButton]);
|
||||||
|
return { input: input, browseButton: browseFileButton };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Kube Config Cluster picker field consists of a file system filee picker and radio button selector for cluster contexts defined in the config filed picked using the file picker.
|
||||||
|
* @param context The context to use to create the field
|
||||||
|
*/
|
||||||
|
async function processKubeConfigClusterPickerField(context: KubeClusterContextFieldContext): Promise<void> {
|
||||||
|
const kubeConfigFilePathVariableName = context.fieldInfo.configFileVariableName || 'AZDATA_NB_VAR_KUBECONFIG_PATH';
|
||||||
|
const filePickerContext: FieldContext = {
|
||||||
|
container: context.container,
|
||||||
|
components: context.components,
|
||||||
|
view: context.view,
|
||||||
|
onNewValidatorCreated: context.onNewValidatorCreated,
|
||||||
|
onNewDisposableCreated: context.onNewDisposableCreated,
|
||||||
|
onNewInputComponentCreated: context.onNewInputComponentCreated,
|
||||||
|
fieldInfo: {
|
||||||
|
label: loc.kubeConfigFilePath,
|
||||||
|
type: FieldType.FilePicker,
|
||||||
|
labelWidth: context.fieldInfo.labelWidth,
|
||||||
|
variableName: kubeConfigFilePathVariableName,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const filePicker = processFilePickerField(filePickerContext, getDefaultKubeConfigPath());
|
||||||
|
context.fieldInfo.subFields = context.fieldInfo.subFields || [];
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: filePickerContext.fieldInfo.label,
|
||||||
|
variableName: kubeConfigFilePathVariableName
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(kubeConfigFilePathVariableName, filePicker.input);
|
||||||
|
const getClusterContexts = async () => {
|
||||||
|
try {
|
||||||
|
let currentClusterContext = '';
|
||||||
|
const clusterContexts: string[] = (await getKubeConfigClusterContexts(filePicker.input.value!)).map(kubeClusterContext => {
|
||||||
|
if (kubeClusterContext.isCurrentContext) {
|
||||||
|
currentClusterContext = kubeClusterContext.name;
|
||||||
|
}
|
||||||
|
return kubeClusterContext.name;
|
||||||
|
});
|
||||||
|
if (clusterContexts.length === 0) {
|
||||||
|
throw Error(loc.clusterContextNotFound);
|
||||||
|
}
|
||||||
|
return { values: clusterContexts, defaultValue: currentClusterContext };
|
||||||
|
} catch (e) {
|
||||||
|
throw Error(localize('kubeConfigClusterPicker.errorLoadingClusters', "An error ocurred while loading or parsing the config file:{0}, error is:{1}", filePicker.input.value, getErrorMessage(e)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createRadioOptions(context, getClusterContexts)
|
||||||
|
.then(clusterContextOptions => {
|
||||||
|
filePicker.input.onTextChanged(async () => {
|
||||||
|
await loadOrReloadRadioOptions(context, clusterContextOptions.optionsList, clusterContextOptions.loader, getClusterContexts);
|
||||||
|
});
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(`failed to create radio options, Error: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function processRadioOptionsTypeField(context: FieldContext): Promise<RadioOptionsInputs> {
|
||||||
|
return await createRadioOptions(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createRadioOptions(context: FieldContext, getRadioButtonInfo?: (() => Promise<{ values: string[] | azdata.CategoryValue[], defaultValue: string }>))
|
||||||
|
: Promise<RadioOptionsInputs> {
|
||||||
|
const label = createLabel(context.view, { text: context.fieldInfo.label, description: context.fieldInfo.description, required: context.fieldInfo.required, width: context.fieldInfo.labelWidth, fontWeight: context.fieldInfo.labelFontWeight });
|
||||||
|
const optionsList = context.view!.modelBuilder.divContainer().withProperties<azdata.DivContainerProperties>({ clickable: false }).component();
|
||||||
|
const radioOptionsLoadingComponent = context.view!.modelBuilder.loadingComponent().withItem(optionsList).component();
|
||||||
|
addLabelInputPairToContainer(context.view, context.components, label, radioOptionsLoadingComponent, LabelPosition.Left);
|
||||||
|
await loadOrReloadRadioOptions(context, optionsList, radioOptionsLoadingComponent, getRadioButtonInfo);
|
||||||
|
return { optionsList: optionsList, loader: radioOptionsLoadingComponent };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadOrReloadRadioOptions(context: FieldContext, optionsList: azdata.DivContainer, radioOptionsLoadingComponent: azdata.LoadingComponent, getRadioButtonInfo: (() => Promise<{ values: string[] | azdata.CategoryValue[]; defaultValue: string; }>) | undefined): Promise<void> {
|
||||||
|
radioOptionsLoadingComponent.loading = true;
|
||||||
|
optionsList.clearItems();
|
||||||
|
let options: (string[] | azdata.CategoryValue[]) = context.fieldInfo.options!;
|
||||||
|
let defaultValue: string = context.fieldInfo.defaultValue!;
|
||||||
|
try {
|
||||||
|
if (getRadioButtonInfo) {
|
||||||
|
const radioButtonsInfo = await getRadioButtonInfo();
|
||||||
|
options = radioButtonsInfo.values;
|
||||||
|
defaultValue = radioButtonsInfo.defaultValue;
|
||||||
|
}
|
||||||
|
options.forEach((op: string | azdata.CategoryValue) => {
|
||||||
|
const option: azdata.CategoryValue = (typeof op === 'string') ? { name: op, displayName: op } : op as azdata.CategoryValue;
|
||||||
|
const radioOption = context.view!.modelBuilder.radioButton().withProperties<azdata.RadioButtonProperties>({
|
||||||
|
label: option.displayName,
|
||||||
|
checked: option.displayName === defaultValue,
|
||||||
|
name: option.name,
|
||||||
|
}).component();
|
||||||
|
if (radioOption.checked) {
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, radioOption);
|
||||||
|
}
|
||||||
|
context.onNewDisposableCreated(radioOption.onDidClick(() => {
|
||||||
|
// reset checked status of all remaining radioButtons
|
||||||
|
optionsList.items.filter(otherOption => otherOption !== radioOption).forEach(otherOption => (otherOption as azdata.RadioButtonComponent).checked = false);
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, radioOption!);
|
||||||
|
}));
|
||||||
|
optionsList.addItem(radioOption);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
const errorLoadingRadioOptionsLabel = context.view!.modelBuilder.text().withProperties({ value: getErrorMessage(e) }).component();
|
||||||
|
optionsList.addItem(errorLoadingRadioOptionsLabel);
|
||||||
|
}
|
||||||
|
radioOptionsLoadingComponent.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Azure Account field consists of 3 separate dropdown fields - Account, Subscription and Resource Group
|
* An Azure Account field consists of 3 separate dropdown fields - Account, Subscription and Resource Group
|
||||||
* @param context The context to use to create the field
|
* @param context The context to use to create the field
|
||||||
*/
|
*/
|
||||||
function processAzureAccountField(context: AzureAccountFieldContext): void {
|
function processAzureAccountField(context: AzureAccountFieldContext): void {
|
||||||
|
context.fieldInfo.subFields = [];
|
||||||
const accountValueToAccountMap = new Map<string, azdata.Account>();
|
const accountValueToAccountMap = new Map<string, azdata.Account>();
|
||||||
const subscriptionValueToSubscriptionMap = new Map<string, azureResource.AzureResourceSubscription>();
|
const subscriptionValueToSubscriptionMap = new Map<string, azureResource.AzureResourceSubscription>();
|
||||||
const accountDropdown = createAzureAccountDropdown(context);
|
const accountDropdown = createAzureAccountDropdown(context);
|
||||||
const subscriptionDropdown = createAzureSubscriptionDropdown(context, subscriptionValueToSubscriptionMap);
|
const subscriptionDropdown = createAzureSubscriptionDropdown(context, subscriptionValueToSubscriptionMap);
|
||||||
const resourceGroupDropdown = createAzureResourceGroupsDropdown(context, accountDropdown, accountValueToAccountMap, subscriptionDropdown, subscriptionValueToSubscriptionMap);
|
const resourceGroupDropdown = createAzureResourceGroupsDropdown(context, accountDropdown, accountValueToAccountMap, subscriptionDropdown, subscriptionValueToSubscriptionMap);
|
||||||
const locationDropdown = createAzureLocationDropdown(context);
|
const locationDropdown = context.fieldInfo.locations && processAzureLocationsField(context);
|
||||||
accountDropdown.onValueChanged(selectedItem => {
|
accountDropdown.onValueChanged(selectedItem => {
|
||||||
const selectedAccount = accountValueToAccountMap.get(selectedItem.selected)!;
|
const selectedAccount = accountValueToAccountMap.get(selectedItem.selected)!;
|
||||||
handleSelectedAccountChanged(context, selectedAccount, subscriptionDropdown, subscriptionValueToSubscriptionMap, resourceGroupDropdown, locationDropdown);
|
handleSelectedAccountChanged(context, selectedAccount, subscriptionDropdown, subscriptionValueToSubscriptionMap, resourceGroupDropdown, locationDropdown);
|
||||||
});
|
});
|
||||||
azdata.accounts.getAllAccounts().then((accounts: azdata.Account[]) => {
|
azdata.accounts.getAllAccounts().then((accounts: azdata.Account[]) => {
|
||||||
// Append a blank value for the "default" option if the field isn't required, this will clear all the dropdowns when selected
|
// Append a blank value for the "default" option if the field isn't required, context will clear all the dropdowns when selected
|
||||||
const dropdownValues = context.fieldInfo.required ? [] : [''];
|
const dropdownValues = context.fieldInfo.required ? [] : [''];
|
||||||
accountDropdown.values = dropdownValues.concat(accounts.map(account => {
|
accountDropdown.values = dropdownValues.concat(accounts.map(account => {
|
||||||
const displayName = `${account.displayInfo.displayName} (${account.displayInfo.userId})`;
|
const displayName = `${account.displayInfo.displayName} (${account.displayInfo.userId})`;
|
||||||
@@ -489,6 +679,10 @@ function createAzureSubscriptionDropdown(
|
|||||||
required: context.fieldInfo.required,
|
required: context.fieldInfo.required,
|
||||||
label: loc.subscription
|
label: loc.subscription
|
||||||
});
|
});
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: label.value!,
|
||||||
|
variableName: context.fieldInfo.subscriptionVariableName
|
||||||
|
});
|
||||||
context.onNewInputComponentCreated(context.fieldInfo.subscriptionVariableName!, subscriptionDropdown, (inputValue: string) => {
|
context.onNewInputComponentCreated(context.fieldInfo.subscriptionVariableName!, subscriptionDropdown, (inputValue: string) => {
|
||||||
return subscriptionValueToSubscriptionMap.get(inputValue)?.id || inputValue;
|
return subscriptionValueToSubscriptionMap.get(inputValue)?.id || inputValue;
|
||||||
});
|
});
|
||||||
@@ -502,18 +696,20 @@ function handleSelectedAccountChanged(
|
|||||||
subscriptionDropdown: azdata.DropDownComponent,
|
subscriptionDropdown: azdata.DropDownComponent,
|
||||||
subscriptionValueToSubscriptionMap: Map<string, azureResource.AzureResourceSubscription>,
|
subscriptionValueToSubscriptionMap: Map<string, azureResource.AzureResourceSubscription>,
|
||||||
resourceGroupDropdown: azdata.DropDownComponent,
|
resourceGroupDropdown: azdata.DropDownComponent,
|
||||||
locationDropdown: azdata.DropDownComponent
|
locationDropdown?: azdata.DropDownComponent
|
||||||
): void {
|
): void {
|
||||||
subscriptionValueToSubscriptionMap.clear();
|
subscriptionValueToSubscriptionMap.clear();
|
||||||
subscriptionDropdown.values = [];
|
subscriptionDropdown.values = [];
|
||||||
handleSelectedSubscriptionChanged(context, selectedAccount, undefined, resourceGroupDropdown);
|
handleSelectedSubscriptionChanged(context, selectedAccount, undefined, resourceGroupDropdown);
|
||||||
if (selectedAccount) {
|
if (locationDropdown) {
|
||||||
if (locationDropdown.values && locationDropdown.values.length === 0) {
|
if (selectedAccount) {
|
||||||
locationDropdown.values = context.fieldInfo.locations;
|
if (locationDropdown.values && locationDropdown.values.length === 0) {
|
||||||
|
locationDropdown.values = context.fieldInfo.locations;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
locationDropdown.values = [];
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
locationDropdown.values = [];
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
vscode.commands.executeCommand<azurecore.GetSubscriptionsResult>('azure.accounts.getSubscriptions', selectedAccount, true /*ignoreErrors*/).then(response => {
|
vscode.commands.executeCommand<azurecore.GetSubscriptionsResult>('azure.accounts.getSubscriptions', selectedAccount, true /*ignoreErrors*/).then(response => {
|
||||||
if (!response) {
|
if (!response) {
|
||||||
@@ -554,6 +750,10 @@ function createAzureResourceGroupsDropdown(
|
|||||||
required: context.fieldInfo.required,
|
required: context.fieldInfo.required,
|
||||||
label: loc.resourceGroup
|
label: loc.resourceGroup
|
||||||
});
|
});
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: label.value!,
|
||||||
|
variableName: context.fieldInfo.resourceGroupVariableName
|
||||||
|
});
|
||||||
context.onNewInputComponentCreated(context.fieldInfo.resourceGroupVariableName!, resourceGroupDropdown);
|
context.onNewInputComponentCreated(context.fieldInfo.resourceGroupVariableName!, resourceGroupDropdown);
|
||||||
addLabelInputPairToContainer(context.view, context.components, label, resourceGroupDropdown, context.fieldInfo.labelPosition);
|
addLabelInputPairToContainer(context.view, context.components, label, resourceGroupDropdown, context.fieldInfo.labelPosition);
|
||||||
subscriptionDropdown.onValueChanged(selectedItem => {
|
subscriptionDropdown.onValueChanged(selectedItem => {
|
||||||
@@ -594,9 +794,13 @@ const knownAzureLocationNameMappings = new Map<string, string>([
|
|||||||
['Central US', 'centralus']
|
['Central US', 'centralus']
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function createAzureLocationDropdown(context: AzureAccountFieldContext): azdata.DropDownComponent {
|
/**
|
||||||
|
* An Azure Locations field consists of a dropdown field for azure locations
|
||||||
|
* @param context The context to use to create the field
|
||||||
|
*/
|
||||||
|
function processAzureLocationsField(context: AzureLocationsFieldContext): azdata.DropDownComponent {
|
||||||
const label = createLabel(context.view, {
|
const label = createLabel(context.view, {
|
||||||
text: loc.location,
|
text: context.fieldInfo.label || loc.location,
|
||||||
required: context.fieldInfo.required,
|
required: context.fieldInfo.required,
|
||||||
width: context.fieldInfo.labelWidth,
|
width: context.fieldInfo.labelWidth,
|
||||||
fontWeight: context.fieldInfo.labelFontWeight
|
fontWeight: context.fieldInfo.labelFontWeight
|
||||||
@@ -608,7 +812,24 @@ function createAzureLocationDropdown(context: AzureAccountFieldContext): azdata.
|
|||||||
label: loc.location,
|
label: loc.location,
|
||||||
values: context.fieldInfo.locations
|
values: context.fieldInfo.locations
|
||||||
});
|
});
|
||||||
context.onNewInputComponentCreated(context.fieldInfo.locationVariableName!, locationDropdown, (inputValue: string) => {
|
context.fieldInfo.subFields = context.fieldInfo.subFields || [];
|
||||||
|
if (context.fieldInfo.locationVariableName) {
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: label.value!,
|
||||||
|
variableName: context.fieldInfo.locationVariableName
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.locationVariableName, locationDropdown, (inputValue: string) => {
|
||||||
|
return knownAzureLocationNameMappings.get(inputValue) || inputValue;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (context.fieldInfo.displayLocationVariableName) {
|
||||||
|
context.fieldInfo.subFields!.push({
|
||||||
|
label: label.value!,
|
||||||
|
variableName: context.fieldInfo.displayLocationVariableName
|
||||||
|
});
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.displayLocationVariableName, locationDropdown);
|
||||||
|
}
|
||||||
|
context.onNewInputComponentCreated(context.fieldInfo.variableName!, locationDropdown, (inputValue: string) => {
|
||||||
return knownAzureLocationNameMappings.get(inputValue) || inputValue;
|
return knownAzureLocationNameMappings.get(inputValue) || inputValue;
|
||||||
});
|
});
|
||||||
addLabelInputPairToContainer(context.view, context.components, label, locationDropdown, context.fieldInfo.labelPosition);
|
addLabelInputPairToContainer(context.view, context.components, label, locationDropdown, context.fieldInfo.labelPosition);
|
||||||
@@ -618,12 +839,12 @@ function createAzureLocationDropdown(context: AzureAccountFieldContext): azdata.
|
|||||||
export function isValidSQLPassword(password: string, userName: string = 'sa'): boolean {
|
export function isValidSQLPassword(password: string, userName: string = 'sa'): boolean {
|
||||||
// Validate SQL Server password
|
// Validate SQL Server password
|
||||||
const containsUserName = password && userName !== undefined && password.toUpperCase().includes(userName.toUpperCase());
|
const containsUserName = password && userName !== undefined && password.toUpperCase().includes(userName.toUpperCase());
|
||||||
// Instead of using one RegEx, I am seperating it to make it more readable.
|
// Instead of using one RegEx, I am separating it to make it more readable.
|
||||||
const hasUpperCase = /[A-Z]/.test(password) ? 1 : 0;
|
const hasUpperCase = /[A-Z]/.test(password) ? 1 : 0;
|
||||||
const hasLowerCase = /[a-z]/.test(password) ? 1 : 0;
|
const hasLowerCase = /[a-z]/.test(password) ? 1 : 0;
|
||||||
const hasNumbers = /\d/.test(password) ? 1 : 0;
|
const hasNumbers = /\d/.test(password) ? 1 : 0;
|
||||||
const hasNonalphas = /\W/.test(password) ? 1 : 0;
|
const hasNonAlphas = /\W/.test(password) ? 1 : 0;
|
||||||
return !containsUserName && password.length >= 8 && password.length <= 128 && (hasUpperCase + hasLowerCase + hasNumbers + hasNonalphas >= 3);
|
return !containsUserName && password.length >= 8 && password.length <= 128 && (hasUpperCase + hasLowerCase + hasNumbers + hasNonAlphas >= 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function removeValidationMessage(container: azdata.window.Dialog | azdata.window.Wizard, message: string): void {
|
export function removeValidationMessage(container: azdata.window.Dialog | azdata.window.Wizard, message: string): void {
|
||||||
@@ -646,7 +867,9 @@ export function setModelValues(inputComponents: InputComponents, model: Model):
|
|||||||
Object.keys(inputComponents).forEach(key => {
|
Object.keys(inputComponents).forEach(key => {
|
||||||
let value;
|
let value;
|
||||||
const input = inputComponents[key].component;
|
const input = inputComponents[key].component;
|
||||||
if ('checked' in input) { // CheckBoxComponent
|
if ('name' in input && 'checked' in input) { //RadioButtonComponent
|
||||||
|
value = input.name;
|
||||||
|
} else if ('checked' in input) { // CheckBoxComponent
|
||||||
value = input.checked ? 'true' : 'false';
|
value = input.checked ? 'true' : 'false';
|
||||||
} else if ('value' in input) { // InputBoxComponent or DropDownComponent
|
} else if ('value' in input) { // InputBoxComponent or DropDownComponent
|
||||||
const inputValue = input.value;
|
const inputValue = input.value;
|
||||||
@@ -671,4 +894,3 @@ export function isInputBoxEmpty(input: azdata.InputBoxComponent): boolean {
|
|||||||
return input.value === undefined || input.value === '';
|
return input.value === undefined || input.value === '';
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MissingRequiredInformationErrorMessage = localize('deployCluster.MissingRequiredInfoError', "Please fill out the required fields marked with red asterisks.");
|
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* 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 { INotebookService } from '../../services/notebookService';
|
||||||
|
import { Model } from '../model';
|
||||||
|
import { WizardBase } from '../wizardBase';
|
||||||
|
import { WizardPageBase } from '../wizardPageBase';
|
||||||
|
import { DeploymentType, NotebookWizardInfo } from './../../interfaces';
|
||||||
|
import { IPlatformService } from './../../services/platformService';
|
||||||
|
import { NotebookWizardPage } from './notebookWizardPage';
|
||||||
|
import { NotebookWizardSummaryPage } from './notebookWizardSummaryPage';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class NotebookWizard extends WizardBase<NotebookWizard, Model> {
|
||||||
|
|
||||||
|
public get notebookService(): INotebookService {
|
||||||
|
return this._notebookService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get platformService(): IPlatformService {
|
||||||
|
return this._platformService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get wizardInfo(): NotebookWizardInfo {
|
||||||
|
return this._wizardInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(private _wizardInfo: NotebookWizardInfo, private _notebookService: INotebookService, private _platformService: IPlatformService) {
|
||||||
|
super(_wizardInfo.title, new Model());
|
||||||
|
this.wizardObject.doneButton.label = _wizardInfo.actionText || this.wizardObject.doneButton.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get deploymentType(): DeploymentType | undefined {
|
||||||
|
return this._wizardInfo.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPages(): WizardPageBase<NotebookWizard>[] {
|
||||||
|
const pages: WizardPageBase<NotebookWizard>[] = [];
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { 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 { 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
const self = this;
|
||||||
|
initializeWizardPage({
|
||||||
|
container: this.wizard.wizardObject,
|
||||||
|
wizardInfo: this.wizard.wizardInfo,
|
||||||
|
pageInfo: this.pageInfo,
|
||||||
|
page: this.pageObject,
|
||||||
|
onNewDisposableCreated: (disposable: vscode.Disposable): void => {
|
||||||
|
self.wizard.registerDisposable(disposable);
|
||||||
|
},
|
||||||
|
onNewInputComponentCreated: (name: string, component: InputComponent): void => {
|
||||||
|
self.inputComponents[name] = { component: component };
|
||||||
|
},
|
||||||
|
onNewValidatorCreated: (validator: Validator): void => {
|
||||||
|
self.validators.push(validator);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public onLeave() {
|
||||||
|
setModelValues(this.inputComponents, this.wizard.model);
|
||||||
|
// The following callback registration clears previous navigation validators.
|
||||||
|
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onEnter() {
|
||||||
|
this.wizard.wizardObject.registerNavigationValidator((pcInfo) => {
|
||||||
|
this.wizard.wizardObject.message = { text: '' };
|
||||||
|
if (pcInfo.newPage > pcInfo.lastPage) {
|
||||||
|
const messages: string[] = [];
|
||||||
|
|
||||||
|
this.validators.forEach(validator => {
|
||||||
|
const result = validator();
|
||||||
|
if (!result.valid) {
|
||||||
|
messages.push(result.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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."),
|
||||||
|
description: messages.length === 1 ? undefined : messages.join(EOL),
|
||||||
|
level: azdata.window.MessageLevel.Error
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return messages.length === 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 nls from 'vscode-nls';
|
||||||
|
|
||||||
|
import { SubFieldInfo, FieldType, FontWeight, LabelPosition, SectionInfo } from '../../interfaces';
|
||||||
|
import { createSection, DefaultInputComponentWidth, DefaultLabelComponentWidth } from '../modelViewUtils';
|
||||||
|
import { WizardPageBase } from '../wizardPageBase';
|
||||||
|
import { NotebookWizard } from './notebookWizard';
|
||||||
|
|
||||||
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
|
export class NotebookWizardSummaryPage extends WizardPageBase<NotebookWizard> {
|
||||||
|
private formItems: azdata.FormComponent[] = [];
|
||||||
|
private form!: azdata.FormBuilder;
|
||||||
|
private view!: azdata.ModelView;
|
||||||
|
|
||||||
|
constructor(wizard: NotebookWizard) {
|
||||||
|
super(localize('notebookWizard.summaryPageTitle', "Review your configuration"), '', wizard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public initialize(): void {
|
||||||
|
this.pageObject.registerContent((view: azdata.ModelView) => {
|
||||||
|
this.view = view;
|
||||||
|
this.form = view.modelBuilder.formContainer();
|
||||||
|
return view.initializeModel(this.form!.withLayout({ width: '100%' }).component());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public onLeave() {
|
||||||
|
this.wizard.wizardObject.message = { text: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
public onEnter() {
|
||||||
|
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;
|
||||||
|
|
||||||
|
this.wizard.wizardInfo.pages.forEach(pageInfo => {
|
||||||
|
const summarySectionInfo: SectionInfo = {
|
||||||
|
labelPosition: labelPosition,
|
||||||
|
labelWidth: labelWidth,
|
||||||
|
inputWidth: inputWidth,
|
||||||
|
title: '',
|
||||||
|
rows: []
|
||||||
|
};
|
||||||
|
pageInfo.sections.forEach(sectionInfo => {
|
||||||
|
sectionInfo.fields!.forEach(fieldInfo => {
|
||||||
|
if (fieldInfo.variableName) {
|
||||||
|
this.addSummaryForVariable(summarySectionInfo, fieldInfo);
|
||||||
|
}
|
||||||
|
if (fieldInfo.subFields) {
|
||||||
|
fieldInfo.subFields.forEach(subFieldInfo => {
|
||||||
|
this.addSummaryForVariable(summarySectionInfo, subFieldInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (summarySectionInfo!.rows!.length > 0) {
|
||||||
|
const formComponent: azdata.FormComponent = {
|
||||||
|
title: pageInfo.title,
|
||||||
|
component: createSection({
|
||||||
|
container: this.wizard.wizardObject,
|
||||||
|
sectionInfo: summarySectionInfo,
|
||||||
|
view: this.view,
|
||||||
|
onNewDisposableCreated: () => { },
|
||||||
|
onNewInputComponentCreated: () => { },
|
||||||
|
onNewValidatorCreated: () => { }
|
||||||
|
})
|
||||||
|
};
|
||||||
|
this.formItems.push(formComponent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.form.addFormItems(this.formItems);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private addSummaryForVariable(summarySectionInfo: SectionInfo, fieldInfo: SubFieldInfo) {
|
||||||
|
summarySectionInfo!.rows!.push({
|
||||||
|
fields: [{
|
||||||
|
type: FieldType.ReadonlyText,
|
||||||
|
label: fieldInfo.label,
|
||||||
|
defaultValue: this.wizard.model.getStringValue(fieldInfo.variableName!),
|
||||||
|
labelFontWeight: FontWeight.Bold
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -173,24 +173,26 @@ export class ResourceTypePickerDialog extends DialogBase {
|
|||||||
|
|
||||||
this._optionsContainer.clearItems();
|
this._optionsContainer.clearItems();
|
||||||
this._optionDropDownMap.clear();
|
this._optionDropDownMap.clear();
|
||||||
resourceType.options.forEach(option => {
|
if (resourceType.options) {
|
||||||
const optionLabel = this._view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
resourceType.options.forEach(option => {
|
||||||
value: option.displayName
|
const optionLabel = this._view.modelBuilder.text().withProperties<azdata.TextComponentProperties>({
|
||||||
}).component();
|
value: option.displayName
|
||||||
optionLabel.width = '150px';
|
}).component();
|
||||||
|
optionLabel.width = '150px';
|
||||||
|
|
||||||
const optionSelectBox = this._view.modelBuilder.dropDown().withProperties<azdata.DropDownProperties>({
|
const optionSelectBox = this._view.modelBuilder.dropDown().withProperties<azdata.DropDownProperties>({
|
||||||
values: option.values,
|
values: option.values,
|
||||||
value: option.values[0],
|
value: option.values[0],
|
||||||
width: '300px',
|
width: '300px',
|
||||||
ariaLabel: option.displayName
|
ariaLabel: option.displayName
|
||||||
}).component();
|
}).component();
|
||||||
|
|
||||||
this._toDispose.push(optionSelectBox.onValueChanged(() => { this.updateToolsDisplayTable(); }));
|
this._toDispose.push(optionSelectBox.onValueChanged(() => { this.updateToolsDisplayTable(); }));
|
||||||
this._optionDropDownMap.set(option.name, optionSelectBox);
|
this._optionDropDownMap.set(option.name, optionSelectBox);
|
||||||
const row = this._view.modelBuilder.flexContainer().withItems([optionLabel, optionSelectBox], { flex: '0 0 auto', CSSStyles: { 'margin-right': '20px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
const row = this._view.modelBuilder.flexContainer().withItems([optionLabel, optionSelectBox], { flex: '0 0 auto', CSSStyles: { 'margin-right': '20px' } }).withLayout({ flexFlow: 'row', alignItems: 'center' }).component();
|
||||||
this._optionsContainer.addItem(row);
|
this._optionsContainer.addItem(row);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
this.updateToolsDisplayTable();
|
this.updateToolsDisplayTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
import { ITool, NoteBookEnvironmentVariablePrefix } from './interfaces';
|
import { ITool, NoteBookEnvironmentVariablePrefix } from './interfaces';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { ToolsInstallPath } from './ui/deployClusterWizard/constants';
|
import { ToolsInstallPath } from './constants';
|
||||||
|
|
||||||
export function getErrorMessage(error: any): string {
|
export function getErrorMessage(error: any): string {
|
||||||
return (error instanceof Error)
|
return (error instanceof Error)
|
||||||
|
|||||||
Reference in New Issue
Block a user