Display Function App nodes in explorer
This includes the basic 'Open In Portal' action
This commit is contained in:
Родитель
26543238ba
Коммит
71a8d1be9a
20
package.json
20
package.json
|
@ -38,6 +38,11 @@
|
|||
"light": "resources/light/refresh.svg",
|
||||
"dark": "resources/dark/refresh.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.openInPortal",
|
||||
"title": "Open In Portal",
|
||||
"category": "Azure Functions"
|
||||
}
|
||||
],
|
||||
"views": {
|
||||
|
@ -55,6 +60,18 @@
|
|||
"when": "view == azureFunctionsExplorer",
|
||||
"group": "navigation@2"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "azureFunctions.refresh",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsSubscription",
|
||||
"group": "1_functionAppGeneralCommands@2"
|
||||
},
|
||||
{
|
||||
"command": "azureFunctions.openInPortal",
|
||||
"when": "view == azureFunctionsExplorer && viewItem == azureFunctionsFunctionApp",
|
||||
"group": "1_subscriptionGeneralCommands@2"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -73,6 +90,7 @@
|
|||
"dependencies": {
|
||||
"archiver": "^2.0.3",
|
||||
"azure-arm-resource": "^2.0.0-preview",
|
||||
"azure-arm-website": "^1.0.0-preview",
|
||||
"ms-rest": "^2.2.2",
|
||||
"ms-rest-azure": "^2.3.1",
|
||||
"opn": "^5.1.0",
|
||||
|
@ -81,4 +99,4 @@
|
|||
"extensionDependencies": [
|
||||
"ms-vscode.azure-account"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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-bg{fill:#c5c5c5}.st0{fill:#2d2d30}</style><path class="icon-canvas-transparent" d="M16 0v16H0V0h16z" id="canvas"/><path class="st0" d="M13 4.201L11.202 6H9.914L13 2.914V2H6.882L4.854 6.056 3 4.201 0 7.202v2.596l3 2.999L5.298 10.5l-.5-.5h1.084l-2 4h2.532l5.894-5.894.394.394-2.001 2L13 12.797l3-2.999V7.202l-3-3.001z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M7.5 7H12l-6 6h-.5l2-4h-3l3-6h4l-4 4zM3 5.615L.116 8.5 3 11.383l.884-.883-2-2 2-2L3 5.615zm10 0l-.884.885 2 2-2 2 .884.883L15.884 8.5 13 5.615z" id="iconBg"/></svg>
|
После Ширина: | Высота: | Размер: 670 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-bg{fill:#424242}.st0{fill:#f6f6f6}</style><path class="icon-canvas-transparent" d="M16 0v16H0V0h16z" id="canvas"/><path class="st0" d="M13 4.201L11.202 6H9.914L13 2.914V2H6.882L4.854 6.056 3 4.201 0 7.202v2.596l3 2.999L5.298 10.5l-.5-.5h1.084l-2 4h2.532l5.894-5.894.394.394-2.001 2L13 12.797l3-2.999V7.202l-3-3.001z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M7.5 7H12l-6 6h-.5l2-4h-3l3-6h4l-4 4zM3 5.615L.116 8.5 3 11.383l.884-.883-2-2 2-2L3 5.615zm10 0l-.884.885 2 2-2 2 .884.883L15.884 8.5 13 5.615z" id="iconBg"/></svg>
|
После Ширина: | Высота: | Размер: 670 B |
|
@ -0,0 +1,16 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 opn from 'opn';
|
||||
import { INode } from './nodes';
|
||||
|
||||
export class AzureFunctionsCommands {
|
||||
public static openInPortal(node?: INode) {
|
||||
if (node && node.tenantId) {
|
||||
opn(`https://portal.azure.com/${node.tenantId}/#resource${node.id}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { TreeDataProvider, Event, EventEmitter, TreeItem } from 'vscode';
|
||||
import { LoadingNode, NoSubscriptionsNode, SignInToAzureNode, SubscriptionNode, INode } from './nodes';
|
||||
import { GenericNode, SubscriptionNode, INode } from './nodes';
|
||||
import { AzureAccount } from './azure-account.api';
|
||||
|
||||
export class AzureFunctionsExplorer implements TreeDataProvider<INode> {
|
||||
|
@ -23,11 +23,11 @@ export class AzureFunctionsExplorer implements TreeDataProvider<INode> {
|
|||
return node.getChildren ? await node.getChildren() : [];
|
||||
} else { // Root of the explorer
|
||||
if (this.azureAccount.status === "Initializing" || this.azureAccount.status === "LoggingIn") {
|
||||
return [new LoadingNode()];
|
||||
return [new GenericNode('azureFunctionsLoading', 'Loading...')];
|
||||
} else if (this.azureAccount.status === "LoggedOut") {
|
||||
return [new SignInToAzureNode()];
|
||||
return [new GenericNode("azureFunctionsSignInToAzure", "Sign in to Azure...", 'azure-account.login')];
|
||||
} else if (this.azureAccount.filters.length === 0) {
|
||||
return [new NoSubscriptionsNode()];
|
||||
return [new GenericNode("azureFunctionsNoSubscriptions", "No subscriptions found. Edit filters...", 'azure-account.selectSubscriptions')];
|
||||
} else {
|
||||
return this.azureAccount.filters.map(filter => new SubscriptionNode(filter))
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import { AzureAccount } from './azure-account.api';
|
|||
import { AzureFunctionsExplorer } from './explorer';
|
||||
import { INode } from './nodes'
|
||||
import { Reporter } from './telemetry';
|
||||
import { AzureFunctionsCommands } from './commands';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
context.subscriptions.push(new Reporter(context));
|
||||
|
@ -25,6 +26,7 @@ export function activate(context: vscode.ExtensionContext) {
|
|||
context.subscriptions.push(vscode.window.registerTreeDataProvider('azureFunctionsExplorer', explorer));
|
||||
|
||||
initCommand(context, 'azureFunctions.refresh', (node?: INode) => explorer.refresh(node));
|
||||
initCommand(context, 'azureFunctions.openInPortal', (node?: INode) => AzureFunctionsCommands.openInPortal(node));
|
||||
} else {
|
||||
vscode.window.showErrorMessage("The Azure Account Extension is required for the Azure Functions extension.");
|
||||
}
|
||||
|
|
86
src/nodes.ts
86
src/nodes.ts
|
@ -4,25 +4,41 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as WebSiteModels from '../node_modules/azure-arm-website/lib/models';
|
||||
|
||||
import { AzureResourceFilter } from './azure-account.api';
|
||||
import WebSiteManagementClient = require('azure-arm-website');
|
||||
|
||||
export interface INode extends vscode.TreeItem {
|
||||
id: string;
|
||||
tenantId?: string;
|
||||
getChildren?(): Promise<INode[]>;
|
||||
}
|
||||
|
||||
export class GenericNode implements INode {
|
||||
readonly contextValue: string;
|
||||
readonly command: vscode.Command;
|
||||
constructor(readonly id: string, readonly label: string, commandId?: string) {
|
||||
this.contextValue = id;
|
||||
if (commandId) {
|
||||
this.command = {
|
||||
command: commandId,
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SubscriptionNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsSubscription';
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
|
||||
readonly collapsibleState = vscode.TreeItemCollapsibleState.Collapsed;
|
||||
|
||||
constructor(private readonly subscriptionFilter: AzureResourceFilter) {
|
||||
if (subscriptionFilter.subscription.displayName) {
|
||||
this.label = subscriptionFilter.subscription.displayName;
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
this.label = subscriptionFilter.subscription.displayName!;
|
||||
this.id = subscriptionFilter.subscription.subscriptionId!;
|
||||
}
|
||||
|
||||
get iconPath(): any {
|
||||
|
@ -31,40 +47,30 @@ export class SubscriptionNode implements INode {
|
|||
dark: path.join(__filename, '..', '..', '..', 'resources', 'dark', 'AzureSubscription.svg')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class LoadingNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsLoading';
|
||||
readonly label: string = "Loading...";
|
||||
}
|
||||
|
||||
export class NoSubscriptionsNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsNoSubscriptions';
|
||||
readonly label: string = "No subscriptions found. Edit filters...";
|
||||
readonly command: vscode.Command = {
|
||||
command: 'azure-account.selectSubscriptions',
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
export class NoResourcesNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsNoResources';
|
||||
readonly label: string = "No resources found.";
|
||||
}
|
||||
|
||||
export class SignInToAzureNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsSignInToAzure';
|
||||
readonly label: string = "Sign in to Azure...";
|
||||
readonly command: vscode.Command = {
|
||||
command: 'azure-account.login',
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
|
||||
export class ErrorNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsError';
|
||||
readonly label: string;
|
||||
constructor(errorMessage: string) {
|
||||
this.label = `Error: ${errorMessage}`;
|
||||
async getChildren(): Promise<INode[]> {
|
||||
const client = new WebSiteManagementClient(this.subscriptionFilter.session.credentials, this.id);
|
||||
const webApps = await client.webApps.list();
|
||||
return webApps.filter(s => s.kind === "functionapp")
|
||||
.sort((s1, s2) => s1.id!.localeCompare(s2.id!))
|
||||
.map(s => new FunctionAppNode(s, this.subscriptionFilter.session.tenantId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class FunctionAppNode implements INode {
|
||||
readonly contextValue: string = 'azureFunctionsFunctionApp';
|
||||
readonly label: string;
|
||||
readonly id: string;
|
||||
|
||||
constructor(readonly functionApp: WebSiteModels.Site, readonly tenantId?: string) {
|
||||
this.label = `${functionApp.name} (${this.functionApp.resourceGroup})`;
|
||||
this.id = functionApp.id!;
|
||||
}
|
||||
|
||||
get iconPath(): any {
|
||||
return {
|
||||
light: path.join(__filename, '..', '..', '..', 'resources', 'light', 'AzureFunctionsApp.svg'),
|
||||
dark: path.join(__filename, '..', '..', '..', 'resources', 'dark', 'AzureFunctionsApp.svg')
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче