Add support for init function app and create function
These are just simple wrappers for the cli at this point
This commit is contained in:
Родитель
71a8d1be9a
Коммит
c86211518e
34
package.json
34
package.json
|
@ -35,8 +35,26 @@
|
|||
"title": "Refresh",
|
||||
"category": "Azure Functions",
|
||||
"icon": {
|
||||
"light": "resources/light/refresh.svg",
|
||||
"dark": "resources/dark/refresh.svg"
|
||||
"light": "resources/light/Refresh.svg",
|
||||
"dark": "resources/dark/Refresh.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.initFunctionApp",
|
||||
"title": "Initialize Function App",
|
||||
"category": "Azure Functions",
|
||||
"icon": {
|
||||
"light": "resources/light/InitFunctionApp.svg",
|
||||
"dark": "resources/dark/InitFunctionApp.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.createFunction",
|
||||
"title": "Create Function",
|
||||
"category": "Azure Functions",
|
||||
"icon": {
|
||||
"light": "resources/light/AddFunction.svg",
|
||||
"dark": "resources/dark/AddFunction.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -56,9 +74,19 @@
|
|||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "azureFunctions.refresh",
|
||||
"command": "azureFunctions.initFunctionApp",
|
||||
"when": "view == azureFunctionsExplorer",
|
||||
"group": "navigation@1"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.createFunction",
|
||||
"when": "view == azureFunctionsExplorer",
|
||||
"group": "navigation@2"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.refresh",
|
||||
"when": "view == azureFunctionsExplorer",
|
||||
"group": "navigation@3"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-action-green{fill:#89d185}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 1.414L11.414 6H16v1.414L7.414 16H5v-1.234L7.371 10H4V8.764L4.382 8H2.019V6H0V2.018h2.019V0H6v2.018h1.373L8.382 0H16v1.414z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M9 7h6l-8 8H6l2.984-6H5l.5-1H6V7l.5-1H8V3l1-2h6L9 7z" id="iconBg"/><path class="icon-vs-action-green" d="M7 3.018H5V1H3.019v2.018H1V5h2.019v2H5V5h2V3.018z" id="colorAction"/></svg>
|
После Ширина: | Высота: | Размер: 684 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-folder{fill:#c09553}.icon-vs-fg{fill:#2b282e}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0V0z" id="canvas"/><path class="icon-vs-out" d="M14.996 9.418V10H16v1.352l-1.004.96v.188c0 .827-.673 1.5-1.5 1.5h-.266l-2.092 2H9.441l.961-2H1.5C.673 14 0 13.327 0 12.5v-10C0 1.673.673 1 1.5 1h8.11l1 2h2.886c.827 0 1.5.673 1.5 1.5V7H16v1.414l-1.004 1.004z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M2 3h6.374l.5 1H2V3z" id="iconBg" style="display: none;"/><g id="iconFg"><path class="icon-vs-bg" d="M12 8l-2 4h2.5L11 15l4-4h-3l3-3z"/><path class="icon-folder" d="M13.996 7V4.5a.5.5 0 0 0-.5-.5H9.992l-1-2H1.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h6.882l3-6h2.614zM2 4V3h6.374l.5 1H2z"/></g></svg>
|
После Ширина: | Высота: | Размер: 894 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-action-green{fill:#388a34}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 1.414L11.414 6H16v1.414L7.414 16H5v-1.234L7.371 10H4V8.764L4.382 8H2.019V6H0V2.018h2.019V0H6v2.018h1.373L8.382 0H16v1.414z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M9 7h6l-8 8H6l2.984-6H5l.5-1H6V7l.5-1H8V3l1-2h6L9 7z" id="iconBg"/><path class="icon-vs-action-green" d="M7 3.018H5V1H3.019v2.018H1V5h2.019v2H5V5h2V3.018z" id="colorAction"/></svg>
|
После Ширина: | Высота: | Размер: 684 B |
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-folder{fill:#dcb67a}.icon-vs-fg{fill:#f0eff1}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0V0z" id="canvas"/><path class="icon-vs-out" d="M14.996 9.418V10H16v1.352l-1.004.96v.188c0 .827-.673 1.5-1.5 1.5h-.266l-2.092 2H9.441l.961-2H1.5C.673 14 0 13.327 0 12.5v-10C0 1.673.673 1 1.5 1h8.11l1 2h2.886c.827 0 1.5.673 1.5 1.5V7H16v1.414l-1.004 1.004z" id="outline" style="display: none;"/><path class="icon-vs-fg" d="M2 3h6.374l.5 1H2V3z" id="iconBg" style="display: none;"/><g id="iconFg"><path class="icon-vs-bg" d="M12 8l-2 4h2.5L11 15l4-4h-3l3-3z"/><path class="icon-folder" d="M13.996 7V4.5a.5.5 0 0 0-.5-.5H9.992l-1-2H1.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5h6.882l3-6h2.614zM2 4V3h6.374l.5 1H2z"/></g></svg>
|
После Ширина: | Высота: | Размер: 894 B |
|
@ -5,12 +5,32 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import * as opn from 'opn';
|
||||
import * as util from './util';
|
||||
import { INode } from './nodes';
|
||||
import { FunctionsCli } from './functions-cli';
|
||||
import { QuickPickItemWithData } from './util';
|
||||
|
||||
export class AzureFunctionsCommands {
|
||||
public static openInPortal(node?: INode) {
|
||||
if (node && node.tenantId) {
|
||||
opn(`https://portal.azure.com/${node.tenantId}/#resource${node.id}`);
|
||||
}
|
||||
export function openInPortal(node?: INode) {
|
||||
if (node && node.tenantId) {
|
||||
opn(`https://portal.azure.com/${node.tenantId}/#resource${node.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function createFunction(functionsCli: FunctionsCli) {
|
||||
const templates = [
|
||||
new QuickPickItemWithData("BlobTrigger"),
|
||||
new QuickPickItemWithData("EventGridTrigger"),
|
||||
new QuickPickItemWithData("HttpTrigger"),
|
||||
new QuickPickItemWithData("QueueTrigger"),
|
||||
new QuickPickItemWithData("TimerTrigger")
|
||||
];
|
||||
const template = await util.showQuickPick(templates, "Select a function template");
|
||||
|
||||
const name = await util.showInputBox("Function Name", "Provide a function name");
|
||||
|
||||
functionsCli.createFunction(template.label, name);
|
||||
}
|
||||
|
||||
export async function initFunctionApp(functionsCli: FunctionsCli) {
|
||||
functionsCli.initFunctionApp("TODO");
|
||||
}
|
|
@ -7,12 +7,13 @@
|
|||
|
||||
import * as vscode from 'vscode';
|
||||
import * as util from "./util";
|
||||
import * as commands from './commands';
|
||||
|
||||
import { AzureAccount } from './azure-account.api';
|
||||
import { AzureFunctionsExplorer } from './explorer';
|
||||
import { INode } from './nodes'
|
||||
import { Reporter } from './telemetry';
|
||||
import { AzureFunctionsCommands } from './commands';
|
||||
import { FunctionsCli } from './functions-cli'
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(new Reporter(context));
|
||||
|
@ -25,8 +26,14 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
const explorer = new AzureFunctionsExplorer(azureAccount);
|
||||
context.subscriptions.push(vscode.window.registerTreeDataProvider('azureFunctionsExplorer', explorer));
|
||||
|
||||
const terminal: vscode.Terminal = vscode.window.createTerminal('Azure Functions');
|
||||
context.subscriptions.push(terminal);
|
||||
const functionsCli = new FunctionsCli(terminal);
|
||||
|
||||
initCommand(context, 'azureFunctions.refresh', (node?: INode) => explorer.refresh(node));
|
||||
initCommand(context, 'azureFunctions.openInPortal', (node?: INode) => AzureFunctionsCommands.openInPortal(node));
|
||||
initCommand(context, 'azureFunctions.openInPortal', (node?: INode) => commands.openInPortal(node));
|
||||
initCommand(context, 'azureFunctions.createFunction', (node?: INode) => commands.createFunction(functionsCli));
|
||||
initCommand(context, 'azureFunctions.initFunctionApp', (node?: INode) => commands.initFunctionApp(functionsCli));
|
||||
} else {
|
||||
vscode.window.showErrorMessage("The Azure Account Extension is required for the Azure Functions extension.");
|
||||
}
|
||||
|
@ -48,9 +55,14 @@ function initAsyncCommand(context: vscode.ExtensionContext, commandId: string, c
|
|||
try {
|
||||
await callback(...args);
|
||||
} catch (error) {
|
||||
result = 'Failed';
|
||||
errorData = util.errorToString(error);
|
||||
throw error;
|
||||
if (error instanceof util.UserCancelledError) {
|
||||
result = 'Canceled';
|
||||
// Swallow the error so that it's not displayed to the user
|
||||
} else {
|
||||
result = 'Failed';
|
||||
errorData = util.errorToString(error);
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
const end = Date.now();
|
||||
const properties: { [key: string]: string; } = { result: result };
|
||||
|
|
|
@ -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 * as vscode from 'vscode';
|
||||
import * as util from './util';
|
||||
|
||||
export class FunctionsCli {
|
||||
constructor(private readonly _terminal: vscode.Terminal) {
|
||||
}
|
||||
|
||||
createFunction(templateName: string, name: string) {
|
||||
this.executeCommand(`func new --language JavaScript --template ${templateName} --name ${name}`);
|
||||
}
|
||||
|
||||
async initFunctionApp(name: string) {
|
||||
this.executeCommand(`func init`);
|
||||
}
|
||||
|
||||
private executeCommand(command: string) {
|
||||
// TODO: Verify terminal is in current working folder
|
||||
this._terminal.show();
|
||||
this._terminal.sendText(command);
|
||||
}
|
||||
}
|
42
src/util.ts
42
src/util.ts
|
@ -4,6 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { reporter } from './telemetry';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export function sendTelemetry(eventName: string, properties?: { [key: string]: string; }, measures?: { [key: string]: number; }) {
|
||||
if (reporter) {
|
||||
|
@ -28,4 +29,43 @@ export function errorToString(error: any): string | undefined {
|
|||
|
||||
return error.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function showQuickPick<T>(items: QuickPickItemWithData<T>[] | Thenable<QuickPickItemWithData<T>[]>, placeHolder: string, token?: vscode.CancellationToken): Promise<QuickPickItemWithData<T>> {
|
||||
const options: vscode.QuickPickOptions = {
|
||||
placeHolder: placeHolder,
|
||||
ignoreFocusOut: true
|
||||
}
|
||||
const result = await vscode.window.showQuickPick(items, options, token);
|
||||
|
||||
if (!result) {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function showInputBox(placeHolder: string, prompt: string): Promise<string> {
|
||||
const options: vscode.InputBoxOptions = {
|
||||
placeHolder: placeHolder,
|
||||
prompt: prompt,
|
||||
// TODO: validateInput
|
||||
ignoreFocusOut: true
|
||||
}
|
||||
const result = await vscode.window.showInputBox(options);
|
||||
|
||||
if (!result) {
|
||||
throw new UserCancelledError();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export class QuickPickItemWithData<T> implements vscode.QuickPickItem {
|
||||
readonly description: string;
|
||||
constructor(readonly label: string, description?: string, readonly data?: T) {
|
||||
this.description = description ? description : '';
|
||||
}
|
||||
}
|
||||
|
||||
export class UserCancelledError extends Error { }
|
Загрузка…
Ссылка в новой задаче