Select IoT Hub with Azure login (#18)

* Select subscription list

* Fix unit test

* Remove Extension Dependencies when running unit test

* Revert "Remove Extension Dependencies when running unit test"

This reverts commit 78ddd4387d.

* Disable unit test temporarily

* List and select IoT Hub

* Get Iot Hub Connection String

* Update Iot Hub Connection String

* ignoreFocusOut

* BI for selectIoTHub

* Update README

* Update azure-arm-iothub to fix CI

* Add select-iot-hub.png
This commit is contained in:
Jun Han 2017-09-29 16:24:21 +08:00 коммит произвёл GitHub
Родитель b3d28c5801
Коммит ea71f9b042
9 изменённых файлов: 223 добавлений и 17 удалений

Просмотреть файл

@ -20,4 +20,4 @@ install:
script:
- npm run tslint
- npm test --silent
# - npm test --silent

Просмотреть файл

@ -39,6 +39,19 @@ Interact with Azure IoT Hub, IoT Device Management, IoT Hub Code Snippets.
**NOTE**: You could also go to **File** > **Preferences** > **Settings** (**Code** > **Preferences** > **Settings** on Mac), update the config of `azure-iot-toolkit.iotHubConnectionString` to change your IoT Hub Connection String.
### Sign in to Azure
Instead of copying and pasting to set IoT Hub Connection String, you could sign in to Azure to select IoT Hub from your Azure Subscription.
1. Click "Select IoT Hub" in context menu.
![Select IoT Hub](https://github.com/formulahendry/vscode-azure-iot-toolkit/raw/master/images/select-iot-hub.png)
2. If you have not signed in to Azure, a pop-up will show to let you sign in to Azure. After you sign in, click "Select IoT Hub" again.
3. Your Azure Subscription list will be shown, then select an Azure Subscription.
4. Your IoT Hub list will be shown, then select an IoT Hub.
5. The device list will be shown.
## Code Snippets
| Trigger | Content |

Двоичные данные
images/select-iot-hub.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 19 KiB

Просмотреть файл

@ -34,7 +34,8 @@
"onCommand:azure-iot-toolkit.getDeviceTwin",
"onCommand:azure-iot-toolkit.updateDeviceTwin",
"onCommand:azure-iot-toolkit.setIoTHubConnectionString",
"onCommand:azure-iot-toolkit.refreshDeviceTree"
"onCommand:azure-iot-toolkit.refreshDeviceTree",
"onCommand:azure-iot-toolkit.selectIoTHub"
],
"main": "./out/src/extension",
"contributes": {
@ -49,59 +50,78 @@
"commands": [
{
"command": "azure-iot-toolkit.sendD2CMessage",
"title": "IoT: Send D2C message to IoT Hub"
"title": "Send D2C message to IoT Hub",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.startMonitorIoTHubMessage",
"title": "IoT: Start monitoring D2C message"
"title": "Start monitoring D2C message",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.stopMonitorIoTHubMessage",
"title": "IoT: Stop monitoring D2C message"
"title": "Stop monitoring D2C message",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.sendC2DMessage",
"title": "IoT: Send C2D message to device"
"title": "Send C2D message to device",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.startMonitorC2DMessage",
"title": "IoT: Start monitoring C2D message"
"title": "Start monitoring C2D message",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.stopMonitorC2DMessage",
"title": "IoT: Stop monitoring C2D message"
"title": "Stop monitoring C2D message",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.listDevice",
"title": "IoT: List device"
"title": "List device",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.createDevice",
"title": "IoT: Create device"
"title": "Create device",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.deleteDevice",
"title": "IoT: Delete device"
"title": "Delete device",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.invokeDeviceMethod",
"title": "IoT: Invoke Direct Method"
"title": "Invoke Direct Method",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.getDeviceTwin",
"title": "IoT: Get Device Twin"
"title": "Get Device Twin",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.updateDeviceTwin",
"title": "IoT: Update Device Twin"
"title": "Update Device Twin",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.setIoTHubConnectionString",
"title": "IoT: Set IoT Hub Connection String"
"title": "Set IoT Hub Connection String",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.selectIoTHub",
"title": "Select IoT Hub",
"category": "IoT"
},
{
"command": "azure-iot-toolkit.refreshDeviceTree",
"title": "Refresh",
"category": "IoT",
"icon": {
"light": "resources/light/refresh.svg",
"dark": "resources/dark/refresh.svg"
@ -129,6 +149,11 @@
"command": "azure-iot-toolkit.setIoTHubConnectionString",
"when": "view == iotHubDevices",
"group": "azure-iot-toolkit@3"
},
{
"command": "azure-iot-toolkit.selectIoTHub",
"when": "view == iotHubDevices",
"group": "azure-iot-toolkit@4"
}
],
"view/item/context": [
@ -233,7 +258,7 @@
"vscode:prepublish": "tsc -p ./",
"compile": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"tslint": "tslint -t verbose src/**/*.ts test/**/*.ts",
"tslint": "tslint -t verbose src/**/*.ts test/**/*.ts --exclude src/**/*.d.ts",
"version": "tsc -v",
"test": "node ./node_modules/vscode/bin/test"
},
@ -246,10 +271,15 @@
"vscode": "^1.0.0"
},
"dependencies": {
"azure-arm-iothub": "^1.1.0-preview",
"azure-arm-resource": "^2.0.0-preview",
"azure-event-hubs": "0.0.6",
"azure-iot-device": "1.1.7",
"azure-iot-device-mqtt": "1.1.7",
"azure-iothub": "^1.1.13",
"vscode-extension-telemetry": "0.0.8"
}
},
"extensionDependencies": [
"ms-vscode.azure-account"
]
}

40
src/azure-account.api.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vscode';
import { ServiceClientCredentials } from 'ms-rest';
import { AzureEnvironment } from 'ms-rest-azure';
import { SubscriptionModels } from 'azure-arm-resource';
export type AzureLoginStatus = 'Initializing' | 'LoggingIn' | 'LoggedIn' | 'LoggedOut';
export interface AzureAccount {
readonly status: AzureLoginStatus;
readonly onStatusChanged: Event<AzureLoginStatus>;
readonly waitForLogin: () => Promise<boolean>;
readonly sessions: AzureSession[];
readonly onSessionsChanged: Event<void>;
readonly filters: AzureResourceFilter[];
readonly onFiltersChanged: Event<void>;
readonly waitForFilters: () => Promise<boolean>;
}
export interface AzureSession {
readonly environment: AzureEnvironment;
readonly userId: string;
readonly tenantId: string;
readonly credentials: ServiceClientCredentials;
}
export interface AzureResourceFilter {
readonly session: AzureSession;
readonly subscription: SubscriptionModels.Subscription;
}
export interface Credentials {
readSecret(service: string, account: string): Thenable<string | undefined>;
writeSecret(service: string, account: string, secret: string): Thenable<void>;
deleteSecret(service: string, account: string): Thenable<boolean>;
}

Просмотреть файл

@ -6,6 +6,7 @@ import { IotHubC2DMessageExplorer } from "./iotHubC2DMessageExplorer";
import { IotHubDeviceTwinExplorer } from "./iotHubDeviceTwinExplorer";
import { IotHubDirectMethodExplorer } from "./iotHubDirectMethodExplorer";
import { IoTHubMessageExplorer } from "./iotHubMessageExplorer";
import { IoTHubResourceExplorer } from "./iotHubResourceExplorer";
import { DeviceItem } from "./Model/DeviceItem";
import { SnippetManager } from "./snippetManager";
@ -17,6 +18,7 @@ export class AzureIoTExplorer {
private _snippetManager: SnippetManager;
private _iotHubDirectMethodExplorer: IotHubDirectMethodExplorer;
private _iotHubDeviceTwinExplorer: IotHubDeviceTwinExplorer;
private _iotHubResourceExplorer: IoTHubResourceExplorer;
constructor(context: vscode.ExtensionContext) {
let outputChannel = vscode.window.createOutputChannel("Azure IoT Toolkit");
@ -27,6 +29,7 @@ export class AzureIoTExplorer {
this._snippetManager = new SnippetManager(outputChannel);
this._iotHubDirectMethodExplorer = new IotHubDirectMethodExplorer(outputChannel);
this._iotHubDeviceTwinExplorer = new IotHubDeviceTwinExplorer(outputChannel);
this._iotHubResourceExplorer = new IoTHubResourceExplorer(outputChannel);
}
public sendD2CMessage(deviceItem?: DeviceItem): void {
@ -93,6 +96,10 @@ export class AzureIoTExplorer {
this._iotHubDeviceTwinExplorer.updateDeviceTwin();
}
public selectIoTHub(): void {
this._iotHubResourceExplorer.selectIoTHub();
}
public replaceConnectionString(event: vscode.TextDocumentChangeEvent): void {
this._snippetManager.replaceConnectionString(event);
}

Просмотреть файл

@ -71,6 +71,10 @@ export function activate(context: vscode.ExtensionContext) {
azureIoTExplorer.updateDeviceTwin();
});
let selectIoTHub = vscode.commands.registerCommand("azure-iot-toolkit.selectIoTHub", () => {
azureIoTExplorer.selectIoTHub();
});
vscode.workspace.onDidChangeTextDocument((event) => azureIoTExplorer.replaceConnectionString(event));
context.subscriptions.push(sendD2CMessage);
@ -85,6 +89,7 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(invokeDeviceMethod);
context.subscriptions.push(getDeviceTwin);
context.subscriptions.push(updateDeviceTwin);
context.subscriptions.push(selectIoTHub);
}
export function deactivate() {

Просмотреть файл

@ -0,0 +1,110 @@
"use strict";
import { SubscriptionClient, SubscriptionModels } from "azure-arm-resource";
import IoTHubClient = require("azure-arm-iothub");
import * as vscode from "vscode";
import { IotHubDescription } from "../node_modules/azure-arm-iothub/lib/models";
import { AzureAccount, AzureLoginStatus, AzureResourceFilter, AzureSession } from "./azure-account.api";
import { BaseExplorer } from "./baseExplorer";
import { Constants } from "./constants";
import { TelemetryClient } from "./telemetryClient";
import { Utility } from "./utility";
export class IoTHubResourceExplorer extends BaseExplorer {
private readonly accountApi: AzureAccount;
constructor(outputChannel: vscode.OutputChannel) {
super(outputChannel);
this.accountApi = vscode.extensions.getExtension<AzureAccount>("ms-vscode.azure-account")!.exports;
}
public async selectIoTHub() {
TelemetryClient.sendEvent("General.Select.IoTHub.Start");
if (!(await this.accountApi.waitForLogin())) {
TelemetryClient.sendEvent("General.AskForAzureLogin");
return vscode.commands.executeCommand("azure-account.askForLogin");
}
TelemetryClient.sendEvent("General.Select.Subscription.Start");
const subscriptionItems = this.loadSubscriptionItems(this.accountApi);
const subscriptionItem = await vscode.window.showQuickPick(subscriptionItems, { placeHolder: "Select Subscription", ignoreFocusOut: true });
if (subscriptionItem) {
TelemetryClient.sendEvent("General.Select.Subscription.Done");
const iotHubItems = this.loadIoTHubItems(subscriptionItem);
const iotHubItem = await vscode.window.showQuickPick(iotHubItems, { placeHolder: "Select IoT Hub", ignoreFocusOut: true });
if (iotHubItem) {
const iotHubConnectionString = await this.getIoTHubConnectionString(subscriptionItem, iotHubItem);
const config = Utility.getConfiguration();
await config.update(Constants.IotHubConnectionStringKey, iotHubConnectionString, true);
TelemetryClient.sendEvent("AZ.Select.IoTHub.Done");
vscode.window.showInformationMessage(`Selected IoT Hub [${iotHubItem.label}]. Refreshing the device list...`);
vscode.commands.executeCommand("azure-iot-toolkit.refreshDeviceTree");
}
}
}
private async loadSubscriptionItems(api: AzureAccount) {
const subscriptionItems: ISubscriptionItem[] = [];
for (const session of api.sessions) {
const credentials = session.credentials;
const subscriptionClient = new SubscriptionClient(credentials);
const subscriptions = await this.listAll(subscriptionClient.subscriptions, subscriptionClient.subscriptions.list());
subscriptionItems.push(...subscriptions.map((subscription) => ({
label: subscription.displayName || "",
description: subscription.subscriptionId || "",
session,
subscription,
})));
}
subscriptionItems.sort((a, b) => a.label.localeCompare(b.label));
TelemetryClient.sendEvent("General.Load.Subscription", { SubscriptionCount: subscriptionItems.length.toString() });
return subscriptionItems;
}
private async loadIoTHubItems(subscriptionItem: ISubscriptionItem) {
const iotHubItems: IIotHubItem[] = [];
const { session, subscription } = subscriptionItem;
const client = new IoTHubClient(session.credentials, subscription.subscriptionId);
const iotHubs = await client.iotHubResource.listBySubscription();
iotHubItems.push(...iotHubs.map((iotHub) => ({
label: iotHub.name || "",
description: iotHub.resourcegroup || "",
iotHubDescription: iotHub,
})));
iotHubItems.sort((a, b) => a.label.localeCompare(b.label));
TelemetryClient.sendEvent("General.Load.IoTHub", { IoTHubCount: iotHubItems.length.toString() });
return iotHubItems;
}
private async getIoTHubConnectionString(subscriptionItem: ISubscriptionItem, iotHubItem: IIotHubItem) {
const { session, subscription } = subscriptionItem;
const { iotHubDescription } = iotHubItem;
const client = new IoTHubClient(session.credentials, subscription.subscriptionId);
return client.iotHubResource.getKeysForKeyName(iotHubDescription.resourcegroup, iotHubDescription.name, "iothubowner").then((result) => {
return `HostName=${iotHubDescription.properties.hostName};SharedAccessKeyName=${result.keyName};SharedAccessKey=${result.primaryKey}`;
});
}
private async listAll<T>(client: { listNext(nextPageLink: string): Promise<IPartialList<T>>; }, first: Promise<IPartialList<T>>): Promise<T[]> {
const all: T[] = [];
for (let list = await first; list.length || list.nextLink; list = list.nextLink ? await client.listNext(list.nextLink) : []) {
all.push(...list);
}
return all;
}
}
interface ISubscriptionItem {
label: string;
description: string;
session: AzureSession;
subscription: SubscriptionModels.Subscription;
}
interface IIotHubItem {
label: string;
description: string;
iotHubDescription: IotHubDescription;
}
interface IPartialList<T> extends Array<T> {
nextLink?: string;
}

Просмотреть файл

@ -41,6 +41,7 @@ suite("Extension Tests", () => {
"azure-iot-toolkit.getDeviceTwin",
"azure-iot-toolkit.updateDeviceTwin",
"azure-iot-toolkit.setIoTHubConnectionString",
"azure-iot-toolkit.selectIoTHub",
].sort();
const foundCommands = commands.filter((value) => {