Skip to content

Commit 2654323

Browse files
committed
Add functions explorer
Refresh command and display subscriptions
1 parent 184eae9 commit 2654323

21 files changed

+531
-10
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ typings/
5757
# dotenv environment variables file
5858
.env
5959

60+
# package-lock file since it just causes merge conflicts
61+
package-lock.json

.travis.yml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
language: node_js
2+
3+
node_js:
4+
- 'stable'
5+
6+
install:
7+
- npm install
8+
9+
script:
10+
- tsc -p ./
11+
12+
notifications:
13+
email:
14+
on_success: never
15+
on_failure: always

.vscode/launch.json

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// A launch configuration that compiles the extension and then opens it inside a new window
2+
{
3+
"version": "0.1.0",
4+
"configurations": [
5+
{
6+
"name": "Launch Extension",
7+
"type": "extensionHost",
8+
"request": "launch",
9+
"runtimeExecutable": "${execPath}",
10+
"args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
11+
"stopOnEntry": false,
12+
"sourceMaps": true,
13+
"outFiles": [ "${workspaceRoot}/out/src/**/*.js" ],
14+
"preLaunchTask": "npm"
15+
},
16+
{
17+
"name": "Launch Tests",
18+
"type": "extensionHost",
19+
"request": "launch",
20+
"runtimeExecutable": "${execPath}",
21+
"args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
22+
"stopOnEntry": false,
23+
"sourceMaps": true,
24+
"outFiles": [ "${workspaceRoot}/out/test/**/*.js" ],
25+
"preLaunchTask": "npm"
26+
}
27+
]
28+
}

.vscode/settings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Place your settings in this file to overwrite default and user settings.
2+
{
3+
"files.exclude": {
4+
"out": false // set this to true to hide the "out" folder with the compiled JS files
5+
},
6+
"search.exclude": {
7+
"out": true // set this to false to include "out" folder in search results
8+
}
9+
}

.vscode/tasks.json

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Available variables which can be used inside of strings.
2+
// ${workspaceRoot}: the root folder of the team
3+
// ${file}: the current opened file
4+
// ${fileBasename}: the current opened file's basename
5+
// ${fileDirname}: the current opened file's dirname
6+
// ${fileExtname}: the current opened file's extension
7+
// ${cwd}: the current working directory of the spawned process
8+
9+
// A task runner that calls a custom npm script that compiles the extension.
10+
{
11+
"version": "0.1.0",
12+
13+
// we want to run npm
14+
"command": "npm",
15+
16+
// the command is a shell script
17+
"isShellCommand": true,
18+
19+
// show the output window only if unrecognized errors occur.
20+
"showOutput": "silent",
21+
22+
// we run the custom script "compile" as defined in package.json
23+
"args": ["run", "compile", "--loglevel", "silent"],
24+
25+
// The tsc compiler is started in watching mode
26+
"isBackground": true,
27+
28+
// use the standard tsc in watch mode problem matcher to find compile problems in the output.
29+
"problemMatcher": "$tsc-watch"
30+
}

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Change Log
2+
All notable changes to the "azurefunctions" extension will be documented in this file.
3+
4+
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
5+
6+
## [Unreleased]

LICENSE LICENSE.md

File renamed without changes.

README.md

+22-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1+
# Azure Functions for Visual Studio Code
12

2-
# Contributing
3+
## Features
34

4-
This project welcomes contributions and suggestions. Most contributions require you to agree to a
5-
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
6-
the rights to use your contribution. For details, visit https://cla.microsoft.com.
5+
## Contributing
6+
There are a couple of ways you can contribute to this repo:
77

