Allow programatic use of deploy, createFunction, and createNewProject
This commit is contained in:
Родитель
32b2fb8317
Коммит
4af25798c3
|
@ -0,0 +1,57 @@
|
|||
# Azure Functions API
|
||||
|
||||
The following extension commands are supported for programatic use. If a parameter is not specified, the user will be prompted for the value. You must list 'ms-azuretools.vscode-azurefunctions' under the 'extensionDependencies' section of your package.json to ensure these apis are available to your extension.
|
||||
> NOTE: The functions extension is still in preview and the apis are subject to change.
|
||||
|
||||
Commands:
|
||||
* [Create New Project](#create-new-project)
|
||||
* [Create Function](#create-function)
|
||||
* [Deploy](#deploy)
|
||||
|
||||
## Create New Project
|
||||
|
||||
### Parameters
|
||||
|
||||
|Name|Type|Description|
|
||||
|---|---|---|
|
||||
|projectPath|string|Absolute file path that will contain your new project. If the path doesn't exist, it will be created.|
|
||||
|language|string|The currently supported languages are 'JavaScript' and 'Java'.|
|
||||
|openFolder|boolean|(Defaulted to true) Represents whether or not to open the project folder after it has been created. If true, the extension host may be restarted when the folder is opened.|
|
||||
|
||||
### Example Usage
|
||||
|
||||
```typescript
|
||||
await vscode.commands.executeCommand('azureFunctions.createNewProject', projectPath, 'JavaScript', false /* openFolder */);
|
||||
```
|
||||
|
||||
## Create Function
|
||||
|
||||
### Parameters
|
||||
|
||||
|Name|Type|Description|
|
||||
|---|---|---|
|
||||
|projectPath|string|Absolute file path that contains your project.|
|
||||
|templateId|string|The id of the template you want to create.|
|
||||
|functionName|string|The name of the function to be created.|
|
||||
|functionSettings|...string[]|Any settings unique to a template. For example, the HttpTrigger template requires an AuthorizationLevel parameter.|
|
||||
|
||||
### Example Usage
|
||||
|
||||
```typescript
|
||||
await vscode.commands.executeCommand('azureFunctions.createFunction', projectPath, 'HttpTrigger-JavaScript', 'HttpTrigger1', 'Anonymous');
|
||||
```
|
||||
|
||||
## Deploy
|
||||
|
||||
### Parameters
|
||||
|
||||
|Name|Type|Description|
|
||||
|---|---|---|
|
||||
|projectPath|string|Absolute file path that contains your project.|
|
||||
|functionAppId|string|The id of the function app you are deploying to.|
|
||||
|
||||
### Example Usage
|
||||
|
||||
```typescript
|
||||
await vscode.commands.executeCommand('azureFunctions.deploy', projectPath, '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/exampleGroup/providers/Microsoft.Web/sites/exampleSite');
|
||||
```
|
|
@ -6,7 +6,7 @@
|
|||
import { IUserInterface } from "../../IUserInterface";
|
||||
import { Template } from "../../templates/Template";
|
||||
|
||||
export abstract class AbstractFunctionCreator {
|
||||
export abstract class FunctionCreatorBase {
|
||||
protected readonly _functionNameRegex: RegExp = /^[a-zA-Z][a-zA-Z\d_\-]*$/;
|
||||
protected _functionAppPath: string;
|
||||
protected _template: Template;
|
||||
|
@ -20,6 +20,6 @@ export abstract class AbstractFunctionCreator {
|
|||
* 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: IUserInterface): Promise<void>;
|
||||
public abstract async promptForSettings(ui: IUserInterface, functionName: string | undefined): Promise<void>;
|
||||
public abstract async createFunction(userSettings: { [propertyName: string]: string }): Promise<string | undefined>;
|
||||
}
|
|
@ -14,13 +14,13 @@ import { cpUtils } from "../../utils/cpUtils";
|
|||
import * as fsUtil from '../../utils/fs';
|
||||
import { getFullClassName, parseJavaClassName, validatePackageName } from "../../utils/javaNameUtils";
|
||||
import { mavenUtils } from "../../utils/mavenUtils";
|
||||
import { AbstractFunctionCreator } from './AbstractFunctionCreator';
|
||||
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 AbstractFunctionCreator {
|
||||
export class JavaFunctionCreator extends FunctionCreatorBase {
|
||||
private _outputChannel: OutputChannel;
|
||||
private _packageName: string;
|
||||
private _functionName: string;
|
||||
|
@ -30,15 +30,19 @@ export class JavaFunctionCreator extends AbstractFunctionCreator {
|
|||
this._outputChannel = outputChannel;
|
||||
}
|
||||
|
||||
public async promptForSettings(ui: IUserInterface): Promise<void> {
|
||||
public async promptForSettings(ui: IUserInterface, functionName: string | undefined): Promise<void> {
|
||||
const packagePlaceHolder: string = localize('azFunc.java.packagePlaceHolder', 'Package');
|
||||
const packagePrompt: string = localize('azFunc.java.packagePrompt', 'Provide a package name');
|
||||
this._packageName = await ui.showInputBox(packagePlaceHolder, packagePrompt, false, validatePackageName, 'com.function');
|
||||
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueJavaFsPath(this._functionAppPath, this._packageName, `${convertTemplateIdToJava(this._template.id)}Java`);
|
||||
const placeHolder: string = localize('azFunc.funcNamePlaceholder', 'Function name');
|
||||
const prompt: string = localize('azFunc.funcNamePrompt', 'Provide a function name');
|
||||
this._functionName = await ui.showInputBox(placeHolder, prompt, false, (s: string) => this.validateTemplateName(s), defaultFunctionName || this._template.defaultFunctionName);
|
||||
if (!functionName) {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueJavaFsPath(this._functionAppPath, this._packageName, `${convertTemplateIdToJava(this._template.id)}Java`);
|
||||
const placeHolder: string = localize('azFunc.funcNamePlaceholder', 'Function name');
|
||||
const prompt: string = localize('azFunc.funcNamePrompt', 'Provide a function name');
|
||||
this._functionName = await ui.showInputBox(placeHolder, prompt, false, (s: string) => this.validateTemplateName(s), defaultFunctionName || this._template.defaultFunctionName);
|
||||
} else {
|
||||
this._functionName = functionName;
|
||||
}
|
||||
}
|
||||
|
||||
public async createFunction(userSettings: { [propertyName: string]: string }): Promise<string | undefined> {
|
||||
|
|
|
@ -10,7 +10,7 @@ import { localize } from "../../localize";
|
|||
import { ProjectLanguage } from '../../ProjectSettings';
|
||||
import { Template } from "../../templates/Template";
|
||||
import * as fsUtil from '../../utils/fs';
|
||||
import { AbstractFunctionCreator } from './AbstractFunctionCreator';
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
|
||||
function getFileNameFromLanguage(language: string): string | undefined {
|
||||
switch (language) {
|
||||
|
@ -38,7 +38,7 @@ function getFileNameFromLanguage(language: string): string | undefined {
|
|||
/**
|
||||
* Function creator for multiple languages that don't require compilation (JavaScript, C# Script, Bash, etc.)
|
||||
*/
|
||||
export class ScriptFunctionCreator extends AbstractFunctionCreator {
|
||||
export class ScriptFunctionCreator extends FunctionCreatorBase {
|
||||
private _language: string;
|
||||
private _functionName: string;
|
||||
|
||||
|
@ -47,11 +47,15 @@ export class ScriptFunctionCreator extends AbstractFunctionCreator {
|
|||
this._language = language;
|
||||
}
|
||||
|
||||
public async promptForSettings(ui: IUserInterface): Promise<void> {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(this._functionAppPath, this._template.defaultFunctionName);
|
||||
const prompt: string = localize('azFunc.funcNamePrompt', 'Provide a function name');
|
||||
const placeHolder: string = localize('azFunc.funcNamePlaceholder', 'Function name');
|
||||
this._functionName = await ui.showInputBox(placeHolder, prompt, false, (s: string) => this.validateTemplateName(s), defaultFunctionName || this._template.defaultFunctionName);
|
||||
public async promptForSettings(ui: IUserInterface, functionName: string | undefined): Promise<void> {
|
||||
if (!functionName) {
|
||||
const defaultFunctionName: string | undefined = await fsUtil.getUniqueFsPath(this._functionAppPath, this._template.defaultFunctionName);
|
||||
const prompt: string = localize('azFunc.funcNamePrompt', 'Provide a function name');
|
||||
const placeHolder: string = localize('azFunc.funcNamePlaceholder', 'Function name');
|
||||
this._functionName = await ui.showInputBox(placeHolder, prompt, false, (s: string) => this.validateTemplateName(s), defaultFunctionName || this._template.defaultFunctionName);
|
||||
} else {
|
||||
this._functionName = functionName;
|
||||
}
|
||||
}
|
||||
|
||||
public async createFunction(userSettings: { [propertyName: string]: string; }): Promise<string | undefined> {
|
||||
|
|
|
@ -20,7 +20,7 @@ import { TemplateData } from '../../templates/TemplateData';
|
|||
import * as workspaceUtil from '../../utils/workspace';
|
||||
import { VSCodeUI } from '../../VSCodeUI';
|
||||
import { createNewProject } from '../createNewProject/createNewProject';
|
||||
import { AbstractFunctionCreator } from './AbstractFunctionCreator';
|
||||
import { FunctionCreatorBase } from './FunctionCreatorBase';
|
||||
import { JavaFunctionCreator } from './JavaFunctionCreator';
|
||||
import { ScriptFunctionCreator } from './ScriptFunctionCreator';
|
||||
|
||||
|
@ -35,7 +35,7 @@ async function validateIsFunctionApp(telemetryProperties: TelemetryProperties, o
|
|||
const message: string = localize('azFunc.notFunctionApp', 'The selected folder is not a function app project. Initialize Project?');
|
||||
const result: vscode.MessageItem | undefined = await vscode.window.showWarningMessage(message, DialogResponses.yes, DialogResponses.skipForNow, DialogResponses.cancel);
|
||||
if (result === DialogResponses.yes) {
|
||||
await createNewProject(telemetryProperties, outputChannel, functionAppPath, false, ui);
|
||||
await createNewProject(telemetryProperties, outputChannel, functionAppPath, undefined, false, ui);
|
||||
} else if (result !== DialogResponses.skipForNow) {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
|
@ -82,10 +82,17 @@ export async function createFunction(
|
|||
outputChannel: vscode.OutputChannel,
|
||||
azureAccount: AzureAccount,
|
||||
templateData: TemplateData,
|
||||
ui: IUserInterface = new VSCodeUI()): Promise<void> {
|
||||
ui: IUserInterface = new VSCodeUI(),
|
||||
functionAppPath?: string,
|
||||
templateId?: string,
|
||||
functionName?: string,
|
||||
...functionSettings: (string)[]): Promise<void> {
|
||||
|
||||
if (functionAppPath === undefined) {
|
||||
const folderPlaceholder: string = localize('azFunc.selectFunctionAppFolderExisting', 'Select the folder containing your function app');
|
||||
functionAppPath = await workspaceUtil.selectWorkspaceFolder(ui, folderPlaceholder);
|
||||
}
|
||||
|
||||
const folderPlaceholder: string = localize('azFunc.selectFunctionAppFolderExisting', 'Select the folder containing your function app');
|
||||
const functionAppPath: string = await workspaceUtil.selectWorkspaceFolder(ui, folderPlaceholder);
|
||||
await validateIsFunctionApp(telemetryProperties, outputChannel, functionAppPath, ui);
|
||||
|
||||
const localAppSettings: LocalAppSettings = new LocalAppSettings(ui, azureAccount, functionAppPath);
|
||||
|
@ -97,16 +104,29 @@ export async function createFunction(
|
|||
const templateFilter: TemplateFilter = await getTemplateFilter();
|
||||
telemetryProperties.templateFilter = templateFilter;
|
||||
|
||||
const templatePicks: PickWithData<Template>[] = (await templateData.getTemplates(language, runtime, templateFilter, ui)).map((t: Template) => new PickWithData<Template>(t, t.name));
|
||||
const templatePlaceHolder: string = localize('azFunc.selectFuncTemplate', 'Select a function template');
|
||||
const template: Template = (await ui.showQuickPick<Template>(templatePicks, templatePlaceHolder)).data;
|
||||
let template: Template;
|
||||
if (!templateId) {
|
||||
const templates: Template[] = await templateData.getTemplates(language, runtime, templateFilter, ui);
|
||||
const templatePicks: PickWithData<Template>[] = templates.map((t: Template) => new PickWithData<Template>(t, t.name));
|
||||
const templatePlaceHolder: string = localize('azFunc.selectFuncTemplate', 'Select a function template');
|
||||
template = (await ui.showQuickPick<Template>(templatePicks, templatePlaceHolder)).data;
|
||||
} else {
|
||||
const templates: Template[] = await templateData.getTemplates(language, runtime, TemplateFilter.All, ui);
|
||||
const foundTemplate: Template | undefined = templates.find((t: Template) => t.id === templateId);
|
||||
if (foundTemplate) {
|
||||
template = foundTemplate;
|
||||
} else {
|
||||
throw new Error(localize('templateNotFound', 'Could not find template with language "{0}", runtime "{1}", and id "{2}".', language, runtime, templateId));
|
||||
}
|
||||
}
|
||||
|
||||
telemetryProperties.templateId = template.id;
|
||||
|
||||
if (!template.functionConfig.isHttpTrigger) {
|
||||
await localAppSettings.validateAzureWebJobsStorage();
|
||||
}
|
||||
|
||||
let functionCreator: AbstractFunctionCreator;
|
||||
let functionCreator: FunctionCreatorBase;
|
||||
switch (language) {
|
||||
case ProjectLanguage.Java:
|
||||
functionCreator = new JavaFunctionCreator(functionAppPath, template, outputChannel);
|
||||
|
@ -116,14 +136,20 @@ export async function createFunction(
|
|||
break;
|
||||
}
|
||||
|
||||
await functionCreator.promptForSettings(ui);
|
||||
await functionCreator.promptForSettings(ui, functionName);
|
||||
|
||||
const userSettings: { [propertyName: string]: string } = {};
|
||||
for (const settingName of template.userPromptedSettings) {
|
||||
const setting: ConfigSetting | undefined = await templateData.getSetting(runtime, template.functionConfig.inBindingType, settingName);
|
||||
if (setting) {
|
||||
const defaultValue: string | undefined = template.functionConfig.inBinding[settingName];
|
||||
const settingValue: string | undefined = await promptForSetting(ui, localAppSettings, setting, defaultValue);
|
||||
let settingValue: string | undefined;
|
||||
if (functionSettings.length > 0) {
|
||||
settingValue = functionSettings.shift();
|
||||
} else {
|
||||
const defaultValue: string | undefined = template.functionConfig.inBinding[settingName];
|
||||
settingValue = await promptForSetting(ui, localAppSettings, setting, defaultValue);
|
||||
}
|
||||
|
||||
userSettings[settingName] = settingValue ? settingValue : '';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,17 +39,21 @@ const funcProblemMatcher: {} = {
|
|||
}
|
||||
};
|
||||
|
||||
export async function createNewProject(telemetryProperties: TelemetryProperties, outputChannel: OutputChannel, functionAppPath?: string, openFolder: boolean = true, ui: IUserInterface = new VSCodeUI()): Promise<void> {
|
||||
export async function createNewProject(telemetryProperties: TelemetryProperties, outputChannel: OutputChannel, functionAppPath?: string, language?: string, openFolder: boolean = true, ui: IUserInterface = new VSCodeUI()): Promise<void> {
|
||||
if (functionAppPath === undefined) {
|
||||
functionAppPath = await workspaceUtil.selectWorkspaceFolder(ui, localize('azFunc.selectFunctionAppFolderNew', 'Select the folder that will contain your function app'));
|
||||
}
|
||||
await fse.ensureDir(functionAppPath);
|
||||
|
||||
// Only display 'supported' languages that can be debugged in VS Code
|
||||
const languagePicks: Pick[] = [
|
||||
new Pick(ProjectLanguage.JavaScript),
|
||||
new Pick(ProjectLanguage.Java)
|
||||
];
|
||||
const language: string = (await ui.showQuickPick(languagePicks, localize('azFunc.selectFuncTemplate', 'Select a language for your function project'))).label;
|
||||
|
||||
if (!language) {
|
||||
language = (await ui.showQuickPick(languagePicks, localize('azFunc.selectFuncTemplate', 'Select a language for your function project'))).label;
|
||||
}
|
||||
telemetryProperties.projectLanguage = language;
|
||||
|
||||
let projectCreator: IProjectCreator;
|
||||
|
|
|
@ -22,32 +22,51 @@ import { convertStringToRuntime, extensionPrefix, getProjectLanguage, getProject
|
|||
import { FunctionAppTreeItem } from '../tree/FunctionAppTreeItem';
|
||||
import { FunctionsTreeItem } from '../tree/FunctionsTreeItem';
|
||||
import { FunctionTreeItem } from '../tree/FunctionTreeItem';
|
||||
import * as azUtils from '../utils/azure';
|
||||
import { cpUtils } from '../utils/cpUtils';
|
||||
import { mavenUtils } from '../utils/mavenUtils';
|
||||
import { nodeUtils } from '../utils/nodeUtils';
|
||||
import * as workspaceUtil from '../utils/workspace';
|
||||
import { VSCodeUI } from '../VSCodeUI';
|
||||
|
||||
export async function deploy(telemetryProperties: TelemetryProperties, tree: AzureTreeDataProvider, outputChannel: vscode.OutputChannel, context?: IAzureParentNode<FunctionAppTreeItem> | vscode.Uri, ui: IUserInterface = new VSCodeUI()): Promise<void> {
|
||||
const uri: vscode.Uri | undefined = context && context instanceof vscode.Uri ? context : undefined;
|
||||
let node: IAzureParentNode<FunctionAppTreeItem> | undefined = context && !(context instanceof vscode.Uri) ? context : undefined;
|
||||
export async function deploy(telemetryProperties: TelemetryProperties, tree: AzureTreeDataProvider, outputChannel: vscode.OutputChannel, deployPath?: vscode.Uri | string, functionAppId?: string | {}, ui: IUserInterface = new VSCodeUI()): Promise<void> {
|
||||
let deployFsPath: string;
|
||||
if (!deployPath) {
|
||||
deployFsPath = await workspaceUtil.selectWorkspaceFolder(ui, localize('azFunc.selectZipDeployFolder', 'Select the folder to zip and deploy'));
|
||||
} else if (deployPath instanceof vscode.Uri) {
|
||||
deployFsPath = deployPath.fsPath;
|
||||
} else {
|
||||
deployFsPath = deployPath;
|
||||
}
|
||||
|
||||
let folderPath: string = uri ? uri.fsPath : await workspaceUtil.selectWorkspaceFolder(ui, localize('azFunc.selectZipDeployFolder', 'Select the folder to zip and deploy'));
|
||||
|
||||
if (!node) {
|
||||
let node: IAzureParentNode<FunctionAppTreeItem>;
|
||||
if (!functionAppId || typeof (functionAppId) !== 'string') {
|
||||
node = <IAzureParentNode<FunctionAppTreeItem>>await tree.showNodePicker(FunctionAppTreeItem.contextValue);
|
||||
} else {
|
||||
const subscriptionId: string = azUtils.getSubscriptionFromId(functionAppId);
|
||||
const subscriptionNode: IAzureParentNode | undefined = <IAzureParentNode | undefined>(await tree.getChildren()).find((n: IAzureNode) => n.subscription.subscriptionId === subscriptionId);
|
||||
if (subscriptionNode) {
|
||||
const functionAppNode: IAzureNode | undefined = (await subscriptionNode.getCachedChildren()).find((n: IAzureNode) => n.treeItem.id === functionAppId);
|
||||
if (functionAppNode) {
|
||||
node = <IAzureParentNode<FunctionAppTreeItem>>functionAppNode;
|
||||
} else {
|
||||
throw new Error(localize('noMatchingFunctionApp', 'Failed to find a function app matching id "{0}".', functionAppId));
|
||||
}
|
||||
} else {
|
||||
throw new Error(localize('noMatchingSubscription', 'Failed to find a subscription matching id "{0}".', subscriptionId));
|
||||
}
|
||||
}
|
||||
|
||||
const client: WebSiteManagementClient = nodeUtils.getWebSiteClient(node);
|
||||
const siteWrapper: SiteWrapper = node.treeItem.siteWrapper;
|
||||
|
||||
const language: ProjectLanguage = await getProjectLanguage(folderPath, ui);
|
||||
const language: ProjectLanguage = await getProjectLanguage(deployFsPath, ui);
|
||||
telemetryProperties.projectLanguage = language;
|
||||
const runtime: ProjectRuntime = await getProjectRuntime(language, ui);
|
||||
telemetryProperties.projectRuntime = runtime;
|
||||
|
||||
if (language === ProjectLanguage.Java) {
|
||||
folderPath = await getJavaFolderPath(outputChannel, folderPath, ui);
|
||||
deployFsPath = await getJavaFolderPath(outputChannel, deployFsPath, ui);
|
||||
}
|
||||
|
||||
await verifyRuntimeIsCompatible(runtime, outputChannel, client, siteWrapper);
|
||||
|
@ -63,7 +82,7 @@ export async function deploy(telemetryProperties: TelemetryProperties, tree: Azu
|
|||
outputChannel.appendLine(localize('stopFunctionApp', 'Stopping Function App: {0} ...', siteWrapper.appName));
|
||||
await siteWrapper.stop(client);
|
||||
}
|
||||
await siteWrapper.deploy(folderPath, client, outputChannel, extensionPrefix);
|
||||
await siteWrapper.deploy(deployFsPath, client, outputChannel, extensionPrefix);
|
||||
} finally {
|
||||
if (language === ProjectLanguage.Java) {
|
||||
outputChannel.appendLine(localize('startFunctionApp', 'Starting Function App: {0} ...', siteWrapper.appName));
|
||||
|
|
|
@ -28,6 +28,7 @@ import { TemplateData } from './templates/TemplateData';
|
|||
import { FunctionAppProvider } from './tree/FunctionAppProvider';
|
||||
import { FunctionAppTreeItem } from './tree/FunctionAppTreeItem';
|
||||
import { FunctionTreeItem } from './tree/FunctionTreeItem';
|
||||
import { VSCodeUI } from './VSCodeUI';
|
||||
|
||||
let reporter: TelemetryReporter | undefined;
|
||||
|
||||
|
@ -58,14 +59,16 @@ export function activate(context: vscode.ExtensionContext): void {
|
|||
actionHandler.registerCommand('azureFunctions.refresh', async (node?: IAzureNode) => await tree.refresh(node));
|
||||
actionHandler.registerCommand('azureFunctions.loadMore', async (node: IAzureNode) => await tree.loadMore(node));
|
||||
actionHandler.registerCommand('azureFunctions.openInPortal', async (node?: IAzureNode<FunctionAppTreeItem>) => await openInPortal(tree, node));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.createFunction', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements) => await createFunction(properties, outputChannel, azureAccount, templateData));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.createNewProject', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements) => await createNewProject(properties, outputChannel));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.createFunction', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements, functionAppPath?: string, templateId?: string, functionName?: string, ...functionSettings: string[]) => {
|
||||
await createFunction(properties, outputChannel, azureAccount, templateData, new VSCodeUI(), functionAppPath, templateId, functionName, ...functionSettings);
|
||||
});
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.createNewProject', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements, functionAppPath?: string, language?: string, openFolder?: boolean | undefined) => await createNewProject(properties, outputChannel, functionAppPath, language, openFolder));
|
||||
actionHandler.registerCommand('azureFunctions.createFunctionApp', async (node?: IAzureParentNode) => await createChildNode(tree, AzureTreeDataProvider.subscriptionContextValue, node));
|
||||
actionHandler.registerCommand('azureFunctions.startFunctionApp', async (node?: IAzureNode<FunctionAppTreeItem>) => await startFunctionApp(tree, node));
|
||||
actionHandler.registerCommand('azureFunctions.stopFunctionApp', async (node?: IAzureNode<FunctionAppTreeItem>) => await stopFunctionApp(tree, node));
|
||||
actionHandler.registerCommand('azureFunctions.restartFunctionApp', async (node?: IAzureNode<FunctionAppTreeItem>) => await restartFunctionApp(tree, node));
|
||||
actionHandler.registerCommand('azureFunctions.deleteFunctionApp', async (node?: IAzureParentNode) => await deleteNode(tree, FunctionAppTreeItem.contextValue, node));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.deploy', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements, arg?: IAzureParentNode<FunctionAppTreeItem> | vscode.Uri) => await deploy(properties, tree, outputChannel, arg));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.deploy', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements, deployPath: vscode.Uri | string, functionAppId?: string) => await deploy(properties, tree, outputChannel, deployPath, functionAppId));
|
||||
actionHandler.registerCommandWithCustomTelemetry('azureFunctions.configureDeploymentSource', async (properties: TelemetryProperties, _measurements: TelemetryMeasurements, node?: IAzureNode<FunctionAppTreeItem>) => await configureDeploymentSource(properties, tree, outputChannel, node));
|
||||
actionHandler.registerCommand('azureFunctions.copyFunctionUrl', async (node?: IAzureNode<FunctionTreeItem>) => await copyFunctionUrl(tree, node));
|
||||
actionHandler.registerCommand('azureFunctions.deleteFunction', async (node?: IAzureNode) => await deleteNode(tree, FunctionTreeItem.contextValue, node));
|
||||
|
|
|
@ -173,7 +173,7 @@ export class TemplateData {
|
|||
for (const rawTemplate of rawTemplates) {
|
||||
try {
|
||||
this._templatesMap[runtime].push(new Template(rawTemplate, resources));
|
||||
} catch {
|
||||
} catch (error) {
|
||||
// Ignore errors so that a single poorly formed template does not affect other templates
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,14 +16,22 @@ import { IUserInterface, PickWithData } from '../IUserInterface';
|
|||
import { localize } from '../localize';
|
||||
import { getResourceTypeLabel, ResourceType } from '../templates/ConfigSetting';
|
||||
|
||||
function getResourceGroupFromId(id: string): string {
|
||||
function parseResourceId(id: string): RegExpMatchArray {
|
||||
const matches: RegExpMatchArray | null = id.match(/\/subscriptions\/(.*)\/resourceGroups\/(.*)\/providers\/(.*)\/(.*)/);
|
||||
|
||||
if (matches === null || matches.length < 3) {
|
||||
throw new Error(localize('azFunc.InvalidResourceId', 'Invalid Azure Resource Id'));
|
||||
}
|
||||
|
||||
return matches[2];
|
||||
return matches;
|
||||
}
|
||||
|
||||
function getResourceGroupFromId(id: string): string {
|
||||
return parseResourceId(id) [2];
|
||||
}
|
||||
|
||||
export function getSubscriptionFromId(id: string): string {
|
||||
return parseResourceId(id) [1];
|
||||
}
|
||||
|
||||
interface IBaseResourceWithName extends BaseResource {
|
||||
|
|
|
@ -12,7 +12,7 @@ export namespace gitUtils {
|
|||
try {
|
||||
await cpUtils.executeCommand(undefined, workingDirectory, gitCommand, '--version');
|
||||
return true;
|
||||
} catch {
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ export namespace mavenUtils {
|
|||
export async function validateMavenInstalled(workingDirectory: string): Promise<void> {
|
||||
try {
|
||||
await cpUtils.executeCommand(undefined, workingDirectory, mvnCommand, '--version');
|
||||
} catch {
|
||||
} catch (error) {
|
||||
throw new Error(localize('azFunc.mvnNotFound', 'Failed to find "maven" on path.'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,39 +16,39 @@ import * as fsUtil from '../src/utils/fs';
|
|||
import { TestAzureAccount } from './TestAzureAccount';
|
||||
import { TestUI } from './TestUI';
|
||||
|
||||
const templateData: TemplateData = new TemplateData();
|
||||
const testFolder: string = path.join(os.tmpdir(), `azFunc.createFuncTests${fsUtil.getRandomHexString()}`);
|
||||
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('Azure Functions Test');
|
||||
|
||||
const projectConfiguration: WorkspaceConfiguration = vscode.workspace.getConfiguration(extensionPrefix);
|
||||
// tslint:disable-next-line:no-backbone-get-set-outside-model
|
||||
const oldTemplateFilter: string | undefined = projectConfiguration.get(templateFilterSetting);
|
||||
const oldProjectLanguage: string | undefined = projectConfiguration.get(projectLanguageSetting);
|
||||
const oldProjectRuntime: string | undefined = projectConfiguration.get(projectRuntimeSetting);
|
||||
|
||||
suiteSetup(async () => {
|
||||
await fse.ensureDir(path.join(testFolder, '.vscode'));
|
||||
// Pretend to create the parent function app
|
||||
await Promise.all([
|
||||
fse.writeFile(path.join(testFolder, 'host.json'), ''),
|
||||
fse.writeFile(path.join(testFolder, 'local.settings.json'), '{ "Values": { "AzureWebJobsStorage": "test" } }'),
|
||||
fse.writeFile(path.join(testFolder, '.vscode', 'launch.json'), '')
|
||||
]);
|
||||
await projectConfiguration.update(templateFilterSetting, TemplateFilter.Core, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectLanguageSetting, ProjectLanguage.JavaScript, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectRuntimeSetting, ProjectRuntime.one, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
suiteTeardown(async () => {
|
||||
outputChannel.dispose();
|
||||
await fse.remove(testFolder);
|
||||
await projectConfiguration.update(templateFilterSetting, oldTemplateFilter, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectLanguageSetting, oldProjectLanguage, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectRuntimeSetting, oldProjectRuntime, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
// tslint:disable-next-line:max-func-body-length
|
||||
suite('Create Core Function Tests', () => {
|
||||
suite('Create JavaScript Core Function Tests', () => {
|
||||
const templateData: TemplateData = new TemplateData();
|
||||
const testFolder: string = path.join(os.tmpdir(), `azFunc.createFuncTests${fsUtil.getRandomHexString()}`);
|
||||
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('Azure Functions Test');
|
||||
|
||||
const projectConfiguration: WorkspaceConfiguration = vscode.workspace.getConfiguration(extensionPrefix);
|
||||
// tslint:disable-next-line:no-backbone-get-set-outside-model
|
||||
const oldTemplateFilter: string | undefined = projectConfiguration.get(templateFilterSetting);
|
||||
const oldProjectLanguage: string | undefined = projectConfiguration.get(projectLanguageSetting);
|
||||
const oldProjectRuntime: string | undefined = projectConfiguration.get(projectRuntimeSetting);
|
||||
|
||||
suiteSetup(async () => {
|
||||
await fse.ensureDir(path.join(testFolder, '.vscode'));
|
||||
// Pretend to create the parent function app
|
||||
await Promise.all([
|
||||
fse.writeFile(path.join(testFolder, 'host.json'), ''),
|
||||
fse.writeFile(path.join(testFolder, 'local.settings.json'), '{ "Values": { "AzureWebJobsStorage": "test" } }'),
|
||||
fse.writeFile(path.join(testFolder, '.vscode', 'launch.json'), '')
|
||||
]);
|
||||
await projectConfiguration.update(templateFilterSetting, TemplateFilter.Core, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectLanguageSetting, ProjectLanguage.JavaScript, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectRuntimeSetting, ProjectRuntime.one, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
suiteTeardown(async () => {
|
||||
outputChannel.dispose();
|
||||
await fse.remove(testFolder);
|
||||
await projectConfiguration.update(templateFilterSetting, oldTemplateFilter, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectLanguageSetting, oldProjectLanguage, vscode.ConfigurationTarget.Global);
|
||||
await projectConfiguration.update(projectRuntimeSetting, oldProjectRuntime, vscode.ConfigurationTarget.Global);
|
||||
});
|
||||
|
||||
const blobTrigger: string = 'Blob trigger';
|
||||
test(blobTrigger, async () => {
|
||||
await testCreateFunction(
|
||||
|
@ -58,7 +58,7 @@ suite('Create Core Function Tests', () => {
|
|||
'connectionString',
|
||||
undefined // Use default path
|
||||
);
|
||||
});
|
||||
}).timeout(6000);
|
||||
|
||||
const cosmosDBTrigger: string = 'Cosmos DB trigger';
|
||||
test(cosmosDBTrigger, async () => {
|
||||
|
@ -160,23 +160,35 @@ suite('Create Core Function Tests', () => {
|
|||
undefined // Use default schedule
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
async function testCreateFunction(templateName: string, ...inputs: (string | undefined)[]): Promise<void> {
|
||||
// Setup common inputs
|
||||
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
|
||||
test('createFunction API', async () => {
|
||||
const templateId: string = 'HttpTrigger-JavaScript';
|
||||
const functionName: string = 'createFunctionApi';
|
||||
const authLevel: string = 'Anonymous';
|
||||
await vscode.commands.executeCommand('azureFunctions.createFunction', testFolder, templateId, functionName, authLevel);
|
||||
await validateFunction(functionName);
|
||||
}).timeout(5000);
|
||||
|
||||
async function testCreateFunction(templateName: string, ...inputs: (string | undefined) []): Promise<void> {
|
||||
// Setup common inputs
|
||||
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
|
||||
}
|
||||
|
||||
const ui: TestUI = new TestUI(inputs);
|
||||
await createFunction({}, outputChannel, new TestAzureAccount(), templateData, ui);
|
||||
assert.equal(inputs.length, 0, 'Not all inputs were used.');
|
||||
|
||||
await validateFunction(funcName);
|
||||
}
|
||||
|
||||
const ui: TestUI = new TestUI(inputs);
|
||||
await createFunction({}, outputChannel, new TestAzureAccount(), templateData, ui);
|
||||
assert.equal(inputs.length, 0, 'Not all inputs were used.');
|
||||
|
||||
const functionPath: string = path.join(testFolder, funcName);
|
||||
assert.equal(await fse.pathExists(path.join(functionPath, 'index.js')), true, 'index.js does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(functionPath, 'function.json')), true, 'function.json does not exist');
|
||||
}
|
||||
async function validateFunction(funcName: string): Promise<void> {
|
||||
const functionPath: string = path.join(testFolder, funcName);
|
||||
assert.equal(await fse.pathExists(path.join(functionPath, 'index.js')), true, 'index.js does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(functionPath, 'function.json')), true, 'function.json does not exist');
|
||||
}
|
||||
});
|
||||
|
|
|
@ -13,15 +13,9 @@ import { ProjectLanguage } from '../src/ProjectSettings';
|
|||
import * as fsUtil from '../src/utils/fs';
|
||||
import { TestUI } from './TestUI';
|
||||
|
||||
const testFolderPath: string = path.join(os.tmpdir(), `azFunc.createNewProjectTests${fsUtil.getRandomHexString()}`);
|
||||
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('Azure Functions Test');
|
||||
|
||||
suite('Create New Java Project Tests', () => {
|
||||
|
||||
suiteSetup(async () => {
|
||||
await fse.ensureDir(testFolderPath);
|
||||
});
|
||||
|
||||
suite('Create New Project Tests', () => {
|
||||
const testFolderPath: string = path.join(os.tmpdir(), `azFunc.createNewProjectTests${fsUtil.getRandomHexString()}`);
|
||||
const outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel('Azure Functions Test');
|
||||
suiteTeardown(async () => {
|
||||
outputChannel.dispose();
|
||||
await fse.remove(testFolderPath);
|
||||
|
@ -29,7 +23,9 @@ suite('Create New Java Project Tests', () => {
|
|||
|
||||
const javaProject: string = 'JavaProject';
|
||||
test(javaProject, async () => {
|
||||
const projectPath: string = path.join(testFolderPath, javaProject);
|
||||
await testCreateNewProject(
|
||||
projectPath,
|
||||
ProjectLanguage.Java,
|
||||
undefined,
|
||||
undefined,
|
||||
|
@ -37,54 +33,49 @@ suite('Create New Java Project Tests', () => {
|
|||
undefined,
|
||||
undefined
|
||||
);
|
||||
await testJavaProjectFilesExist(testFolderPath);
|
||||
await testJavaProjectFilesExist(projectPath);
|
||||
}).timeout(60 * 1000);
|
||||
});
|
||||
|
||||
suite('Create New JavaScript Project Tests', () => {
|
||||
|
||||
suiteSetup(async () => {
|
||||
await fse.ensureDir(testFolderPath);
|
||||
});
|
||||
|
||||
suiteTeardown(async () => {
|
||||
outputChannel.dispose();
|
||||
await fse.remove(testFolderPath);
|
||||
});
|
||||
|
||||
const javaScriptProject: string = 'JavaScriptProject';
|
||||
test(javaScriptProject, async () => {
|
||||
await testCreateNewProject(ProjectLanguage.JavaScript);
|
||||
await testProjectFilesExist(testFolderPath);
|
||||
}).timeout(10 * 1000);
|
||||
});
|
||||
const projectPath: string = path.join(testFolderPath, javaScriptProject);
|
||||
await testCreateNewProject(projectPath, ProjectLanguage.JavaScript);
|
||||
await testProjectFilesExist(projectPath);
|
||||
});
|
||||
|
||||
async function testCreateNewProject(language: string, ...inputs: (string | undefined)[]): Promise<void> {
|
||||
// Setup common inputs
|
||||
inputs.unshift(language); // Specify the function name
|
||||
inputs.unshift(testFolderPath); // 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
|
||||
test('createNewProject API', async () => {
|
||||
const projectPath: string = path.join(testFolderPath, 'createNewProjectApi');
|
||||
await vscode.commands.executeCommand('azureFunctions.createNewProject', projectPath, 'JavaScript', false /* openFolder */);
|
||||
await testProjectFilesExist(projectPath);
|
||||
});
|
||||
|
||||
async function testCreateNewProject(projectPath: string, language: string, ...inputs: (string | undefined)[]): Promise<void> {
|
||||
// Setup common inputs
|
||||
inputs.unshift(language); // Specify the function name
|
||||
inputs.unshift(projectPath); // 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
|
||||
}
|
||||
|
||||
const ui: TestUI = new TestUI(inputs);
|
||||
await createNewProject({}, outputChannel, undefined, undefined, false, ui);
|
||||
assert.equal(inputs.length, 0, 'Not all inputs were used.');
|
||||
}
|
||||
|
||||
const ui: TestUI = new TestUI(inputs);
|
||||
await createNewProject({}, outputChannel, undefined, false, ui);
|
||||
assert.equal(inputs.length, 0, 'Not all inputs were used.');
|
||||
}
|
||||
async function testProjectFilesExist(projectPath: string): Promise<void> {
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, '.gitignore')), true, '.gitignore does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'host.json')), true, 'host.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'local.settings.json')), true, 'function.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, '.git')), true, '.git folder does not exist');
|
||||
const vscodePath: string = path.join(projectPath, '.vscode');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'settings.json')), true, 'settings.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'launch.json')), true, 'launch.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'tasks.json')), true, 'tasks.json does not exist');
|
||||
}
|
||||
|
||||
async function testProjectFilesExist(projectPath: string): Promise<void> {
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, '.gitignore')), true, '.gitignore does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'host.json')), true, 'host.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'local.settings.json')), true, 'function.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, '.git')), true, '.git folder does not exist');
|
||||
const vscodePath: string = path.join(projectPath, '.vscode');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'settings.json')), true, 'settings.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'launch.json')), true, 'launch.json does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(vscodePath, 'tasks.json')), true, 'tasks.json does not exist');
|
||||
}
|
||||
|
||||
async function testJavaProjectFilesExist(projectPath: string): Promise<void> {
|
||||
await testProjectFilesExist(projectPath);
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'src')), true, 'src folder does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'pom.xml')), true, 'pom.xml does not exist');
|
||||
}
|
||||
async function testJavaProjectFilesExist(projectPath: string): Promise<void> {
|
||||
await testProjectFilesExist(projectPath);
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'src')), true, 'src folder does not exist');
|
||||
assert.equal(await fse.pathExists(path.join(projectPath, 'pom.xml')), true, 'pom.xml does not exist');
|
||||
}
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче