Use wizard for create function (#1096)
This commit is contained in:
Родитель
20ccaf3aaa
Коммит
ec9b71adbd
|
@ -17,7 +17,7 @@ export { activateInternal, deactivateInternal } from './src/extension';
|
|||
//
|
||||
// The tests should import '../extension.bundle'. At design-time they live in tests/ and so will pick up this file (extension.bundle.ts).
|
||||
// At runtime the tests live in dist/tests and will therefore pick up the main webpack bundle at dist/extension.bundle.js.
|
||||
export * from './src/commands/createFunction/CSharpFunctionCreator';
|
||||
export * from './src/commands/createFunction/dotnetSteps/DotnetNamespaceStep';
|
||||
export * from './src/commands/createNewProject/createNewProject';
|
||||
export * from './src/commands/createNewProject/initProjectForVSCode';
|
||||
export * from './src/commands/createNewProject/JavaScriptProjectCreator';
|
||||
|
|
|
@ -944,7 +944,7 @@
|
|||
},
|
||||
"readable-stream": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
|
||||
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
|
||||
"requires": {
|
||||
"core-util-is": "~1.0.0",
|
||||
|
@ -3285,8 +3285,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
@ -3307,14 +3306,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
@ -3329,20 +3326,17 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -3459,8 +3453,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
@ -3472,7 +3465,6 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
@ -3487,7 +3479,6 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
@ -3495,14 +3486,12 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
|
@ -3521,7 +3510,6 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
@ -3602,8 +3590,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
@ -3615,7 +3602,6 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
@ -3701,8 +3687,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
@ -3738,7 +3723,6 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
@ -3758,7 +3742,6 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
@ -3802,14 +3785,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -5731,7 +5712,7 @@
|
|||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "http://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz",
|
||||
"integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=",
|
||||
"requires": {
|
||||
"hash-base": "^3.0.0",
|
||||
|
@ -6818,7 +6799,7 @@
|
|||
"dependencies": {
|
||||
"async": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "http://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
||||
}
|
||||
}
|
||||
|
@ -7316,7 +7297,7 @@
|
|||
},
|
||||
"sax": {
|
||||
"version": "0.5.8",
|
||||
"resolved": "http://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
|
||||
"resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz",
|
||||
"integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE="
|
||||
},
|
||||
"schema-utils": {
|
||||
|
@ -7439,7 +7420,7 @@
|
|||
},
|
||||
"simple-git": {
|
||||
"version": "1.92.0",
|
||||
"resolved": "http://registry.npmjs.org/simple-git/-/simple-git-1.92.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.92.0.tgz",
|
||||
"integrity": "sha1-YGFGjrfRnwFBB4/HQuYkV+kQ9Uc=",
|
||||
"requires": {
|
||||
"debug": "^3.1.0"
|
||||
|
@ -8883,7 +8864,7 @@
|
|||
},
|
||||
"validator": {
|
||||
"version": "9.4.1",
|
||||
"resolved": "http://registry.npmjs.org/validator/-/validator-9.4.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz",
|
||||
"integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA=="
|
||||
},
|
||||
"value-or-function": {
|
||||
|
@ -9623,9 +9604,9 @@
|
|||
}
|
||||
},
|
||||
"vscode-azureextensionui": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npmjs.org/vscode-azureextensionui/-/vscode-azureextensionui-0.21.1.tgz",
|
||||
"integrity": "sha512-wL44OOTPYEfXmu+OmWi/p82c1qNL9UbNWWfuXJM7MNVBGeMmW47Da4L2zBsr3m4P0ZQni67N2sdmKgIuhmoeHA==",
|
||||
"version": "0.21.2",
|
||||
"resolved": "https://registry.npmjs.org/vscode-azureextensionui/-/vscode-azureextensionui-0.21.2.tgz",
|
||||
"integrity": "sha512-pK6dScTLUwwlBKWQSACC2zQrVsgWqxf2E2wLag/MDn1yUVJMm1qC/bnx3jZepJOYlpOzz+mYZ6UJRrD3mxuNGg==",
|
||||
"requires": {
|
||||
"azure-arm-resource": "^3.0.0-preview",
|
||||
"azure-arm-storage": "^3.1.0",
|
||||
|
|
|
@ -865,7 +865,7 @@
|
|||
"request-promise": "^4.2.2",
|
||||
"semver": "^5.5.0",
|
||||
"vscode-azureappservice": "^0.32.0",
|
||||
"vscode-azureextensionui": "^0.21.1",
|
||||
"vscode-azureextensionui": "^0.21.2",
|
||||
"vscode-azurekudu": "^0.1.8",
|
||||
"vscode-nls": "^4.0.0",
|
||||
"websocket": "^1.0.25",
|
||||
|
|
|
@ -4,13 +4,12 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import * as vscode from 'vscode';
|
||||
import { DialogResponses, IActionContext, IAzureQuickPickItem, parseError, StorageAccountKind, StorageAccountPerformance, StorageAccountReplication } from 'vscode-azureextensionui';
|
||||
import { DialogResponses, IActionContext, parseError, StorageAccountKind, StorageAccountPerformance, StorageAccountReplication } from 'vscode-azureextensionui';
|
||||
import { localSettingsFileName } from './constants';
|
||||
import { NoSubscriptionError } from './errors';
|
||||
import { ext } from './extensionVariables';
|
||||
import { localize } from './localize';
|
||||
import { getResourceTypeLabel, ResourceType } from './templates/IFunctionSetting';
|
||||
import * as azUtil from './utils/azure';
|
||||
import * as fsUtil from './utils/fs';
|
||||
|
||||
|
@ -22,80 +21,8 @@ export interface ILocalAppSettings {
|
|||
|
||||
export const azureWebJobsStorageKey: string = 'AzureWebJobsStorage';
|
||||
|
||||
export async function promptForAppSetting(actionContext: IActionContext, localSettingsPath: string, resourceType: ResourceType): Promise<string> {
|
||||
const settings: ILocalAppSettings = await getLocalSettings(localSettingsPath);
|
||||
const resourceTypeLabel: string = getResourceTypeLabel(resourceType);
|
||||
|
||||
if (settings.Values) {
|
||||
const existingSettings: string[] = Object.keys(settings.Values);
|
||||
if (existingSettings.length !== 0) {
|
||||
let picks: IAzureQuickPickItem<boolean>[] = [{ data: true /* createNewAppSetting */, label: localize('azFunc.newAppSetting', '$(plus) New App Setting'), description: '' }];
|
||||
picks = picks.concat(existingSettings.map((s: string) => { return { data: false /* createNewAppSetting */, label: s, description: '' }; }));
|
||||
const options: vscode.QuickPickOptions = { placeHolder: localize('azFunc.selectAppSetting', 'Select an App Setting for your \'{0}\'', resourceTypeLabel) };
|
||||
const result: IAzureQuickPickItem<boolean> = await ext.ui.showQuickPick(picks, options);
|
||||
if (!result.data /* createNewAppSetting */) {
|
||||
return result.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let resourceResult: azUtil.IResourceResult | undefined;
|
||||
try {
|
||||
switch (resourceType) {
|
||||
case ResourceType.DocumentDB:
|
||||
resourceResult = await azUtil.promptForCosmosDBAccount();
|
||||
break;
|
||||
case ResourceType.Storage:
|
||||
resourceResult = await azUtil.promptForStorageAccount(
|
||||
actionContext,
|
||||
{
|
||||
kind: [
|
||||
StorageAccountKind.BlobStorage
|
||||
],
|
||||
learnMoreLink: 'https://aka.ms/T5o0nf'
|
||||
}
|
||||
);
|
||||
break;
|
||||
case ResourceType.ServiceBus:
|
||||
resourceResult = await azUtil.promptForServiceBus();
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof NoSubscriptionError) {
|
||||
// swallow error and prompt for connection string instead
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
const appSettingSuffix: string = `_${resourceType.toUpperCase()}`;
|
||||
let appSettingKey: string;
|
||||
let connectionString: string;
|
||||
if (resourceResult) {
|
||||
appSettingKey = `${resourceResult.name}${appSettingSuffix}`;
|
||||
connectionString = resourceResult.connectionString;
|
||||
} else {
|
||||
const keyOptions: vscode.InputBoxOptions = {
|
||||
placeHolder: localize('azFunc.AppSettingKeyPlaceholder', '\'{0}\' App Setting Key', resourceTypeLabel),
|
||||
prompt: localize('azFunc.AppSettingKeyPrompt', 'Enter a key for your \'{0}\' connection string', resourceTypeLabel),
|
||||
value: `example${appSettingSuffix}`
|
||||
};
|
||||
appSettingKey = await ext.ui.showInputBox(keyOptions);
|
||||
|
||||
const valueOptions: vscode.InputBoxOptions = {
|
||||
placeHolder: localize('azFunc.AppSettingValuePlaceholder', '\'{0}\' App Setting Value', resourceTypeLabel),
|
||||
prompt: localize('azFunc.AppSettingValuePrompt', 'Enter the connection string for your \'{0}\'', resourceTypeLabel)
|
||||
};
|
||||
connectionString = await ext.ui.showInputBox(valueOptions);
|
||||
}
|
||||
|
||||
await setAppSetting(settings, localSettingsPath, appSettingKey, connectionString);
|
||||
return appSettingKey;
|
||||
}
|
||||
|
||||
export async function validateAzureWebJobsStorage(actionContext: IActionContext, localSettingsPath: string): Promise<void> {
|
||||
const settings: ILocalAppSettings = await getLocalSettings(localSettingsPath);
|
||||
const settings: ILocalAppSettings = await getLocalAppSettings(localSettingsPath);
|
||||
if (settings.Values && settings.Values[azureWebJobsStorageKey]) {
|
||||
return;
|
||||
}
|
||||
|
@ -104,47 +31,37 @@ export async function validateAzureWebJobsStorage(actionContext: IActionContext,
|
|||
const selectStorageAccount: vscode.MessageItem = { title: localize('azFunc.SelectStorageAccount', 'Select Storage Account') };
|
||||
const result: vscode.MessageItem = await ext.ui.showWarningMessage(message, selectStorageAccount, DialogResponses.skipForNow);
|
||||
if (result === selectStorageAccount) {
|
||||
let connectionString: string;
|
||||
|
||||
try {
|
||||
const resourceResult: azUtil.IResourceResult = await azUtil.promptForStorageAccount(
|
||||
actionContext,
|
||||
{
|
||||
kind: [
|
||||
StorageAccountKind.BlobStorage
|
||||
],
|
||||
performance: [
|
||||
StorageAccountPerformance.Premium
|
||||
],
|
||||
replication: [
|
||||
StorageAccountReplication.ZRS
|
||||
],
|
||||
learnMoreLink: 'https://aka.ms/Cfqnrc'
|
||||
}
|
||||
);
|
||||
connectionString = resourceResult.connectionString;
|
||||
} catch (error) {
|
||||
if (error instanceof NoSubscriptionError) {
|
||||
const options: vscode.InputBoxOptions = {
|
||||
placeHolder: localize('azFunc.StoragePlaceholder', '\'{0}\' Connection String', azureWebJobsStorageKey),
|
||||
prompt: localize('azFunc.StoragePrompt', 'Enter the connection string for your \'{0}\'', azureWebJobsStorageKey)
|
||||
};
|
||||
connectionString = await ext.ui.showInputBox(options);
|
||||
} else {
|
||||
throw error;
|
||||
const resourceResult: azUtil.IResourceResult = await azUtil.promptForStorageAccount(
|
||||
actionContext,
|
||||
{
|
||||
kind: [
|
||||
StorageAccountKind.BlobStorage
|
||||
],
|
||||
performance: [
|
||||
StorageAccountPerformance.Premium
|
||||
],
|
||||
replication: [
|
||||
StorageAccountReplication.ZRS
|
||||
],
|
||||
learnMoreLink: 'https://aka.ms/Cfqnrc'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
await setAppSetting(settings, localSettingsPath, azureWebJobsStorageKey, connectionString);
|
||||
// tslint:disable-next-line:strict-boolean-expressions
|
||||
settings.Values = settings.Values || {};
|
||||
settings.Values[azureWebJobsStorageKey] = resourceResult.connectionString;
|
||||
await fsUtil.writeFormattedJson(localSettingsPath, settings);
|
||||
}
|
||||
}
|
||||
export async function setLocalAppSetting(functionAppPath: string, key: string, value: string): Promise<void> {
|
||||
const localSettingsPath: string = path.join(functionAppPath, localSettingsFileName);
|
||||
const settings: ILocalAppSettings = await getLocalAppSettings(localSettingsPath);
|
||||
|
||||
async function setAppSetting(settings: ILocalAppSettings, localSettingsPath: string, key: string, value: string): Promise<void> {
|
||||
if (!settings.Values) {
|
||||
settings.Values = {};
|
||||
}
|
||||
|
||||
if (settings.Values[key]) {
|
||||
// tslint:disable-next-line:strict-boolean-expressions
|
||||
settings.Values = settings.Values || {};
|
||||
if (settings.Values[key] === value) {
|
||||
return;
|
||||
} else if (settings.Values[key]) {
|
||||
const message: string = localize('azFunc.SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', key);
|
||||
if (await ext.ui.showWarningMessage(message, { modal: true }, DialogResponses.yes, DialogResponses.cancel) !== DialogResponses.yes) {
|
||||
return;
|
||||
|
@ -155,7 +72,7 @@ async function setAppSetting(settings: ILocalAppSettings, localSettingsPath: str
|
|||
await fsUtil.writeFormattedJson(localSettingsPath, settings);
|
||||
}
|
||||
|
||||
export async function getLocalSettings(localSettingsPath: string, allowOverwrite: boolean = false): Promise<ILocalAppSettings> {
|
||||
export async function getLocalAppSettings(localSettingsPath: string, allowOverwrite: boolean = false): Promise<ILocalAppSettings> {
|
||||
if (await fse.pathExists(localSettingsPath)) {
|
||||
const data: string = (await fse.readFile(localSettingsPath)).toString();
|
||||
if (/[^\s]/.test(data)) {
|
||||
|
|
|
@ -9,7 +9,7 @@ import * as vscode from 'vscode';
|
|||
import { AppSettingsTreeItem, SiteClient } from "vscode-azureappservice";
|
||||
import { localSettingsFileName } from "../../constants";
|
||||
import { ext } from "../../extensionVariables";
|
||||
import { getLocalSettings, ILocalAppSettings } from "../../LocalAppSettings";
|
||||
import { getLocalAppSettings, ILocalAppSettings } from "../../LocalAppSettings";
|
||||
import { localize } from "../../localize";
|
||||
import * as workspaceUtil from '../../utils/workspace';
|
||||
import { confirmOverwriteSettings } from "./confirmOverwriteSettings";
|
||||
|
@ -30,7 +30,7 @@ export async function downloadAppSettings(node?: AppSettingsTreeItem): Promise<v
|
|||
await node.runWithTemporaryDescription(localize('downloading', 'Downloading...'), async () => {
|
||||
ext.outputChannel.show(true);
|
||||
ext.outputChannel.appendLine(localize('downloadStart', 'Downloading settings from "{0}"...', client.fullName));
|
||||
let localSettings: ILocalAppSettings = await getLocalSettings(localSettingsPath, true /* allowOverwrite */);
|
||||
let localSettings: ILocalAppSettings = await getLocalAppSettings(localSettingsPath, true /* allowOverwrite */);
|
||||
|
||||
const isEncrypted: boolean | undefined = localSettings.IsEncrypted;
|
||||
if (localSettings.IsEncrypted) {
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { IAzureUserInput } from 'vscode-azureextensionui';
|
||||
// tslint:disable-next-line:no-require-imports
|
||||
import XRegExp = require('xregexp');
|
||||
import { ProjectRuntime } from '../../constants';
|
||||
import { localize } from "../../localize";
|
||||
import { executeDotnetTemplateCommand } from '../../templates/executeDotnetTemplateCommand';
|
||||
import { cpUtils } from '../../utils/cpUtils';
|
||||
import { dotnetUtils } from '../../utils/dotnetUtils';
|
||||
import * as fsUtil from '../../utils/fs';
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
|
||||
export class CSharpFunctionCreator extends FunctionCreatorBase {
|
||||
private _functionName: string;
|
||||
private _namespace: string;
|
||||
|
||||
public async promptForSettings(ui: IAzureUserInput, functionName: string | undefined, functionSettings: { [key: string]: string | undefined }): Promise<void> {
|
||||
if (!functionName) {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(this._functionAppPath, this._template.defaultFunctionName, '.cs');
|
||||
this._functionName = await ui.showInputBox({
|
||||
placeHolder: localize('azFunc.funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('azFunc.funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(s),
|
||||
value: defaultFunctionName || this._template.defaultFunctionName
|
||||
});
|
||||
} else {
|
||||
this._functionName = functionName;
|
||||
}
|
||||
|
||||
if (functionSettings.namespace !== undefined) {
|
||||
this._namespace = functionSettings.namespace;
|
||||
} else {
|
||||
this._namespace = await ui.showInputBox({
|
||||
placeHolder: localize('azFunc.namespacePlaceHolder', 'Namespace'),
|
||||
prompt: localize('azFunc.namespacePrompt', 'Provide a namespace'),
|
||||
validateInput: validateCSharpNamespace,
|
||||
value: 'Company.Function'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async createFunction(userSettings: { [propertyName: string]: string }, runtime: ProjectRuntime): Promise<string | undefined> {
|
||||
await dotnetUtils.validateDotnetInstalled(this._actionContext);
|
||||
|
||||
const args: string[] = [];
|
||||
args.push('--arg:name');
|
||||
args.push(cpUtils.wrapArgInQuotes(this._functionName));
|
||||
|
||||
args.push('--arg:namespace');
|
||||
args.push(cpUtils.wrapArgInQuotes(this._namespace));
|
||||
|
||||
for (const key of Object.keys(userSettings)) {
|
||||
args.push(`--arg:${key}`);
|
||||
args.push(cpUtils.wrapArgInQuotes(userSettings[key]));
|
||||
}
|
||||
|
||||
await executeDotnetTemplateCommand(runtime, this._functionAppPath, 'create', '--identity', this._template.id, ...args);
|
||||
|
||||
return path.join(this._functionAppPath, `${this._functionName}.cs`);
|
||||
}
|
||||
|
||||
private validateTemplateName(name: string | undefined): string | undefined {
|
||||
if (!name) {
|
||||
return localize('azFunc.emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(path.join(this._functionAppPath, `${name}.cs`))) {
|
||||
return localize('azFunc.existingCSFile', 'A CSharp file with the name \'{0}\' already exists.', name);
|
||||
} else if (!this._functionNameRegex.test(name)) {
|
||||
return localize('azFunc.functionNameInvalidError', 'Function name must start with a letter and can contain letters, digits, \'_\' and \'-\'');
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Identifier specification: https://github.com/dotnet/csharplang/blob/master/spec/lexical-structure.md#identifiers
|
||||
const formattingCharacter: string = '\\p{Cf}';
|
||||
const connectingCharacter: string = '\\p{Pc}';
|
||||
const decimalDigitCharacter: string = '\\p{Nd}';
|
||||
const combiningCharacter: string = '\\p{Mn}|\\p{Mc}';
|
||||
const letterCharacter: string = '\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}';
|
||||
const identifierPartCharacter: string = `${letterCharacter}|${decimalDigitCharacter}|${connectingCharacter}|${combiningCharacter}|${formattingCharacter}`;
|
||||
const identifierStartCharacter: string = `(${letterCharacter}|_)`;
|
||||
const identifierOrKeyword: string = `${identifierStartCharacter}(${identifierPartCharacter})*`;
|
||||
const identifierRegex: RegExp = XRegExp(`^${identifierOrKeyword}$`);
|
||||
// Keywords: https://github.com/dotnet/csharplang/blob/master/spec/lexical-structure.md#keywords
|
||||
const keywords: string[] = ['abstract', 'as', 'base', 'bool', 'break', 'byte', 'case', 'catch', 'char', 'checked', 'class', 'const', 'continue', 'decimal', 'default', 'delegate', 'do', 'double', 'else', 'enum', 'event', 'explicit', 'extern', 'false', 'finally', 'fixed', 'float', 'for', 'foreach', 'goto', 'if', 'implicit', 'in', 'int', 'interface', 'internal', 'is', 'lock', 'long', 'namespace', 'new', 'null', 'object', 'operator', 'out', 'override', 'params', 'private', 'protected', 'public', 'readonly', 'ref', 'return', 'sbyte', 'sealed', 'short', 'sizeof', 'stackalloc', 'static', 'string', 'struct', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'uint', 'ulong', 'unchecked', 'unsafe', 'ushort', 'using', 'virtual', 'void', 'volatile', 'while'];
|
||||
|
||||
export function validateCSharpNamespace(value: string | undefined): string | undefined {
|
||||
if (!value) {
|
||||
return localize('azFunc.cSharpEmptyTemplateNameError', 'The template name cannot be empty.');
|
||||
}
|
||||
|
||||
// Namespace specification: https://github.com/dotnet/csharplang/blob/master/spec/namespaces.md#namespace-declarations
|
||||
const identifiers: string[] = value.split('.');
|
||||
for (const identifier of identifiers) {
|
||||
if (identifier === '') {
|
||||
return localize('azFunc.cSharpExtraPeriod', 'Leading or trailing "." character is not allowed.');
|
||||
} else if (!identifierRegex.test(identifier)) {
|
||||
return localize('azFunc.cSharpInvalidCharacters', 'The identifier "{0}" contains invalid characters.', identifier);
|
||||
} else if (keywords.find((s: string) => s === identifier.toLowerCase()) !== undefined) {
|
||||
return localize('azFunc.cSharpKeywordWarning', 'The identifier "{0}" is a reserved keyword.', identifier);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IActionContext, IAzureUserInput } from "vscode-azureextensionui";
|
||||
import { ProjectRuntime } from "../../constants";
|
||||
import { IFunctionTemplate } from "../../templates/IFunctionTemplate";
|
||||
|
||||
export abstract class FunctionCreatorBase {
|
||||
protected readonly _functionNameRegex: RegExp = /^[a-zA-Z][a-zA-Z\d_\-]*$/;
|
||||
protected _functionAppPath: string;
|
||||
protected _template: IFunctionTemplate;
|
||||
protected _actionContext: IActionContext;
|
||||
|
||||
constructor(functionAppPath: string, template: IFunctionTemplate, actionContext: IActionContext) {
|
||||
this._functionAppPath = functionAppPath;
|
||||
this._template = template;
|
||||
this._actionContext = actionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt for any settings that are specific to this creator
|
||||
* This includes the function name (Since the name could have different restrictions for different languages)
|
||||
*/
|
||||
public abstract async promptForSettings(ui: IAzureUserInput, functionName: string | undefined, functionSettings: { [key: string]: string | undefined }): Promise<void>;
|
||||
public abstract async createFunction(userSettings: { [propertyName: string]: string }, runtime: ProjectRuntime): Promise<string | undefined>;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IActionContext, ISubscriptionWizardContext } from "vscode-azureextensionui";
|
||||
import { ProjectLanguage, ProjectRuntime } from "../../constants";
|
||||
import { IFunctionTemplate } from "../../templates/IFunctionTemplate";
|
||||
|
||||
export interface IFunctionWizardContext extends Partial<ISubscriptionWizardContext> {
|
||||
functionAppPath: string;
|
||||
runtime: ProjectRuntime;
|
||||
language: ProjectLanguage;
|
||||
actionContext: IActionContext;
|
||||
template: IFunctionTemplate;
|
||||
|
||||
functionName?: string;
|
||||
newFilePath?: string;
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { IAzureUserInput } from 'vscode-azureextensionui';
|
||||
import { ext } from '../../extensionVariables';
|
||||
import { localize } from "../../localize";
|
||||
import { removeLanguageFromId } from "../../templates/TemplateProvider";
|
||||
import * as fsUtil from '../../utils/fs';
|
||||
import { getFullClassName, parseJavaClassName, validatePackageName } from "../../utils/javaNameUtils";
|
||||
import { mavenUtils } from "../../utils/mavenUtils";
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
|
||||
function getNewJavaFunctionFilePath(functionAppPath: string, packageName: string, functionName: string): string {
|
||||
return path.join(functionAppPath, 'src', 'main', 'java', ...packageName.split('.'), `${parseJavaClassName(functionName)}.java`);
|
||||
}
|
||||
|
||||
export class JavaFunctionCreator extends FunctionCreatorBase {
|
||||
private _packageName: string;
|
||||
private _functionName: string;
|
||||
|
||||
public async promptForSettings(ui: IAzureUserInput, functionName: string | undefined): Promise<void> {
|
||||
this._packageName = await ui.showInputBox({
|
||||
placeHolder: localize('azFunc.java.packagePlaceHolder', 'Package'),
|
||||
prompt: localize('azFunc.java.packagePrompt', 'Provide a package name'),
|
||||
validateInput: validatePackageName,
|
||||
value: 'com.function'
|
||||
});
|
||||
|
||||
if (!functionName) {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueJavaFsPath(this._functionAppPath, this._packageName, `${removeLanguageFromId(this._template.id)}Java`);
|
||||
this._functionName = await ui.showInputBox({
|
||||
placeHolder: localize('azFunc.funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('azFunc.funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(s),
|
||||
value: defaultFunctionName || this._template.defaultFunctionName
|
||||
});
|
||||
} else {
|
||||
this._functionName = functionName;
|
||||
}
|
||||
}
|
||||
|
||||
public async createFunction(userSettings: { [propertyName: string]: string }): Promise<string | undefined> {
|
||||
const javaFuntionProperties: string[] = [];
|
||||
for (const key of Object.keys(userSettings)) {
|
||||
javaFuntionProperties.push(mavenUtils.formatMavenArg(`D${key}`, userSettings[key]));
|
||||
}
|
||||
await mavenUtils.validateMavenInstalled(this._actionContext, this._functionAppPath);
|
||||
ext.outputChannel.show();
|
||||
await mavenUtils.executeMvnCommand(
|
||||
this._actionContext.properties,
|
||||
ext.outputChannel,
|
||||
this._functionAppPath,
|
||||
'azure-functions:add',
|
||||
'-B',
|
||||
mavenUtils.formatMavenArg('Dfunctions.package', this._packageName),
|
||||
mavenUtils.formatMavenArg('Dfunctions.name', this._functionName),
|
||||
mavenUtils.formatMavenArg('Dfunctions.template', removeLanguageFromId(this._template.id)),
|
||||
...javaFuntionProperties
|
||||
);
|
||||
|
||||
return getNewJavaFunctionFilePath(this._functionAppPath, this._packageName, this._functionName);
|
||||
}
|
||||
|
||||
private validateTemplateName(name: string | undefined): string | undefined {
|
||||
if (!name) {
|
||||
return localize('azFunc.emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(getNewJavaFunctionFilePath(this._functionAppPath, this._packageName, name))) {
|
||||
return localize('azFunc.existingFolderError', 'The Java class \'{0}\' already exists.', getFullClassName(this._packageName, name));
|
||||
} else if (!this._functionNameRegex.test(name)) {
|
||||
return localize('azFunc.functionNameInvalidError', 'Function name must start with a letter and can contain letters, digits, \'_\' and \'-\'');
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { IActionContext, IAzureUserInput } from 'vscode-azureextensionui';
|
||||
import { ProjectLanguage } from '../../constants';
|
||||
import { IFunctionJson } from '../../FunctionConfig';
|
||||
import { localize } from "../../localize";
|
||||
import { IScriptFunctionTemplate } from '../../templates/parseScriptTemplates';
|
||||
import * as fsUtil from '../../utils/fs';
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
|
||||
export function getScriptFileNameFromLanguage(language: string): string | undefined {
|
||||
switch (language) {
|
||||
case ProjectLanguage.Bash:
|
||||
return 'run.sh';
|
||||
case ProjectLanguage.Batch:
|
||||
return 'run.bat';
|
||||
case ProjectLanguage.CSharpScript:
|
||||
return 'run.csx';
|
||||
case ProjectLanguage.FSharpScript:
|
||||
return 'run.fsx';
|
||||
case ProjectLanguage.JavaScript:
|
||||
return 'index.js';
|
||||
case ProjectLanguage.PHP:
|
||||
return 'run.php';
|
||||
case ProjectLanguage.PowerShell:
|
||||
return 'run.ps1';
|
||||
case ProjectLanguage.Python:
|
||||
return '__init__.py';
|
||||
case ProjectLanguage.TypeScript:
|
||||
return 'index.ts';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function creator for multiple languages that don't require compilation (JavaScript, C# Script, Bash, etc.)
|
||||
*/
|
||||
export class ScriptFunctionCreator extends FunctionCreatorBase {
|
||||
protected _template: IScriptFunctionTemplate;
|
||||
protected _functionName: string;
|
||||
private _language: string;
|
||||
|
||||
constructor(functionAppPath: string, template: IScriptFunctionTemplate, actionContext: IActionContext, language: string) {
|
||||
super(functionAppPath, template, actionContext);
|
||||
this._language = language;
|
||||
}
|
||||
|
||||
public async promptForSettings(ui: IAzureUserInput, functionName: string | undefined): Promise<void> {
|
||||
if (!functionName) {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(this._functionAppPath, this._template.defaultFunctionName);
|
||||
this._functionName = await ui.showInputBox({
|
||||
placeHolder: localize('azFunc.funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('azFunc.funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(s),
|
||||
value: defaultFunctionName || this._template.defaultFunctionName
|
||||
});
|
||||
} else {
|
||||
this._functionName = functionName;
|
||||
}
|
||||
}
|
||||
|
||||
public async createFunction(userSettings: { [propertyName: string]: string }): Promise<string | undefined> {
|
||||
const functionPath: string = path.join(this._functionAppPath, this._functionName);
|
||||
await fse.ensureDir(functionPath);
|
||||
await Promise.all(Object.keys(this._template.templateFiles).map(async (fileName: string) => {
|
||||
await fse.writeFile(path.join(functionPath, fileName), this._template.templateFiles[fileName]);
|
||||
}));
|
||||
|
||||
for (const key of Object.keys(userSettings)) {
|
||||
this._template.functionConfig.inBinding[key] = userSettings[key];
|
||||
}
|
||||
|
||||
const functionJson: IFunctionJson = this._template.functionConfig.functionJson;
|
||||
if (this.editFunctionJson) {
|
||||
await this.editFunctionJson(functionJson);
|
||||
}
|
||||
|
||||
await fsUtil.writeFormattedJson(path.join(functionPath, 'function.json'), functionJson);
|
||||
|
||||
const mainFileName: string | undefined = getScriptFileNameFromLanguage(this._language);
|
||||
if (mainFileName) {
|
||||
return path.join(functionPath, mainFileName);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
protected editFunctionJson?(functionJson: IFunctionJson): Promise<void>;
|
||||
|
||||
private validateTemplateName(name: string | undefined): string | undefined {
|
||||
if (!name) {
|
||||
return localize('azFunc.emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(path.join(this._functionAppPath, name))) {
|
||||
return localize('azFunc.existingFolderError', 'A folder with the name \'{0}\' already exists.', name);
|
||||
} else if (!this._functionNameRegex.test(name)) {
|
||||
return localize('azFunc.functionNameInvalidError', 'Function name must start with a letter and can contain letters, digits, \'_\' and \'-\'');
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { tsConfigFileName, tsDefaultOutDir } from '../../constants';
|
||||
import { IFunctionJson } from '../../FunctionConfig';
|
||||
import { ScriptFunctionCreator } from './ScriptFunctionCreator';
|
||||
|
||||
export class TypeScriptFunctionCreator extends ScriptFunctionCreator {
|
||||
protected async editFunctionJson(functionJson: IFunctionJson): Promise<void> {
|
||||
let outDir: string = tsDefaultOutDir;
|
||||
try {
|
||||
const tsconfigPath: string = path.join(this._functionAppPath, tsConfigFileName);
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
outDir = (await fse.readJSON(tsconfigPath)).compilerOptions.outDir;
|
||||
} catch {
|
||||
// ignore and use default outDir
|
||||
}
|
||||
|
||||
functionJson.scriptFile = path.join('..', outDir, this._functionName, 'index.js');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Progress } from 'vscode';
|
||||
import { AzureWizardExecuteStep } from 'vscode-azureextensionui';
|
||||
import { setLocalAppSetting } from '../../../LocalAppSettings';
|
||||
import { localize } from '../../../localize';
|
||||
import { IFunctionSetting } from '../../../templates/IFunctionSetting';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export interface IConnection {
|
||||
name: string;
|
||||
connectionString: string;
|
||||
}
|
||||
|
||||
export abstract class AzureConnectionCreateStepBase<T extends IFunctionWizardContext> extends AzureWizardExecuteStep<T> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
|
||||
constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public abstract async getConnection(wizardContext: T): Promise<IConnection>;
|
||||
|
||||
public async execute(wizardContext: T, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
|
||||
progress.report({ message: localize('retrieving', 'Retrieving connection string...') });
|
||||
|
||||
const result: IConnection = await this.getConnection(wizardContext);
|
||||
const appSettingKey: string = `${result.name}_${nonNullProp(this._setting, 'resourceType').toUpperCase()}`;
|
||||
wizardContext[this._setting.name] = appSettingKey;
|
||||
await setLocalAppSetting(wizardContext.functionAppPath, appSettingKey, result.connectionString);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CosmosDBManagementClient, CosmosDBManagementModels } from 'azure-arm-cosmosdb';
|
||||
import { createAzureClient } from 'vscode-azureextensionui';
|
||||
import { getResourceGroupFromId } from '../../../utils/azure';
|
||||
import { nonNullProp, nonNullValue } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
import { AzureConnectionCreateStepBase, IConnection } from './AzureConnectionCreateStepBase';
|
||||
import { ICosmosDBWizardContext } from './ICosmosDBWizardContext';
|
||||
|
||||
export class CosmosDBConnectionCreateStep extends AzureConnectionCreateStepBase<IFunctionWizardContext & ICosmosDBWizardContext> {
|
||||
public async getConnection(wizardContext: ICosmosDBWizardContext): Promise<IConnection> {
|
||||
const databaseAccount: CosmosDBManagementModels.DatabaseAccount = nonNullProp(wizardContext, 'databaseAccount');
|
||||
const name: string = nonNullProp(databaseAccount, 'name');
|
||||
|
||||
const client: CosmosDBManagementClient = createAzureClient(wizardContext, CosmosDBManagementClient);
|
||||
const resourceGroup: string = getResourceGroupFromId(nonNullProp(databaseAccount, 'id'));
|
||||
const csListResult: CosmosDBManagementModels.DatabaseAccountListConnectionStringsResult = await client.databaseAccounts.listConnectionStrings(resourceGroup, name);
|
||||
const cs: CosmosDBManagementModels.DatabaseAccountConnectionString = nonNullValue(nonNullProp(csListResult, 'connectionStrings')[0], 'connectionStrings[0]');
|
||||
return {
|
||||
name,
|
||||
connectionString: nonNullProp(cs, 'connectionString')
|
||||
};
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: ICosmosDBWizardContext): boolean {
|
||||
return !!wizardContext.databaseAccount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CosmosDBManagementClient } from 'azure-arm-cosmosdb';
|
||||
import { AzureWizardPromptStep, createAzureClient, ISubWizardOptions } from 'vscode-azureextensionui';
|
||||
import { localize } from '../../../localize';
|
||||
import { promptForResource } from '../../../utils/azure';
|
||||
import { ICosmosDBWizardContext } from './ICosmosDBWizardContext';
|
||||
|
||||
export class CosmosDBListStep extends AzureWizardPromptStep<ICosmosDBWizardContext> {
|
||||
public async prompt(wizardContext: ICosmosDBWizardContext): Promise<void | ISubWizardOptions<ICosmosDBWizardContext>> {
|
||||
const placeHolder: string = localize('placeHolder', 'Select a database account');
|
||||
const client: CosmosDBManagementClient = createAzureClient(wizardContext, CosmosDBManagementClient);
|
||||
wizardContext.databaseAccount = await promptForResource(placeHolder, client.databaseAccounts.list());
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: ICosmosDBWizardContext): boolean {
|
||||
return !wizardContext.databaseAccount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CosmosDBManagementModels } from 'azure-arm-cosmosdb';
|
||||
import { ISubscriptionWizardContext } from 'vscode-azureextensionui';
|
||||
|
||||
export interface ICosmosDBWizardContext extends ISubscriptionWizardContext {
|
||||
databaseAccount?: CosmosDBManagementModels.DatabaseAccount;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServiceBusManagementModels } from 'azure-arm-sb';
|
||||
import { ISubscriptionWizardContext } from 'vscode-azureextensionui';
|
||||
|
||||
export interface IServiceBusWizardContext extends ISubscriptionWizardContext {
|
||||
sbNamespace?: ServiceBusManagementModels.SBNamespace;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServiceBusManagementClient, ServiceBusManagementModels } from 'azure-arm-sb';
|
||||
import { createAzureClient } from 'vscode-azureextensionui';
|
||||
import { localize } from '../../../localize';
|
||||
import { getResourceGroupFromId } from '../../../utils/azure';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
import { AzureConnectionCreateStepBase, IConnection } from './AzureConnectionCreateStepBase';
|
||||
import { IServiceBusWizardContext } from './IServiceBusWizardContext';
|
||||
|
||||
export class ServiceBusConnectionCreateStep extends AzureConnectionCreateStepBase<IServiceBusWizardContext & IFunctionWizardContext> {
|
||||
public async getConnection(wizardContext: IServiceBusWizardContext): Promise<IConnection> {
|
||||
const sbNamespace: ServiceBusManagementModels.SBNamespace = nonNullProp(wizardContext, 'sbNamespace');
|
||||
const id: string = nonNullProp(sbNamespace, 'id');
|
||||
const name: string = nonNullProp(sbNamespace, 'name');
|
||||
|
||||
const resourceGroup: string = getResourceGroupFromId(id);
|
||||
const client: ServiceBusManagementClient = createAzureClient(wizardContext, ServiceBusManagementClient);
|
||||
const authRules: ServiceBusManagementModels.SBAuthorizationRule[] = await client.namespaces.listAuthorizationRules(resourceGroup, name);
|
||||
const authRule: ServiceBusManagementModels.SBAuthorizationRule | undefined = authRules.find(ar => ar.rights.some(r => r.toLowerCase() === 'listen'));
|
||||
if (!authRule) {
|
||||
throw new Error(localize('noAuthRule', 'Failed to get connection string for service bus namespace "{0}".', name));
|
||||
}
|
||||
const keys: ServiceBusManagementModels.AccessKeys = await client.namespaces.listKeys(resourceGroup, name, nonNullProp(authRule, 'name'));
|
||||
return {
|
||||
name: name,
|
||||
connectionString: nonNullProp(keys, 'primaryConnectionString')
|
||||
};
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IServiceBusWizardContext): boolean {
|
||||
return !!wizardContext.sbNamespace;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ServiceBusManagementClient } from 'azure-arm-sb';
|
||||
import { AzureWizardPromptStep, createAzureClient, ISubWizardOptions } from 'vscode-azureextensionui';
|
||||
import { localize } from '../../../localize';
|
||||
import { promptForResource } from '../../../utils/azure';
|
||||
import { IServiceBusWizardContext } from './IServiceBusWizardContext';
|
||||
|
||||
export class ServiceBusListStep extends AzureWizardPromptStep<IServiceBusWizardContext> {
|
||||
public async prompt(wizardContext: IServiceBusWizardContext): Promise<void | ISubWizardOptions<IServiceBusWizardContext>> {
|
||||
const placeHolder: string = localize('placeHolder', 'Select a service bus namespace');
|
||||
const client: ServiceBusManagementClient = createAzureClient(wizardContext, ServiceBusManagementClient);
|
||||
wizardContext.sbNamespace = await promptForResource(placeHolder, client.namespaces.list());
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IServiceBusWizardContext): boolean {
|
||||
return !wizardContext.sbNamespace;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { StorageManagementClient, StorageManagementModels } from 'azure-arm-storage';
|
||||
import { createAzureClient, IStorageAccountWizardContext } from 'vscode-azureextensionui';
|
||||
import { getResourceGroupFromId } from '../../../utils/azure';
|
||||
import { nonNullProp, nonNullValue } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
import { AzureConnectionCreateStepBase, IConnection } from './AzureConnectionCreateStepBase';
|
||||
|
||||
export class StorageConnectionCreateStep extends AzureConnectionCreateStepBase<IStorageAccountWizardContext & IFunctionWizardContext> {
|
||||
public async getConnection(wizardContext: IStorageAccountWizardContext): Promise<IConnection> {
|
||||
const storageAccount: StorageManagementModels.StorageAccount = <StorageManagementModels.StorageAccount>nonNullProp(wizardContext, 'storageAccount');
|
||||
const name: string = nonNullProp(storageAccount, 'name');
|
||||
|
||||
const client: StorageManagementClient = createAzureClient(wizardContext, StorageManagementClient);
|
||||
const resourceGroup: string = getResourceGroupFromId(nonNullProp(storageAccount, 'id'));
|
||||
const result: StorageManagementModels.StorageAccountListKeysResult = await client.storageAccounts.listKeys(resourceGroup, name);
|
||||
const key: string = nonNullProp(nonNullValue(nonNullProp(result, 'keys')[0], 'keys[0]'), 'value');
|
||||
|
||||
let endpointSuffix: string = nonNullProp(wizardContext.environment, 'storageEndpointSuffix');
|
||||
// https://github.com/Azure/azure-sdk-for-node/issues/4706
|
||||
if (endpointSuffix.startsWith('.')) {
|
||||
endpointSuffix = endpointSuffix.substr(1);
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
connectionString: `DefaultEndpointsProtocol=https;AccountName=${name};AccountKey=${key};EndpointSuffix=${endpointSuffix}`
|
||||
};
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IStorageAccountWizardContext): boolean {
|
||||
return !!wizardContext.storageAccount;
|
||||
}
|
||||
}
|
|
@ -6,71 +6,37 @@
|
|||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { isString } from 'util';
|
||||
import { InputBoxOptions, MessageItem, ProgressLocation, QuickPickItem, Uri, window, workspace } from 'vscode';
|
||||
import { callWithTelemetryAndErrorHandling, DialogResponses, IActionContext, IAzureQuickPickItem, TelemetryProperties } from 'vscode-azureextensionui';
|
||||
import { MessageItem, Uri, window, workspace, WorkspaceFolder } from 'vscode';
|
||||
import { AzureWizard, AzureWizardExecuteStep, AzureWizardPromptStep, callWithTelemetryAndErrorHandling, DialogResponses, IActionContext, IAzureQuickPickItem, IWizardOptions, TelemetryProperties, UserCancelledError } from 'vscode-azureextensionui';
|
||||
import { localSettingsFileName, ProjectLanguage, projectLanguageSetting, ProjectRuntime, projectRuntimeSetting, TemplateFilter } from '../../constants';
|
||||
import { SkipForNowError } from '../../errors';
|
||||
import { NoWorkspaceError } from '../../errors';
|
||||
import { ext } from '../../extensionVariables';
|
||||
import { addLocalFuncTelemetry } from '../../funcCoreTools/getLocalFuncCoreToolsVersion';
|
||||
import { promptForAppSetting, validateAzureWebJobsStorage } from '../../LocalAppSettings';
|
||||
import { validateAzureWebJobsStorage } from '../../LocalAppSettings';
|
||||
import { localize } from '../../localize';
|
||||
import { getProjectLanguage, getProjectRuntime, getTemplateFilter, promptForProjectLanguage, promptForProjectRuntime, selectTemplateFilter, updateWorkspaceSetting } from '../../ProjectSettings';
|
||||
import { IEnumValue, IFunctionSetting, ValueType } from '../../templates/IFunctionSetting';
|
||||
import { ValueType } from '../../templates/IFunctionSetting';
|
||||
import { IFunctionTemplate } from '../../templates/IFunctionTemplate';
|
||||
import { IScriptFunctionTemplate } from '../../templates/parseScriptTemplates';
|
||||
import { TemplateProvider } from '../../templates/TemplateProvider';
|
||||
import { nonNullValue } from '../../utils/nonNull';
|
||||
import * as workspaceUtil from '../../utils/workspace';
|
||||
import { createNewProject } from '../createNewProject/createNewProject';
|
||||
import { tryGetFunctionProjectRoot } from '../createNewProject/isFunctionProject';
|
||||
import { CSharpFunctionCreator } from './CSharpFunctionCreator';
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
import { JavaFunctionCreator } from './JavaFunctionCreator';
|
||||
import { ScriptFunctionCreator } from './ScriptFunctionCreator';
|
||||
import { TypeScriptFunctionCreator } from './TypeScriptFunctionCreator';
|
||||
import { DotnetFunctionCreateStep } from './dotnetSteps/DotnetFunctionCreateStep';
|
||||
import { DotnetFunctionNameStep } from './dotnetSteps/DotnetFunctionNameStep';
|
||||
import { DotnetNamespaceStep } from './dotnetSteps/DotnetNamespaceStep';
|
||||
import { IDotnetFunctionWizardContext } from './dotnetSteps/IDotnetFunctionWizardContext';
|
||||
import { BooleanPromptStep } from './genericSteps/BooleanPromptStep';
|
||||
import { EnumPromptStep } from './genericSteps/EnumPromptStep';
|
||||
import { LocalAppSettingListStep } from './genericSteps/LocalAppSettingListStep';
|
||||
import { StringPromptStep } from './genericSteps/StringPromptStep';
|
||||
import { IFunctionWizardContext } from './IFunctionWizardContext';
|
||||
import { JavaFunctionCreateStep } from './javaSteps/JavaFunctionCreateStep';
|
||||
import { JavaFunctionNameStep } from './javaSteps/JavaFunctionNameStep';
|
||||
import { JavaPackageNameStep } from './javaSteps/JavaPackageNameStep';
|
||||
import { ScriptFunctionCreateStep } from './scriptSteps/ScriptFunctionCreateStep';
|
||||
import { ScriptFunctionNameStep } from './scriptSteps/ScriptFunctionNameStep';
|
||||
import { TypeScriptFunctionCreateStep } from './scriptSteps/TypeScriptFunctionCreateStep';
|
||||
|
||||
async function promptForSetting(actionContext: IActionContext, localSettingsPath: string, setting: IFunctionSetting): Promise<string> {
|
||||
if (setting.resourceType !== undefined) {
|
||||
return await promptForAppSetting(actionContext, localSettingsPath, setting.resourceType);
|
||||
} else {
|
||||
switch (setting.valueType) {
|
||||
case ValueType.boolean:
|
||||
return await promptForBooleanSetting(setting);
|
||||
case ValueType.enum:
|
||||
return await promptForEnumSetting(setting);
|
||||
default:
|
||||
// Default to 'string' type for any setting that isn't supported
|
||||
return await promptForStringSetting(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function promptForEnumSetting(setting: IFunctionSetting): Promise<string> {
|
||||
const picks: IAzureQuickPickItem<string>[] = setting.enums.map((ev: IEnumValue) => { return { data: ev.value, label: ev.displayName, description: '' }; });
|
||||
|
||||
return (await ext.ui.showQuickPick(picks, { placeHolder: setting.label })).data;
|
||||
}
|
||||
|
||||
async function promptForBooleanSetting(setting: IFunctionSetting): Promise<string> {
|
||||
const picks: QuickPickItem[] = [
|
||||
{ label: 'true', description: '' },
|
||||
{ label: 'false', description: '' }
|
||||
];
|
||||
|
||||
return (await ext.ui.showQuickPick(picks, { placeHolder: setting.label })).label;
|
||||
}
|
||||
|
||||
async function promptForStringSetting(setting: IFunctionSetting): Promise<string> {
|
||||
const options: InputBoxOptions = {
|
||||
placeHolder: setting.label,
|
||||
prompt: setting.description || localize('azFunc.stringSettingPrompt', 'Provide a \'{0}\'', setting.label),
|
||||
validateInput: (s: string): string | undefined => setting.validateSetting(s),
|
||||
value: setting.defaultValue
|
||||
};
|
||||
return await ext.ui.showInputBox(options);
|
||||
}
|
||||
|
||||
// tslint:disable-next-line:max-func-body-length cyclomatic-complexity
|
||||
export async function createFunction(
|
||||
actionContext: IActionContext,
|
||||
folderPath?: string,
|
||||
|
@ -87,15 +53,27 @@ export async function createFunction(
|
|||
}
|
||||
|
||||
if (folderPath === undefined) {
|
||||
const folderPlaceholder: string = localize('azFunc.selectFunctionAppFolderExisting', 'Select the folder containing your function project');
|
||||
folderPath = await workspaceUtil.selectWorkspaceFolder(ext.ui, folderPlaceholder);
|
||||
const folderPlaceholder: string = localize('selectFunctionAppFolderExisting', 'Select the folder containing your function project');
|
||||
let folder: WorkspaceFolder | undefined;
|
||||
if (!workspace.workspaceFolders || workspace.workspaceFolders.length === 0) {
|
||||
throw new NoWorkspaceError();
|
||||
} else if (workspace.workspaceFolders.length === 1) {
|
||||
folder = workspace.workspaceFolders[0];
|
||||
} else {
|
||||
folder = await window.showWorkspaceFolderPick({ placeHolder: folderPlaceholder });
|
||||
if (!folder) {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
}
|
||||
|
||||
folderPath = folder.uri.fsPath;
|
||||
}
|
||||
|
||||
let isNewProject: boolean = false;
|
||||
let templateFilter: TemplateFilter;
|
||||
let functionAppPath: string | undefined = await tryGetFunctionProjectRoot(folderPath);
|
||||
if (!functionAppPath) {
|
||||
const message: string = localize('azFunc.notFunctionApp', 'The selected folder is not a function app project. Initialize Project?');
|
||||
const message: string = localize('notFunctionApp', 'The selected folder is not a function app project. Initialize Project?');
|
||||
const result: MessageItem = await ext.ui.showWarningMessage(message, { modal: true }, DialogResponses.yes, DialogResponses.skipForNow, DialogResponses.cancel);
|
||||
if (result === DialogResponses.yes) {
|
||||
await createNewProject(actionContext, folderPath, undefined, undefined, false);
|
||||
|
@ -109,8 +87,6 @@ export async function createFunction(
|
|||
functionAppPath = folderPath;
|
||||
}
|
||||
|
||||
const localSettingsPath: string = path.join(functionAppPath, localSettingsFileName);
|
||||
|
||||
if (language === undefined) {
|
||||
language = await getProjectLanguage(functionAppPath, ext.ui);
|
||||
}
|
||||
|
@ -138,54 +114,21 @@ export async function createFunction(
|
|||
actionContext.properties.projectLanguage = language;
|
||||
actionContext.properties.projectRuntime = runtime;
|
||||
actionContext.properties.templateFilter = templateFilter;
|
||||
|
||||
actionContext.properties.templateId = template.id;
|
||||
|
||||
let functionCreator: FunctionCreatorBase;
|
||||
switch (language) {
|
||||
case ProjectLanguage.Java:
|
||||
functionCreator = new JavaFunctionCreator(functionAppPath, template, actionContext);
|
||||
break;
|
||||
case ProjectLanguage.CSharp:
|
||||
functionCreator = new CSharpFunctionCreator(functionAppPath, template, actionContext);
|
||||
break;
|
||||
case ProjectLanguage.TypeScript:
|
||||
functionCreator = new TypeScriptFunctionCreator(functionAppPath, <IScriptFunctionTemplate>template, actionContext, language);
|
||||
break;
|
||||
default:
|
||||
functionCreator = new ScriptFunctionCreator(functionAppPath, <IScriptFunctionTemplate>template, actionContext, language);
|
||||
break;
|
||||
const wizardContext: IFunctionWizardContext = { functionName, functionAppPath, template, actionContext, runtime, language };
|
||||
const wizard: AzureWizard<IFunctionWizardContext> = new AzureWizard(wizardContext, getWizardOptions(wizardContext, functionSettings));
|
||||
await wizard.prompt(actionContext);
|
||||
await wizard.execute(actionContext);
|
||||
|
||||
const newFilePath: string | undefined = wizardContext.newFilePath;
|
||||
if (newFilePath && (await fse.pathExists(newFilePath))) {
|
||||
const newFileUri: Uri = Uri.file(newFilePath);
|
||||
window.showTextDocument(await workspace.openTextDocument(newFileUri));
|
||||
}
|
||||
|
||||
await functionCreator.promptForSettings(ext.ui, functionName, functionSettings);
|
||||
|
||||
const userSettings: { [propertyName: string]: string } = {};
|
||||
for (const setting of template.userPromptedSettings) {
|
||||
try {
|
||||
let settingValue: string | undefined;
|
||||
if (functionSettings[setting.name.toLowerCase()] !== undefined) {
|
||||
settingValue = functionSettings[setting.name.toLowerCase()];
|
||||
} else {
|
||||
settingValue = await promptForSetting(actionContext, localSettingsPath, setting);
|
||||
}
|
||||
|
||||
userSettings[setting.name] = settingValue ? settingValue : '';
|
||||
} catch (error) {
|
||||
if (!(error instanceof SkipForNowError)) { // ignore error if user wants to skip this app setting
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await window.withProgress({ location: ProgressLocation.Notification, title: localize('creatingFunction', 'Creating function...') }, async () => {
|
||||
const newFilePath: string | undefined = await functionCreator.createFunction(userSettings, nonNullValue(runtime, 'runtime'));
|
||||
if (newFilePath && (await fse.pathExists(newFilePath))) {
|
||||
const newFileUri: Uri = Uri.file(newFilePath);
|
||||
window.showTextDocument(await workspace.openTextDocument(newFileUri));
|
||||
}
|
||||
});
|
||||
|
||||
if (!template.isHttpTrigger) {
|
||||
const localSettingsPath: string = path.join(functionAppPath, localSettingsFileName);
|
||||
await validateAzureWebJobsStorage(actionContext, localSettingsPath);
|
||||
}
|
||||
|
||||
|
@ -199,6 +142,54 @@ export async function createFunction(
|
|||
}
|
||||
}
|
||||
|
||||
function getWizardOptions(wizardContext: IFunctionWizardContext, defaultSettings: { [key: string]: string | undefined }): IWizardOptions<IFunctionWizardContext> {
|
||||
const promptSteps: AzureWizardPromptStep<IFunctionWizardContext>[] = [];
|
||||
const executeSteps: AzureWizardExecuteStep<IFunctionWizardContext>[] = [];
|
||||
switch (wizardContext.language) {
|
||||
case ProjectLanguage.Java:
|
||||
promptSteps.push(new JavaPackageNameStep(), new JavaFunctionNameStep());
|
||||
executeSteps.push(new JavaFunctionCreateStep());
|
||||
break;
|
||||
case ProjectLanguage.CSharp:
|
||||
(<IDotnetFunctionWizardContext>wizardContext).namespace = defaultSettings.namespace;
|
||||
promptSteps.push(new DotnetFunctionNameStep(), new DotnetNamespaceStep());
|
||||
executeSteps.push(new DotnetFunctionCreateStep());
|
||||
break;
|
||||
case ProjectLanguage.TypeScript:
|
||||
promptSteps.push(new ScriptFunctionNameStep());
|
||||
executeSteps.push(new TypeScriptFunctionCreateStep());
|
||||
break;
|
||||
default:
|
||||
promptSteps.push(new ScriptFunctionNameStep());
|
||||
executeSteps.push(new ScriptFunctionCreateStep());
|
||||
break;
|
||||
}
|
||||
|
||||
for (const setting of wizardContext.template.userPromptedSettings) {
|
||||
if (defaultSettings[setting.name.toLowerCase()] !== undefined) {
|
||||
wizardContext[setting.name] = defaultSettings[setting.name.toLowerCase()];
|
||||
} else if (setting.resourceType !== undefined) {
|
||||
promptSteps.push(new LocalAppSettingListStep(setting));
|
||||
} else {
|
||||
switch (setting.valueType) {
|
||||
case ValueType.boolean:
|
||||
promptSteps.push(new BooleanPromptStep(setting));
|
||||
break;
|
||||
case ValueType.enum:
|
||||
promptSteps.push(new EnumPromptStep(setting));
|
||||
break;
|
||||
default:
|
||||
// Default to 'string' type for any valueType that isn't supported
|
||||
promptSteps.push(new StringPromptStep(setting));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const title: string = localize('createFunction', 'Create new {0}', wizardContext.template.name);
|
||||
return { promptSteps, executeSteps, title, showExecuteProgress: true };
|
||||
}
|
||||
|
||||
async function promptForTemplate(functionAppPath: string, language: ProjectLanguage, runtime: ProjectRuntime, templateFilter: TemplateFilter, telemetryProperties: TelemetryProperties): Promise<[IFunctionTemplate, ProjectLanguage, ProjectRuntime, TemplateFilter]> {
|
||||
const runtimePickId: string = 'runtime';
|
||||
const languagePickId: string = 'language';
|
||||
|
@ -216,7 +207,7 @@ async function promptForTemplate(functionAppPath: string, language: ProjectLangu
|
|||
]);
|
||||
});
|
||||
|
||||
const placeHolder: string = localize('azFunc.selectFuncTemplate', 'Select a function template');
|
||||
const placeHolder: string = localize('selectFuncTemplate', 'Select a function template');
|
||||
const result: IFunctionTemplate | string = (await ext.ui.showQuickPick(picksTask, { placeHolder })).data;
|
||||
if (isString(result)) {
|
||||
switch (result) {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import { Progress } from 'vscode';
|
||||
import { AzureWizardExecuteStep } from 'vscode-azureextensionui';
|
||||
import { localize } from "../../../localize";
|
||||
import { executeDotnetTemplateCommand } from '../../../templates/executeDotnetTemplateCommand';
|
||||
import { cpUtils } from '../../../utils/cpUtils';
|
||||
import { dotnetUtils } from '../../../utils/dotnetUtils';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IDotnetFunctionWizardContext } from './IDotnetFunctionWizardContext';
|
||||
|
||||
export class DotnetFunctionCreateStep extends AzureWizardExecuteStep<IDotnetFunctionWizardContext> {
|
||||
public async execute(wizardContext: IDotnetFunctionWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
|
||||
progress.report({ message: localize('creatingFunction', 'Creating {0}...', wizardContext.template.name) });
|
||||
await dotnetUtils.validateDotnetInstalled(wizardContext.actionContext);
|
||||
|
||||
const args: string[] = [];
|
||||
args.push('--arg:name');
|
||||
args.push(cpUtils.wrapArgInQuotes(nonNullProp(wizardContext, 'functionName')));
|
||||
|
||||
args.push('--arg:namespace');
|
||||
args.push(cpUtils.wrapArgInQuotes(nonNullProp(wizardContext, 'namespace')));
|
||||
|
||||
for (const setting of wizardContext.template.userPromptedSettings) {
|
||||
args.push(`--arg:${setting.name}`);
|
||||
// tslint:disable-next-line: strict-boolean-expressions no-unsafe-any
|
||||
args.push(cpUtils.wrapArgInQuotes(wizardContext[setting.name] || ''));
|
||||
}
|
||||
|
||||
await executeDotnetTemplateCommand(wizardContext.runtime, wizardContext.functionAppPath, 'create', '--identity', wizardContext.template.id, ...args);
|
||||
|
||||
wizardContext.newFilePath = path.join(wizardContext.functionAppPath, `${wizardContext.functionName}.cs`);
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IDotnetFunctionWizardContext): boolean {
|
||||
return !wizardContext.newFilePath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { functionNameInvalidMessage, functionNameRegex } from '../../../constants';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import * as fsUtil from '../../../utils/fs';
|
||||
import { IDotnetFunctionWizardContext } from './IDotnetFunctionWizardContext';
|
||||
|
||||
export class DotnetFunctionNameStep extends AzureWizardPromptStep<IDotnetFunctionWizardContext> {
|
||||
public async prompt(wizardContext: IDotnetFunctionWizardContext): Promise<void> {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(wizardContext.functionAppPath, wizardContext.template.defaultFunctionName, '.cs');
|
||||
wizardContext.functionName = await ext.ui.showInputBox({
|
||||
placeHolder: localize('funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(wizardContext, s),
|
||||
value: defaultFunctionName || wizardContext.template.defaultFunctionName
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IDotnetFunctionWizardContext): boolean {
|
||||
return !wizardContext.functionName;
|
||||
}
|
||||
|
||||
private validateTemplateName(wizardContext: IDotnetFunctionWizardContext, name: string | undefined): string | undefined {
|
||||
if (!name) {
|
||||
return localize('emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(path.join(wizardContext.functionAppPath, `${name}.cs`))) {
|
||||
return localize('existingFile', 'A file with the name "{0}" already exists.', name);
|
||||
} else if (!functionNameRegex.test(name)) {
|
||||
return functionNameInvalidMessage;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
// tslint:disable-next-line:no-require-imports
|
||||
import XRegExp = require('xregexp');
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import { IDotnetFunctionWizardContext } from './IDotnetFunctionWizardContext';
|
||||
|
||||
export class DotnetNamespaceStep extends AzureWizardPromptStep<IDotnetFunctionWizardContext> {
|
||||
public async prompt(wizardContext: IDotnetFunctionWizardContext): Promise<void> {
|
||||
wizardContext.namespace = await ext.ui.showInputBox({
|
||||
placeHolder: localize('namespacePlaceHolder', 'Namespace'),
|
||||
prompt: localize('namespacePrompt', 'Provide a namespace'),
|
||||
validateInput: validateCSharpNamespace,
|
||||
value: 'Company.Function'
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IDotnetFunctionWizardContext): boolean {
|
||||
return !wizardContext.namespace;
|
||||
}
|
||||
}
|
||||
|
||||
// Identifier specification: https://github.com/dotnet/csharplang/blob/master/spec/lexical-structure.md#identifiers
|
||||
const formattingCharacter: string = '\\p{Cf}';
|
||||
const connectingCharacter: string = '\\p{Pc}';
|
||||
const decimalDigitCharacter: string = '\\p{Nd}';
|
||||
const combiningCharacter: string = '\\p{Mn}|\\p{Mc}';
|
||||
const letterCharacter: string = '\\p{Lu}|\\p{Ll}|\\p{Lt}|\\p{Lm}|\\p{Lo}|\\p{Nl}';
|
||||
const identifierPartCharacter: string = `${letterCharacter}|${decimalDigitCharacter}|${connectingCharacter}|${combiningCharacter}|${formattingCharacter}`;
|
||||
const identifierStartCharacter: string = `(${letterCharacter}|_)`;
|
||||
const identifierOrKeyword: string = `${identifierStartCharacter}(${identifierPartCharacter})*`;
|
||||
const identifierRegex: RegExp = XRegExp(`^${identifierOrKeyword}$`);
|
||||
// Keywords: https://github.com/dotnet/csharplang/blob/master/spec/lexical-structure.md#keywords
|
||||
const keywords: string[] = ['abstract', 'as', 'base', 'bool', 'break', 'byte', 'case', 'catch', 'char', 'checked', 'class', 'const', 'continue', 'decimal', 'default', 'delegate', 'do', 'double', 'else', 'enum', 'event', 'explicit', 'extern', 'false', 'finally', 'fixed', 'float', 'for', 'foreach', 'goto', 'if', 'implicit', 'in', 'int', 'interface', 'internal', 'is', 'lock', 'long', 'namespace', 'new', 'null', 'object', 'operator', 'out', 'override', 'params', 'private', 'protected', 'public', 'readonly', 'ref', 'return', 'sbyte', 'sealed', 'short', 'sizeof', 'stackalloc', 'static', 'string', 'struct', 'switch', 'this', 'throw', 'true', 'try', 'typeof', 'uint', 'ulong', 'unchecked', 'unsafe', 'ushort', 'using', 'virtual', 'void', 'volatile', 'while'];
|
||||
|
||||
export function validateCSharpNamespace(value: string | undefined): string | undefined {
|
||||
if (!value) {
|
||||
return localize('cSharpEmptyTemplateNameError', 'The template name cannot be empty.');
|
||||
}
|
||||
|
||||
// Namespace specification: https://github.com/dotnet/csharplang/blob/master/spec/namespaces.md#namespace-declarations
|
||||
const identifiers: string[] = value.split('.');
|
||||
for (const identifier of identifiers) {
|
||||
if (identifier === '') {
|
||||
return localize('cSharpExtraPeriod', 'Leading or trailing "." character is not allowed.');
|
||||
} else if (!identifierRegex.test(identifier)) {
|
||||
return localize('cSharpInvalidCharacters', 'The identifier "{0}" contains invalid characters.', identifier);
|
||||
} else if (keywords.find((s: string) => s === identifier.toLowerCase()) !== undefined) {
|
||||
return localize('cSharpKeywordWarning', 'The identifier "{0}" is a reserved keyword.', identifier);
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export interface IDotnetFunctionWizardContext extends IFunctionWizardContext {
|
||||
namespace?: string;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { QuickPickItem } from "vscode";
|
||||
import { AzureWizardPromptStep } from "vscode-azureextensionui";
|
||||
import { ext } from "../../../extensionVariables";
|
||||
import { IFunctionSetting } from "../../../templates/IFunctionSetting";
|
||||
import { IFunctionWizardContext } from "../IFunctionWizardContext";
|
||||
|
||||
export class BooleanPromptStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<void> {
|
||||
const picks: QuickPickItem[] = [
|
||||
{ label: 'true', description: '' },
|
||||
{ label: 'false', description: '' }
|
||||
];
|
||||
wizardContext[this._setting.name] = (await ext.ui.showQuickPick(picks, { placeHolder: this._setting.label })).label;
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._setting.name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep, IAzureQuickPickItem } from "vscode-azureextensionui";
|
||||
import { ext } from "../../../extensionVariables";
|
||||
import { IFunctionSetting } from "../../../templates/IFunctionSetting";
|
||||
import { IFunctionWizardContext } from "../IFunctionWizardContext";
|
||||
|
||||
export class EnumPromptStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<void> {
|
||||
const picks: IAzureQuickPickItem<string>[] = this._setting.enums.map(e => { return { data: e.value, label: e.displayName }; });
|
||||
wizardContext[this._setting.name] = (await ext.ui.showQuickPick(picks, { placeHolder: this._setting.label })).data;
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._setting.name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Progress } from 'vscode';
|
||||
import { AzureWizardExecuteStep } from 'vscode-azureextensionui';
|
||||
import { localSettingsFileName } from '../../../constants';
|
||||
import { setLocalAppSetting } from '../../../LocalAppSettings';
|
||||
import { localize } from '../../../localize';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export class LocalAppSettingCreateStep extends AzureWizardExecuteStep<IFunctionWizardContext> {
|
||||
private readonly _nameKey: string;
|
||||
private readonly _valueKey: string;
|
||||
|
||||
constructor(nameKey: string, valueKey: string) {
|
||||
super();
|
||||
this._nameKey = nameKey;
|
||||
this._valueKey = valueKey;
|
||||
}
|
||||
|
||||
public async execute(wizardContext: IFunctionWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
|
||||
progress.report({ message: localize('updatingLocalSettings', 'Updating {0}...', localSettingsFileName) });
|
||||
// tslint:disable-next-line: no-unsafe-any no-any
|
||||
await setLocalAppSetting(wizardContext.functionAppPath, nonNullProp(wizardContext, <any>this._nameKey), nonNullProp(wizardContext, <any>this._valueKey));
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !!wizardContext[this._valueKey];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import { AzureWizardExecuteStep, AzureWizardPromptStep, IAzureQuickPickItem, ISubscriptionWizardContext, ISubWizardOptions, StorageAccountKind, StorageAccountListStep, StorageAccountPerformance, StorageAccountReplication } from 'vscode-azureextensionui';
|
||||
import { localSettingsFileName } from '../../../constants';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { getLocalAppSettings, ILocalAppSettings } from '../../../LocalAppSettings';
|
||||
import { localize } from '../../../localize';
|
||||
import { IFunctionSetting, ResourceType } from '../../../templates/IFunctionSetting';
|
||||
import { CosmosDBConnectionCreateStep } from '../azureSteps/CosmosDBConnectionCreateStep';
|
||||
import { CosmosDBListStep } from '../azureSteps/CosmosDBListStep';
|
||||
import { ServiceBusConnectionCreateStep } from '../azureSteps/ServiceBusConnectionCreateStep';
|
||||
import { ServiceBusListStep } from '../azureSteps/ServiceBusListStep';
|
||||
import { StorageConnectionCreateStep } from '../azureSteps/StorageConnectionCreateStep';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
import { LocalAppSettingCreateStep } from './LocalAppSettingCreateStep';
|
||||
import { LocalAppSettingNameStep } from './LocalAppSettingNameStep';
|
||||
import { LocalAppSettingValueStep } from './LocalAppSettingValueStep';
|
||||
|
||||
export class LocalAppSettingListStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
|
||||
constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<ISubWizardOptions<IFunctionWizardContext> | void> {
|
||||
const localSettingsPath: string = path.join(wizardContext.functionAppPath, localSettingsFileName);
|
||||
const settings: ILocalAppSettings = await getLocalAppSettings(localSettingsPath);
|
||||
if (settings.Values) {
|
||||
const existingSettings: string[] = Object.keys(settings.Values);
|
||||
if (existingSettings.length > 0) {
|
||||
let picks: IAzureQuickPickItem<string | undefined>[] = [{ label: localize('newAppSetting', '$(plus) Create new local app setting'), data: undefined }];
|
||||
picks = picks.concat(existingSettings.map((s: string) => { return { data: s, label: s }; }));
|
||||
const placeHolder: string = localize('selectAppSetting', 'Select setting from "{0}"', localSettingsFileName);
|
||||
const result: string | undefined = (await ext.ui.showQuickPick(picks, { placeHolder })).data;
|
||||
if (result) {
|
||||
wizardContext[this._setting.name] = result;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const azurePromptSteps: AzureWizardPromptStep<IFunctionWizardContext & ISubscriptionWizardContext>[] = [];
|
||||
const azureExecuteSteps: AzureWizardExecuteStep<IFunctionWizardContext & ISubscriptionWizardContext>[] = [];
|
||||
switch (this._setting.resourceType) {
|
||||
case ResourceType.DocumentDB:
|
||||
azurePromptSteps.push(new CosmosDBListStep());
|
||||
azureExecuteSteps.push(new CosmosDBConnectionCreateStep(this._setting));
|
||||
break;
|
||||
case ResourceType.Storage:
|
||||
azurePromptSteps.push(new StorageAccountListStep(
|
||||
{ kind: StorageAccountKind.Storage, performance: StorageAccountPerformance.Standard, replication: StorageAccountReplication.LRS },
|
||||
{ kind: [StorageAccountKind.BlobStorage], learnMoreLink: 'https://aka.ms/T5o0nf' }
|
||||
));
|
||||
azureExecuteSteps.push(new StorageConnectionCreateStep(this._setting));
|
||||
break;
|
||||
case ResourceType.ServiceBus:
|
||||
azurePromptSteps.push(new ServiceBusListStep());
|
||||
azureExecuteSteps.push(new ServiceBusConnectionCreateStep(this._setting));
|
||||
break;
|
||||
default:
|
||||
// Unsupported resource type - prompt user to enter connection string manually
|
||||
const valueKey: string = this._setting.name + '_value';
|
||||
return {
|
||||
promptSteps: [new LocalAppSettingNameStep(this._setting), new LocalAppSettingValueStep(valueKey)],
|
||||
executeSteps: [new LocalAppSettingCreateStep(this._setting.name, valueKey)]
|
||||
};
|
||||
}
|
||||
|
||||
const subscriptionPromptStep: AzureWizardPromptStep<ISubscriptionWizardContext> | undefined = await ext.tree.getSubscriptionPromptStep(wizardContext);
|
||||
if (subscriptionPromptStep) {
|
||||
azurePromptSteps.unshift(subscriptionPromptStep);
|
||||
}
|
||||
return { promptSteps: azurePromptSteps, executeSteps: azureExecuteSteps };
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._setting.name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from '../../../localize';
|
||||
import { IFunctionSetting } from '../../../templates/IFunctionSetting';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export class LocalAppSettingNameStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
|
||||
public constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<void> {
|
||||
const appSettingSuffix: string = `_${nonNullProp(this._setting, 'resourceType').toUpperCase()}`;
|
||||
wizardContext[this._setting.name] = await ext.ui.showInputBox({
|
||||
placeHolder: localize('appSettingKeyPlaceholder', 'Local app setting key'),
|
||||
prompt: localize('appSettingKeyPrompt', 'Provide a key for a connection string'),
|
||||
value: `example${appSettingSuffix}`
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._setting.name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from '../../../localize';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export class LocalAppSettingValueStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _key: string;
|
||||
|
||||
public constructor(key: string) {
|
||||
super();
|
||||
this._key = key;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<void> {
|
||||
wizardContext[this._key] = await ext.ui.showInputBox({
|
||||
placeHolder: localize('appSettingValuePlaceholder', 'App setting value'),
|
||||
prompt: localize('appSettingValuePrompt', 'Provide a connection string')
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._key];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep } from "vscode-azureextensionui";
|
||||
import { ext } from "../../../extensionVariables";
|
||||
import { localize } from "../../../localize";
|
||||
import { IFunctionSetting } from "../../../templates/IFunctionSetting";
|
||||
import { IFunctionWizardContext } from "../IFunctionWizardContext";
|
||||
|
||||
export class StringPromptStep extends AzureWizardPromptStep<IFunctionWizardContext> {
|
||||
private readonly _setting: IFunctionSetting;
|
||||
constructor(setting: IFunctionSetting) {
|
||||
super();
|
||||
this._setting = setting;
|
||||
}
|
||||
|
||||
public async prompt(wizardContext: IFunctionWizardContext): Promise<void> {
|
||||
wizardContext[this._setting.name] = await ext.ui.showInputBox({
|
||||
placeHolder: this._setting.label,
|
||||
prompt: this._setting.description || localize('stringSettingPrompt', 'Provide a \'{0}\'', this._setting.label),
|
||||
validateInput: (s: string): string | undefined => this._setting.validateSetting(s),
|
||||
value: this._setting.defaultValue
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IFunctionWizardContext): boolean {
|
||||
return !wizardContext[this._setting.name];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export interface IJavaFunctionWizardContext extends IFunctionWizardContext {
|
||||
packageName?: string;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as path from 'path';
|
||||
import { Progress } from 'vscode';
|
||||
import { AzureWizardExecuteStep } from 'vscode-azureextensionui';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import { removeLanguageFromId } from "../../../templates/TemplateProvider";
|
||||
import { parseJavaClassName } from "../../../utils/javaNameUtils";
|
||||
import { mavenUtils } from "../../../utils/mavenUtils";
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IJavaFunctionWizardContext } from './IJavaFunctionWizardContext';
|
||||
|
||||
export function getNewJavaFunctionFilePath(functionAppPath: string, packageName: string, functionName: string): string {
|
||||
return path.join(functionAppPath, 'src', 'main', 'java', ...packageName.split('.'), `${parseJavaClassName(functionName)}.java`);
|
||||
}
|
||||
|
||||
export class JavaFunctionCreateStep extends AzureWizardExecuteStep<IJavaFunctionWizardContext> {
|
||||
public async execute(wizardContext: IJavaFunctionWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
|
||||
await mavenUtils.validateMavenInstalled(wizardContext.actionContext, wizardContext.functionAppPath);
|
||||
progress.report({ message: localize('creatingFunction', 'Creating {0}...', wizardContext.template.name) });
|
||||
|
||||
const args: string[] = [];
|
||||
for (const setting of wizardContext.template.userPromptedSettings) {
|
||||
// tslint:disable-next-line: strict-boolean-expressions no-unsafe-any
|
||||
args.push(mavenUtils.formatMavenArg(`D${setting.name}`, wizardContext[setting.name] || ''));
|
||||
}
|
||||
|
||||
const packageName: string = nonNullProp(wizardContext, 'packageName');
|
||||
const functionName: string = nonNullProp(wizardContext, 'functionName');
|
||||
await mavenUtils.executeMvnCommand(
|
||||
wizardContext.actionContext.properties,
|
||||
ext.outputChannel,
|
||||
wizardContext.functionAppPath,
|
||||
'azure-functions:add',
|
||||
'-B',
|
||||
mavenUtils.formatMavenArg('Dfunctions.package', packageName),
|
||||
mavenUtils.formatMavenArg('Dfunctions.name', functionName),
|
||||
mavenUtils.formatMavenArg('Dfunctions.template', removeLanguageFromId(wizardContext.template.id)),
|
||||
...args
|
||||
);
|
||||
|
||||
wizardContext.newFilePath = getNewJavaFunctionFilePath(wizardContext.functionAppPath, packageName, functionName);
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IJavaFunctionWizardContext): boolean {
|
||||
return !wizardContext.newFilePath;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { functionNameInvalidMessage, functionNameRegex } from '../../../constants';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import { removeLanguageFromId } from "../../../templates/TemplateProvider";
|
||||
import * as fsUtil from '../../../utils/fs';
|
||||
import { getFullClassName } from "../../../utils/javaNameUtils";
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IJavaFunctionWizardContext } from './IJavaFunctionWizardContext';
|
||||
import { getNewJavaFunctionFilePath } from './JavaFunctionCreateStep';
|
||||
|
||||
export class JavaFunctionNameStep extends AzureWizardPromptStep<IJavaFunctionWizardContext> {
|
||||
public async prompt(wizardContext: IJavaFunctionWizardContext): Promise<void> {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueJavaFsPath(wizardContext.functionAppPath, nonNullProp(wizardContext, 'packageName'), `${removeLanguageFromId(wizardContext.template.id)}Java`);
|
||||
wizardContext.functionName = await ext.ui.showInputBox({
|
||||
placeHolder: localize('funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(wizardContext, s),
|
||||
value: defaultFunctionName || wizardContext.template.defaultFunctionName
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IJavaFunctionWizardContext): boolean {
|
||||
return !wizardContext.functionName;
|
||||
}
|
||||
|
||||
private validateTemplateName(wizardContext: IJavaFunctionWizardContext, name: string | undefined): string | undefined {
|
||||
const packageName: string = nonNullProp(wizardContext, 'packageName');
|
||||
if (!name) {
|
||||
return localize('emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(getNewJavaFunctionFilePath(wizardContext.functionAppPath, packageName, name))) {
|
||||
return localize('existingFolderError', 'The Java class "{0}" already exists.', getFullClassName(packageName, name));
|
||||
} else if (!functionNameRegex.test(name)) {
|
||||
return functionNameInvalidMessage;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import { validatePackageName } from "../../../utils/javaNameUtils";
|
||||
import { IJavaFunctionWizardContext } from './IJavaFunctionWizardContext';
|
||||
|
||||
export class JavaPackageNameStep extends AzureWizardPromptStep<IJavaFunctionWizardContext> {
|
||||
public async prompt(wizardContext: IJavaFunctionWizardContext): Promise<void> {
|
||||
wizardContext.packageName = await ext.ui.showInputBox({
|
||||
placeHolder: localize('packagePlaceHolder', 'Package'),
|
||||
prompt: localize('packagePrompt', 'Provide a package name'),
|
||||
validateInput: validatePackageName,
|
||||
value: 'com.function'
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IJavaFunctionWizardContext): boolean {
|
||||
return !wizardContext.packageName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IScriptFunctionTemplate } from '../../../templates/parseScriptTemplates';
|
||||
import { IFunctionWizardContext } from '../IFunctionWizardContext';
|
||||
|
||||
export interface IScriptFunctionWizardContext extends IFunctionWizardContext {
|
||||
template: IScriptFunctionTemplate;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { Progress } from 'vscode';
|
||||
import { AzureWizardExecuteStep } from 'vscode-azureextensionui';
|
||||
import { ProjectLanguage } from '../../../constants';
|
||||
import { IFunctionJson } from '../../../FunctionConfig';
|
||||
import { localize } from "../../../localize";
|
||||
import * as fsUtil from '../../../utils/fs';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IScriptFunctionWizardContext } from './IScriptFunctionWizardContext';
|
||||
|
||||
export function getScriptFileNameFromLanguage(language: string): string | undefined {
|
||||
switch (language) {
|
||||
case ProjectLanguage.Bash:
|
||||
return 'run.sh';
|
||||
case ProjectLanguage.Batch:
|
||||
return 'run.bat';
|
||||
case ProjectLanguage.CSharpScript:
|
||||
return 'run.csx';
|
||||
case ProjectLanguage.FSharpScript:
|
||||
return 'run.fsx';
|
||||
case ProjectLanguage.JavaScript:
|
||||
return 'index.js';
|
||||
case ProjectLanguage.PHP:
|
||||
return 'run.php';
|
||||
case ProjectLanguage.PowerShell:
|
||||
return 'run.ps1';
|
||||
case ProjectLanguage.Python:
|
||||
return '__init__.py';
|
||||
case ProjectLanguage.TypeScript:
|
||||
return 'index.ts';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export class ScriptFunctionCreateStep extends AzureWizardExecuteStep<IScriptFunctionWizardContext> {
|
||||
public async execute(wizardContext: IScriptFunctionWizardContext, progress: Progress<{ message?: string | undefined; increment?: number | undefined }>): Promise<void> {
|
||||
progress.report({ message: localize('creatingFunction', 'Creating {0}...', wizardContext.template.name) });
|
||||
|
||||
const functionPath: string = path.join(wizardContext.functionAppPath, nonNullProp(wizardContext, 'functionName'));
|
||||
await fse.ensureDir(functionPath);
|
||||
await Promise.all(Object.keys(wizardContext.template.templateFiles).map(async fileName => {
|
||||
await fse.writeFile(path.join(functionPath, fileName), wizardContext.template.templateFiles[fileName]);
|
||||
}));
|
||||
|
||||
for (const setting of wizardContext.template.userPromptedSettings) {
|
||||
// tslint:disable-next-line: strict-boolean-expressions no-unsafe-any
|
||||
wizardContext.template.functionConfig.inBinding[setting.name] = wizardContext[setting.name] || '';
|
||||
}
|
||||
|
||||
const functionJson: IFunctionJson = wizardContext.template.functionConfig.functionJson;
|
||||
if (this.editFunctionJson) {
|
||||
await this.editFunctionJson(wizardContext, functionJson);
|
||||
}
|
||||
|
||||
await fsUtil.writeFormattedJson(path.join(functionPath, 'function.json'), functionJson);
|
||||
|
||||
const scriptFileName: string | undefined = getScriptFileNameFromLanguage(wizardContext.language);
|
||||
if (scriptFileName) {
|
||||
wizardContext.newFilePath = path.join(functionPath, scriptFileName);
|
||||
}
|
||||
}
|
||||
|
||||
public shouldExecute(wizardContext: IScriptFunctionWizardContext): boolean {
|
||||
return !wizardContext.newFilePath;
|
||||
}
|
||||
|
||||
protected editFunctionJson?(wizardContext: IScriptFunctionWizardContext, functionJson: IFunctionJson): Promise<void>;
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { AzureWizardPromptStep } from 'vscode-azureextensionui';
|
||||
import { functionNameInvalidMessage, functionNameRegex } from '../../../constants';
|
||||
import { ext } from '../../../extensionVariables';
|
||||
import { localize } from "../../../localize";
|
||||
import * as fsUtil from '../../../utils/fs';
|
||||
import { IScriptFunctionWizardContext } from './IScriptFunctionWizardContext';
|
||||
|
||||
export class ScriptFunctionNameStep extends AzureWizardPromptStep<IScriptFunctionWizardContext> {
|
||||
public async prompt(wizardContext: IScriptFunctionWizardContext): Promise<void> {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(wizardContext.functionAppPath, wizardContext.template.defaultFunctionName);
|
||||
wizardContext.functionName = await ext.ui.showInputBox({
|
||||
placeHolder: localize('funcNamePlaceholder', 'Function name'),
|
||||
prompt: localize('funcNamePrompt', 'Provide a function name'),
|
||||
validateInput: (s: string): string | undefined => this.validateTemplateName(wizardContext, s),
|
||||
value: defaultFunctionName || wizardContext.template.defaultFunctionName
|
||||
});
|
||||
}
|
||||
|
||||
public shouldPrompt(wizardContext: IScriptFunctionWizardContext): boolean {
|
||||
return !wizardContext.functionName;
|
||||
}
|
||||
|
||||
private validateTemplateName(wizardContext: IScriptFunctionWizardContext, name: string | undefined): string | undefined {
|
||||
if (!name) {
|
||||
return localize('emptyTemplateNameError', 'The template name cannot be empty.');
|
||||
} else if (fse.existsSync(path.join(wizardContext.functionAppPath, name))) {
|
||||
return localize('existingFolderError', 'A folder with the name "{0}" already exists.', name);
|
||||
} else if (!functionNameRegex.test(name)) {
|
||||
return functionNameInvalidMessage;
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { tsConfigFileName, tsDefaultOutDir } from '../../../constants';
|
||||
import { IFunctionJson } from '../../../FunctionConfig';
|
||||
import { nonNullProp } from '../../../utils/nonNull';
|
||||
import { IScriptFunctionWizardContext } from './IScriptFunctionWizardContext';
|
||||
import { ScriptFunctionCreateStep } from './ScriptFunctionCreateStep';
|
||||
|
||||
export class TypeScriptFunctionCreateStep extends ScriptFunctionCreateStep {
|
||||
protected async editFunctionJson(wizardContext: IScriptFunctionWizardContext, functionJson: IFunctionJson): Promise<void> {
|
||||
let outDir: string = tsDefaultOutDir;
|
||||
try {
|
||||
const tsconfigPath: string = path.join(wizardContext.functionAppPath, tsConfigFileName);
|
||||
// tslint:disable-next-line:no-unsafe-any
|
||||
outDir = (await fse.readJSON(tsconfigPath)).compilerOptions.outDir;
|
||||
} catch {
|
||||
// ignore and use default outDir
|
||||
}
|
||||
|
||||
functionJson.scriptFile = path.join('..', outDir, nonNullProp(wizardContext, 'functionName'), 'index.js');
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@ import { extensionPrefix, extInstallCommand, extInstallTaskName, func, funcWatch
|
|||
import { pythonDebugConfig } from '../../debug/PythonDebugProvider';
|
||||
import { ext } from '../../extensionVariables';
|
||||
import { validateFuncCoreToolsInstalled } from '../../funcCoreTools/validateFuncCoreToolsInstalled';
|
||||
import { azureWebJobsStorageKey, getLocalSettings, ILocalAppSettings } from '../../LocalAppSettings';
|
||||
import { azureWebJobsStorageKey, getLocalAppSettings, ILocalAppSettings } from '../../LocalAppSettings';
|
||||
import { localize } from "../../localize";
|
||||
import { getGlobalFuncExtensionSetting } from '../../ProjectSettings';
|
||||
import { cpUtils } from "../../utils/cpUtils";
|
||||
|
@ -150,7 +150,7 @@ export class PythonProjectCreator extends ScriptProjectCreatorBase {
|
|||
// Make sure local settings isn't using Storage Emulator for non-windows
|
||||
// https://github.com/Microsoft/vscode-azurefunctions/issues/583
|
||||
const localSettingsPath: string = path.join(this.functionAppPath, localSettingsFileName);
|
||||
const localSettings: ILocalAppSettings = await getLocalSettings(localSettingsPath);
|
||||
const localSettings: ILocalAppSettings = await getLocalAppSettings(localSettingsPath);
|
||||
// tslint:disable-next-line:strict-boolean-expressions
|
||||
localSettings.Values = localSettings.Values || {};
|
||||
localSettings.Values[azureWebJobsStorageKey] = '';
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as fse from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
import { ProjectLanguage } from '../../constants';
|
||||
import { getScriptFileNameFromLanguage } from '../createFunction/ScriptFunctionCreator';
|
||||
import { getScriptFileNameFromLanguage } from '../createFunction/scriptSteps/ScriptFunctionCreateStep';
|
||||
import { tryGetCsprojFile, tryGetFsprojFile } from './DotnetProjectCreator';
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from "./localize";
|
||||
|
||||
export const isWindows: boolean = /^win/.test(process.platform);
|
||||
|
||||
export const extensionPrefix: string = 'azureFunctions';
|
||||
|
@ -91,3 +93,6 @@ export const localhost: string = '127.0.0.1';
|
|||
|
||||
export const tsDefaultOutDir: string = 'dist';
|
||||
export const tsConfigFileName: string = 'tsconfig.json';
|
||||
|
||||
export const functionNameRegex: RegExp = /^[a-zA-Z][a-zA-Z\d_\-]*$/;
|
||||
export const functionNameInvalidMessage: string = localize('functionNameInvalidMessage', 'Function name must start with a letter and can only contain letters, digits, "_" and "-"');
|
||||
|
|
|
@ -5,14 +5,8 @@
|
|||
|
||||
import { localize } from './localize';
|
||||
|
||||
// tslint:disable:max-classes-per-file
|
||||
// tslint:disable:max-classes-per-file export-name
|
||||
|
||||
export class NoWorkspaceError extends Error {
|
||||
public message: string = localize('azFunc.noWorkspaceError', 'You must have a workspace open to perform this operation.');
|
||||
public message: string = localize('noWorkspaceError', 'You must have a workspace open to perform this operation.');
|
||||
}
|
||||
|
||||
export class NoSubscriptionError extends Error {
|
||||
public message: string = localize('azFunc.noSubscriptionError', 'You must be signed in to Azure to perform this operation.');
|
||||
}
|
||||
|
||||
export class SkipForNowError extends Error { }
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from '../localize';
|
||||
|
||||
export enum ValueType {
|
||||
string = 'string',
|
||||
boolean = 'boolean',
|
||||
|
@ -20,21 +18,6 @@ export enum ResourceType {
|
|||
ServiceBus = 'ServiceBus'
|
||||
}
|
||||
|
||||
export function getResourceTypeLabel(resourceType: ResourceType): string {
|
||||
switch (resourceType) {
|
||||
case ResourceType.DocumentDB:
|
||||
return localize('azFunc.DocumentDB', 'Cosmos DB Account');
|
||||
case ResourceType.Storage:
|
||||
return localize('azFunc.Storage', 'Storage Account');
|
||||
case ResourceType.EventHub:
|
||||
return localize('azFunc.EventHub', 'Event Hub');
|
||||
case ResourceType.ServiceBus:
|
||||
return localize('azFunc.ServiceBus', 'Service Bus');
|
||||
default:
|
||||
return resourceType;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IEnumValue {
|
||||
value: string;
|
||||
displayName: string;
|
||||
|
|
|
@ -3,17 +3,12 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CosmosDBManagementClient, CosmosDBManagementModels } from 'azure-arm-cosmosdb';
|
||||
import { ServiceBusManagementClient, ServiceBusManagementModels } from 'azure-arm-sb';
|
||||
import { StorageManagementClient, StorageManagementModels } from 'azure-arm-storage';
|
||||
import { BaseResource } from 'ms-rest-azure';
|
||||
import { isArray } from 'util';
|
||||
import { QuickPickOptions } from 'vscode';
|
||||
import { AzureTreeItem, AzureWizard, createAzureClient, IActionContext, IAzureQuickPickItem, IAzureUserInput, IStorageAccountFilters, IStorageAccountWizardContext, StorageAccountKind, StorageAccountListStep, StorageAccountPerformance, StorageAccountReplication, SubscriptionTreeItem } from 'vscode-azureextensionui';
|
||||
import { SkipForNowError } from '../errors';
|
||||
import { AzureTreeItem, AzureWizard, createAzureClient, IActionContext, IAzureQuickPickItem, IStorageAccountFilters, IStorageAccountWizardContext, StorageAccountKind, StorageAccountListStep, StorageAccountPerformance, StorageAccountReplication, SubscriptionTreeItem } from 'vscode-azureextensionui';
|
||||
import { ext } from '../extensionVariables';
|
||||
import { localize } from '../localize';
|
||||
import { getResourceTypeLabel, ResourceType } from '../templates/IFunctionSetting';
|
||||
import { nonNullProp, nonNullValue } from './nonNull';
|
||||
|
||||
function parseResourceId(id: string): RegExpMatchArray {
|
||||
|
@ -42,7 +37,7 @@ interface IBaseResourceWithName extends BaseResource {
|
|||
name?: string;
|
||||
}
|
||||
|
||||
async function promptForResource<T extends IBaseResourceWithName>(ui: IAzureUserInput, resourceType: string, resourcesTask: Promise<T[]>): Promise<T> {
|
||||
export async function promptForResource<T extends IBaseResourceWithName>(placeHolder: string, resourcesTask: Promise<T[]>): Promise<T | undefined> {
|
||||
const picksTask: Promise<IAzureQuickPickItem<T | undefined>[]> = resourcesTask.then((resources: T[]) => {
|
||||
const picks: IAzureQuickPickItem<T | undefined>[] = !isArray(resources) ? [] : <IAzureQuickPickItem<T>[]>(resources
|
||||
.map((r: T) => r.name ? { data: r, label: r.name } : undefined)
|
||||
|
@ -55,14 +50,7 @@ async function promptForResource<T extends IBaseResourceWithName>(ui: IAzureUser
|
|||
return picks;
|
||||
});
|
||||
|
||||
const options: QuickPickOptions = { placeHolder: localize('azFunc.resourcePrompt', 'Select a \'{0}\'', resourceType) };
|
||||
|
||||
const result: T | undefined = (await ui.showQuickPick(picksTask, options)).data;
|
||||
if (!result) {
|
||||
throw new SkipForNowError();
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return (await ext.ui.showQuickPick(picksTask, { placeHolder })).data;
|
||||
}
|
||||
|
||||
export interface IResourceResult {
|
||||
|
@ -70,23 +58,6 @@ export interface IResourceResult {
|
|||
connectionString: string;
|
||||
}
|
||||
|
||||
export async function promptForCosmosDBAccount(): Promise<IResourceResult> {
|
||||
const resourceTypeLabel: string = getResourceTypeLabel(ResourceType.DocumentDB);
|
||||
const node: AzureTreeItem = await ext.tree.showTreeItemPicker(SubscriptionTreeItem.contextValue);
|
||||
|
||||
const client: CosmosDBManagementClient = createAzureClient(node.root, CosmosDBManagementClient);
|
||||
const dbAccount: CosmosDBManagementModels.DatabaseAccount = await promptForResource<CosmosDBManagementModels.DatabaseAccount>(ext.ui, resourceTypeLabel, client.databaseAccounts.list());
|
||||
const name: string = nonNullProp(dbAccount, 'name');
|
||||
|
||||
const resourceGroup: string = getResourceGroupFromId(nonNullProp(dbAccount, 'id'));
|
||||
const csListResult: CosmosDBManagementModels.DatabaseAccountListConnectionStringsResult = await client.databaseAccounts.listConnectionStrings(resourceGroup, name);
|
||||
const cs: CosmosDBManagementModels.DatabaseAccountConnectionString = nonNullValue(nonNullProp(csListResult, 'connectionStrings')[0], 'connectionString[0]');
|
||||
return {
|
||||
name: name,
|
||||
connectionString: nonNullProp(cs, 'connectionString')
|
||||
};
|
||||
}
|
||||
|
||||
export async function promptForStorageAccount(actionContext: IActionContext, filterOptions: IStorageAccountFilters): Promise<IResourceResult> {
|
||||
const node: AzureTreeItem = await ext.tree.showTreeItemPicker(SubscriptionTreeItem.contextValue);
|
||||
|
||||
|
@ -118,25 +89,3 @@ export async function promptForStorageAccount(actionContext: IActionContext, fil
|
|||
connectionString: `DefaultEndpointsProtocol=https;AccountName=${name};AccountKey=${key};EndpointSuffix=${endpointSuffix}`
|
||||
};
|
||||
}
|
||||
|
||||
export async function promptForServiceBus(): Promise<IResourceResult> {
|
||||
const resourceTypeLabel: string = getResourceTypeLabel(ResourceType.ServiceBus);
|
||||
const node: AzureTreeItem = await ext.tree.showTreeItemPicker(SubscriptionTreeItem.contextValue);
|
||||
|
||||
const client: ServiceBusManagementClient = createAzureClient(node.root, ServiceBusManagementClient);
|
||||
const resource: ServiceBusManagementModels.SBNamespace = await promptForResource<ServiceBusManagementModels.SBNamespace>(ext.ui, resourceTypeLabel, client.namespaces.list());
|
||||
const id: string = nonNullProp(resource, 'id');
|
||||
const name: string = nonNullProp(resource, 'name');
|
||||
|
||||
const resourceGroup: string = getResourceGroupFromId(id);
|
||||
const authRules: ServiceBusManagementModels.SBAuthorizationRule[] = await client.namespaces.listAuthorizationRules(resourceGroup, name);
|
||||
const authRule: ServiceBusManagementModels.SBAuthorizationRule | undefined = authRules.find((ar: ServiceBusManagementModels.SBAuthorizationRule) => ar.rights.some((r: string) => r.toLowerCase() === 'listen'));
|
||||
if (!authRule) {
|
||||
throw new Error(localize('noAuthRule', 'Failed to get connection string for Service Bus namespace "{0}".', name));
|
||||
}
|
||||
const keys: ServiceBusManagementModels.AccessKeys = await client.namespaces.listKeys(resourceGroup, name, nonNullProp(authRule, 'name'));
|
||||
return {
|
||||
name: name,
|
||||
connectionString: nonNullProp(keys, 'primaryConnectionString')
|
||||
};
|
||||
}
|
||||
|
|
|
@ -47,16 +47,12 @@ export abstract class FunctionTesterBase {
|
|||
const funcName: string = templateName.replace(/ /g, '');
|
||||
inputs.unshift(funcName); // Specify the function name
|
||||
inputs.unshift(templateName); // Select the function template
|
||||
inputs.unshift(testFolder); // Select the test func app folder
|
||||
if (vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0) {
|
||||
inputs.unshift('$(file-directory) Browse...'); // If the test environment has an open workspace, select the 'Browse...' option
|
||||
}
|
||||
|
||||
ext.ui = new TestUserInput(inputs);
|
||||
await runWithFuncSetting(templateFilterSetting, TemplateFilter.All, async () => {
|
||||
await runWithFuncSetting(projectLanguageSetting, this.language, async () => {
|
||||
await runWithFuncSetting(projectRuntimeSetting, this.runtime, async () => {
|
||||
await vscode.commands.executeCommand('azureFunctions.createFunction');
|
||||
await vscode.commands.executeCommand('azureFunctions.createFunction', testFolder);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче