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:
Родитель
b3d28c5801
Коммит
ea71f9b042
|
@ -20,4 +20,4 @@ install:
|
|||
|
||||
script:
|
||||
- npm run tslint
|
||||
- npm test --silent
|
||||
# - npm test --silent
|
13
README.md
13
README.md
|
@ -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 |
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 19 KiB |
62
package.json
62
package.json
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
Загрузка…
Ссылка в новой задаче