8-
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
9-
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
10-
provided by the bot. You will only need to do this once across all repos using our CLA.
8+
- **Ideas, feature requests and bugs**: We are open to all ideas and we want to get rid of bugs! Use the Issues section to either report a new issue, provide your ideas or contribute to existing threads.
9+
- **Documentation**: Found a typo or strangely worded sentences? Submit a PR!
10+
- **Code**: Contribute bug fixes, features or design changes:
11+
- Clone the repository locally and open in VS Code.
12+
- Open the terminal (press `CTRL+`\`) and run `npm install`.
13+
- To build, press `F1` and type in `Tasks: Run Build Task`.
14+
- Debug: press `F5` to start debugging the extension.
1115

12-
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
13-
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
14-
contact [[email protected]](mailto:[email protected]) with any additional questions or comments.
16+
### Legal
17+
Before we can accept your pull request you will need to sign a **Contribution License Agreement**. All you need to do is to submit a pull request, then the PR will get appropriately labelled (e.g. `cla-required`, `cla-norequired`, `cla-signed`, `cla-already-signed`). If you already signed the agreement we will continue with reviewing the PR, otherwise system will tell you how you can sign the CLA. Once you sign the CLA all future PR's will be labeled as `cla-signed`.
18+
19+
### Code of Conduct
20+
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [[email protected]](mailto:[email protected]) with any additional questions or comments.
21+
22+
## Telemetry
23+
This extension collects telemetry data to help us build a better experience with Cosmos DB and VS Code. The extension respects the `telemetry.enableTelemetry` setting which you can learn more about in our [FAQ](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting).
24+
25+
## License
26+
[MIT](LICENSE.md)

package.json

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
{
2+
"name": "vscode-azurefunctions",
3+
"displayName": "Azure Functions",
4+
"description": "An Azure Functions extension for Visual Studio Code.",
5+
"version": "0.1.0",
6+
"publisher": "ms-azuretools",
7+
"icon": "resources/TODO.png",
8+
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
9+
"engines": {
10+
"vscode": "^1.16.0"
11+
},
12+
"repository": {
13+
"type": "git",
14+
"url": "https://github.com/Microsoft/vscode-azurefunctions"
15+
},
16+
"galleryBanner": {
17+
"color": "#0072c6",
18+
"theme": "dark"
19+
},
20+
"homepage": "https://github.com/Microsoft/vscode-azurefunctions/blob/master/README.md",
21+
"license": "SEE LICENSE IN LICENSE.md",
22+
"categories": [
23+
"Azure"
24+
],
25+
"preview": true,
26+
"activationEvents": [
27+
"onCommand:azureFunctions.refresh",
28+
"onView:azureFunctionsExplorer"
29+
],
30+
"main": "./out/src/extension",
31+
"contributes": {
32+
"commands": [
33+
{
34+
"command": "azureFunctions.refresh",
35+
"title": "Refresh",
36+
"category": "Azure Functions",
37+
"icon": {
38+
"light": "resources/light/refresh.svg",
39+
"dark": "resources/dark/refresh.svg"
40+
}
41+
}
42+
],
43+
"views": {
44+
"explorer": [
45+
{
46+
"id": "azureFunctionsExplorer",
47+
"name": "Functions"
48+
}
49+
]
50+
},
51+
"menus": {
52+
"view/title": [
53+
{
54+
"command": "azureFunctions.refresh",
55+
"when": "view == azureFunctionsExplorer",
56+
"group": "navigation@2"
57+
}
58+
]
59+
}
60+
},
61+
"scripts": {
62+
"vscode:prepublish": "tsc -p ./",
63+
"compile": "tsc -watch -p ./",
64+
"postinstall": "node ./node_modules/vscode/bin/install",
65+
"test": "node ./node_modules/vscode/bin/test"
66+
},
67+
"devDependencies": {
68+
"@types/archiver": "^2.0.0",
69+
"@types/node": "^8.0.28",
70+
"typescript": "^2.0.3",
71+
"vscode": "^1.0.0"
72+
},
73+
"dependencies": {
74+
"archiver": "^2.0.3",
75+
"azure-arm-resource": "^2.0.0-preview",
76+
"ms-rest": "^2.2.2",
77+
"ms-rest-azure": "^2.3.1",
78+
"opn": "^5.1.0",
79+
"vscode-extension-telemetry": "^0.0.6"
80+
},
81+
"extensionDependencies": [
82+
"ms-vscode.azure-account"
83+
]
84+
}

resources/dark/AzureSubscription.svg

+1
Loading

resources/dark/Refresh.svg

+1
Loading

resources/light/AzureSubscription.svg

+1
Loading

resources/light/Refresh.svg

+1
Loading

src/azure-account.api.d.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { Event } from 'vscode';
7+
import { ServiceClientCredentials } from 'ms-rest';
8+
import { AzureEnvironment } from 'ms-rest-azure';
9+
import { SubscriptionModels } from 'azure-arm-resource';
10+
11+
export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut';
12+
13+
export interface AzureAccount {
14+
readonly status: AzureLoginStatus;
15+
readonly onStatusChanged: Event<AzureLoginStatus>;
16+
readonly waitForLogin: () => Promise<boolean>;
17+
readonly sessions: AzureSession[];
18+
readonly onSessionsChanged: Event<void>;
19+
readonly filters: AzureResourceFilter[];
20+
readonly onFiltersChanged: Event<void>;
21+
}
22+
23+
export interface AzureSession {
24+
readonly environment: AzureEnvironment;
25+
readonly userId: string;
26+
readonly tenantId: string;
27+
readonly credentials: ServiceClientCredentials;
28+
}
29+
30+
export interface AzureResourceFilter {
31+
readonly session: AzureSession;
32+
readonly subscription: SubscriptionModels.Subscription;
33+
}
34+
35+
export interface Credentials {
36+
readSecret(service: string, account: string): Thenable<string | undefined>;
37+
writeSecret(service: string, account: string, secret: string): Thenable<void>;
38+
deleteSecret(service: string, account: string): Thenable<boolean>;
39+
}

src/explorer.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import { TreeDataProvider, Event, EventEmitter, TreeItem } from 'vscode';
7+
import { LoadingNode, NoSubscriptionsNode, SignInToAzureNode, SubscriptionNode, INode } from './nodes';
8+
import { AzureAccount } from './azure-account.api';
9+
10+
export class AzureFunctionsExplorer implements TreeDataProvider<INode> {
11+
private _onDidChangeTreeData: EventEmitter<INode> = new EventEmitter<INode>();
12+
readonly onDidChangeTreeData: Event<INode> = this._onDidChangeTreeData.event;
13+
14+
constructor(private azureAccount: AzureAccount) {
15+
}
16+
17+
getTreeItem(node: INode): TreeItem {
18+
return node;
19+
}
20+
21+
async getChildren(node?: INode): Promise<INode[]> {
22+
if (node) {
23+
return node.getChildren ? await node.getChildren() : [];
24+
} else { // Root of the explorer
25+
if (this.azureAccount.status === "Initializing" || this.azureAccount.status === "LoggingIn") {
26+
return [new LoadingNode()];
27+
} else if (this.azureAccount.status === "LoggedOut") {
28+
return [new SignInToAzureNode()];
29+
} else if (this.azureAccount.filters.length === 0) {
30+
return [new NoSubscriptionsNode()];
31+
} else {
32+
return this.azureAccount.filters.map(filter => new SubscriptionNode(filter))
33+
}
34+
}
35+
}
36+
37+
refresh(node?: INode): void {
38+
this._onDidChangeTreeData.fire(node);
39+
}
40+
}

src/extension.ts

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
'use strict';
7+
8+
import * as vscode from 'vscode';
9+
import * as util from "./util";
10+
11+
import { AzureAccount } from './azure-account.api';
12+
import { AzureFunctionsExplorer } from './explorer';
13+
import { INode } from './nodes'
14+
import { Reporter } from './telemetry';
15+
16+
export function activate(context: vscode.ExtensionContext) {
17+
context.subscriptions.push(new Reporter(context));
18+
19+
const azureAccount = vscode.extensions.getExtension<AzureAccount>('ms-vscode.azure-account')!.exports;
20+
if (azureAccount) {
21+
context.subscriptions.push(azureAccount.onFiltersChanged(() => explorer.refresh()));
22+
context.subscriptions.push(azureAccount.onStatusChanged(() => explorer.refresh()));
23+
24+
const explorer = new AzureFunctionsExplorer(azureAccount);
25+
context.subscriptions.push(vscode.window.registerTreeDataProvider('azureFunctionsExplorer', explorer));
26+
27+
initCommand(context, 'azureFunctions.refresh', (node?: INode) => explorer.refresh(node));
28+
} else {
29+
vscode.window.showErrorMessage("The Azure Account Extension is required for the Azure Functions extension.");
30+
}
31+
}
32+
33+
export function deactivate() {
34+
}
35+
36+
function initCommand(context: vscode.ExtensionContext, commandId: string, callback: (...args: any[]) => any) {
37+
initAsyncCommand(context, commandId, (...args: any[]) => Promise.resolve(callback(...args)));
38+
}
39+
40+
function initAsyncCommand(context: vscode.ExtensionContext, commandId: string, callback: (...args: any[]) => Promise<any>) {
41+
context.subscriptions.push(vscode.commands.registerCommand(commandId, async (...args: any[]) => {
42+
const start = Date.now();
43+
let result = 'Succeeded';
44+
let errorData: string | undefined;
45+
46+
try {
47+
await callback(...args);
48+
} catch (error) {
49+
result = 'Failed';
50+
errorData = util.errorToString(error);
51+
throw error;
52+
} finally {
53+
const end = Date.now();
54+
const properties: { [key: string]: string; } = { result: result };
55+
if (errorData) {
56+
properties.error = errorData;
57+
}
58+
util.sendTelemetry(commandId, properties, { duration: (end - start) / 1000 });
59+
}
60+
}));
61+
}

0 commit comments

Comments
 (0)